2018-10-22 19:01:30 +02:00
//##########################################################################
//# #
//# CLOUDCOMPARE PLUGIN: q3DMASC #
//# #
//# This program is free software; you can redistribute it and/or modify #
//# it under the terms of the GNU General Public License as published by #
//# the Free Software Foundation; version 2 or later of the License. #
//# #
//# This program is distributed in the hope that it will be useful, #
//# but WITHOUT ANY WARRANTY; without even the implied warranty of #
//# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
//# GNU General Public License for more details. #
//# #
//# COPYRIGHT: Dimitri Lague / CNRS / UEB #
//# #
//##########################################################################
# include "q3DMASC.h"
//local
# include "q3DMASCDisclaimerDialog.h"
2018-10-26 10:41:22 +02:00
# include "q3DMASCClassifier.h"
2018-10-26 12:02:23 +02:00
# include "q3DMASCTools.h"
2018-11-04 23:31:48 +01:00
# include "qClassify3DMASCDialog.h"
2019-01-19 21:40:38 +01:00
# include "qTrain3DMASCDialog.h"
2019-01-19 00:49:29 +01:00
# include "q3DMASCCommands.h"
2018-10-22 19:01:30 +02:00
//qCC_db
# include <ccPointCloud.h>
2018-11-04 00:06:47 +01:00
# include <ccProgressDialog.h>
2018-10-22 19:01:30 +02:00
//Qt
# include <QtGui>
# include <QtCore>
# include <QApplication>
2018-11-04 00:06:47 +01:00
# include <QFileDialog>
2019-01-21 00:10:47 +01:00
# include <QMessageBox>
2018-10-25 10:09:38 +02:00
2018-10-22 19:01:30 +02:00
q3DMASCPlugin : : q3DMASCPlugin ( QObject * parent /*=0*/ )
: QObject ( parent )
, ccStdPluginInterface ( " :/CC/plugin/q3DMASCPlugin/info.json " )
, m_classifyAction ( 0 )
, m_trainAction ( 0 )
{
}
void q3DMASCPlugin : : onNewSelection ( const ccHObject : : Container & selectedEntities )
{
if ( m_classifyAction )
{
//classification: only one point cloud
2018-11-04 23:31:48 +01:00
//m_classifyAction->setEnabled(selectedEntities.size() == 1 && selectedEntities[0]->isA(CC_TYPES::POINT_CLOUD));
m_classifyAction - > setEnabled ( m_app - > dbRootObject ( ) - > getChildrenNumber ( ) ! = 0 ) ;
2018-10-22 19:01:30 +02:00
}
if ( m_trainAction )
{
2018-11-04 00:06:47 +01:00
//m_trainAction->setEnabled(m_app && m_app->dbRootObject() && m_app->dbRootObject()->getChildrenNumber() != 0); //need some loaded entities to train the classifier!
m_trainAction - > setEnabled ( true ) ;
2018-10-22 19:01:30 +02:00
}
m_selectedEntities = selectedEntities ;
}
QList < QAction * > q3DMASCPlugin : : getActions ( )
{
QList < QAction * > group ;
if ( ! m_trainAction )
{
m_trainAction = new QAction ( " Train classifier " , this ) ;
m_trainAction - > setToolTip ( " Train classifier " ) ;
m_trainAction - > setIcon ( QIcon ( QString : : fromUtf8 ( " :/CC/plugin/q3DMASCPlugin/iconCreate.png " ) ) ) ;
connect ( m_trainAction , SIGNAL ( triggered ( ) ) , this , SLOT ( doTrainAction ( ) ) ) ;
}
group . push_back ( m_trainAction ) ;
if ( ! m_classifyAction )
{
m_classifyAction = new QAction ( " Classify " , this ) ;
m_classifyAction - > setToolTip ( " Classify cloud " ) ;
m_classifyAction - > setIcon ( QIcon ( QString : : fromUtf8 ( " :/CC/plugin/q3DMASCPlugin/iconClassify.png " ) ) ) ;
connect ( m_classifyAction , SIGNAL ( triggered ( ) ) , this , SLOT ( doClassifyAction ( ) ) ) ;
}
group . push_back ( m_classifyAction ) ;
return group ;
}
void q3DMASCPlugin : : doClassifyAction ( )
{
if ( ! m_app )
{
assert ( false ) ;
return ;
}
//disclaimer accepted?
if ( ! ShowClassifyDisclaimer ( m_app ) )
{
return ;
}
2018-11-04 23:31:48 +01:00
QString inputFilename ;
{
QSettings settings ;
settings . beginGroup ( " 3DMASC " ) ;
QString inputPath = settings . value ( " FilePath " , QCoreApplication : : applicationDirPath ( ) ) . toString ( ) ;
inputFilename = QFileDialog : : getOpenFileName ( m_app - > getMainWindow ( ) , " Load 3DMASC classifier file " , inputPath , " *.txt " ) ;
if ( inputFilename . isNull ( ) )
{
//process cancelled by the user
return ;
}
settings . setValue ( " FilePath " , QFileInfo ( inputFilename ) . absolutePath ( ) ) ;
settings . endGroup ( ) ;
}
2018-10-26 10:41:22 +02:00
2018-11-04 23:31:48 +01:00
QSet < QString > cloudLabels ;
if ( ! masc : : Tools : : LoadClassifierCloudLabels ( inputFilename , cloudLabels ) )
{
m_app - > dispToConsole ( " Failed to read classifier file (see Console) " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2018-10-26 10:41:22 +02:00
return ;
2018-11-04 23:31:48 +01:00
}
if ( cloudLabels . empty ( ) )
{
m_app - > dispToConsole ( " Invalid classifier file (no cloud label defined) " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
else if ( cloudLabels . size ( ) > 3 )
{
m_app - > dispToConsole ( " This classifier uses more than 3 clouds (the GUI version cannot handle it) " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
2018-10-26 10:41:22 +02:00
2018-11-04 23:31:48 +01:00
//now show a dialog where the user will be able to set the cloud roles
Classify3DMASCDialog classifDlg ( m_app ) ;
classifDlg . setCloudRoles ( cloudLabels ) ;
2018-11-05 00:11:27 +01:00
classifDlg . classifierFileLineEdit - > setText ( inputFilename ) ;
2019-01-19 00:49:29 +01:00
static bool s_keepAttributes = false ;
classifDlg . keepAttributesCheckBox - > setChecked ( s_keepAttributes ) ;
2018-11-04 23:31:48 +01:00
if ( ! classifDlg . exec ( ) )
2018-11-04 00:06:47 +01:00
{
2018-11-04 23:31:48 +01:00
//process cancelled by the user
2018-11-04 00:06:47 +01:00
return ;
}
2019-01-19 00:49:29 +01:00
s_keepAttributes = classifDlg . keepAttributesCheckBox - > isChecked ( ) ;
2018-11-04 23:31:48 +01:00
masc : : Tools : : NamedClouds clouds ;
QString mainCloudLabel ;
classifDlg . getClouds ( clouds , mainCloudLabel ) ;
2018-11-04 11:21:50 +01:00
masc : : Feature : : Set features ;
2018-11-04 23:31:48 +01:00
masc : : Classifier classifier ;
if ( ! masc : : Tools : : LoadClassifier ( inputFilename , clouds , features , classifier , m_app - > getMainWindow ( ) ) )
2018-10-22 19:01:30 +02:00
{
return ;
}
2018-11-04 23:31:48 +01:00
//the 'main cloud' is the cloud that should be classified
masc : : CorePoints corePoints ;
corePoints . origin = corePoints . cloud = clouds [ mainCloudLabel ] ;
2018-10-22 19:01:30 +02:00
2018-11-04 23:31:48 +01:00
//prepare the main cloud
2019-01-20 22:12:20 +01:00
ccProgressDialog progressDlg ( true , m_app - > getMainWindow ( ) ) ;
progressDlg . setAutoClose ( false ) ; //we don't want the progress dialog to 'pop' for each feature
2018-11-04 23:31:48 +01:00
QString error ;
2019-01-19 00:49:29 +01:00
SFCollector generatedScalarFields ;
2019-01-20 22:12:20 +01:00
if ( ! masc : : Tools : : PrepareFeatures ( corePoints , features , error , & progressDlg , & generatedScalarFields ) )
2018-11-04 23:31:48 +01:00
{
m_app - > dispToConsole ( error , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2019-01-20 23:02:57 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2018-11-04 23:31:48 +01:00
return ;
}
2019-01-20 22:12:20 +01:00
progressDlg . close ( ) ;
2018-11-04 23:31:48 +01:00
QCoreApplication : : processEvents ( ) ;
2019-01-20 22:12:20 +01:00
progressDlg . setAutoClose ( true ) ; //restore the default behavior of the progress dialog
2018-11-04 23:31:48 +01:00
//apply classifier
{
QString errorMessage ;
if ( ! classifier . classify ( features , corePoints . cloud , errorMessage , m_app - > getMainWindow ( ) ) )
{
m_app - > dispToConsole ( errorMessage , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2019-01-20 23:02:57 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2018-11-04 23:31:48 +01:00
return ;
}
2019-01-19 00:49:29 +01:00
if ( ! s_keepAttributes )
{
2019-01-20 23:02:57 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2019-01-19 00:49:29 +01:00
}
2018-11-04 23:31:48 +01:00
}
}
2019-01-20 22:12:20 +01:00
struct FeatureSelection
{
FeatureSelection ( masc : : Feature : : Shared f = masc : : Feature : : Shared ( nullptr ) ) : feature ( f ) { }
masc : : Feature : : Shared feature ;
bool selected = true ;
bool prepared = false ;
float importance = std : : numeric_limits < float > : : quiet_NaN ( ) ;
} ;
2018-11-04 23:31:48 +01:00
void q3DMASCPlugin : : doTrainAction ( )
{
//disclaimer accepted?
if ( ! ShowTrainDisclaimer ( m_app ) )
return ;
2018-11-04 00:06:47 +01:00
QString inputFilename ;
{
QSettings settings ;
settings . beginGroup ( " 3DMASC " ) ;
QString inputPath = settings . value ( " FilePath " , QCoreApplication : : applicationDirPath ( ) ) . toString ( ) ;
2018-11-04 23:31:48 +01:00
inputFilename = QFileDialog : : getOpenFileName ( m_app - > getMainWindow ( ) , " Load 3DMASC training file " , inputPath , " *.txt " ) ;
2018-11-04 00:06:47 +01:00
if ( inputFilename . isNull ( ) )
{
//process cancelled by the user
return ;
}
settings . setValue ( " FilePath " , QFileInfo ( inputFilename ) . absolutePath ( ) ) ;
settings . endGroup ( ) ;
}
2019-01-21 00:10:47 +01:00
//load the cloud labels (PC1, PC2, CTX, etc.)
QSet < QString > cloudLabels ;
if ( ! masc : : Tools : : LoadClassifierCloudLabels ( inputFilename , cloudLabels ) )
{
m_app - > dispToConsole ( " Failed to read classifier file (see Console) " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
if ( cloudLabels . empty ( ) )
{
m_app - > dispToConsole ( " Invalid classifier file (no cloud label defined) " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
static bool s_keepAttributes = false ;
masc : : Tools : : NamedClouds loadedClouds ;
bool useCloudsFromDB = ( QMessageBox : : question ( m_app - > getMainWindow ( ) , " Use clouds in DB " , " Use clouds in db (yes) or clouds specified in the file(no)? " , QMessageBox : : Yes , QMessageBox : : No ) = = QMessageBox : : Yes ) ;
if ( useCloudsFromDB )
{
if ( cloudLabels . size ( ) > 3 )
{
m_app - > dispToConsole ( " This classifier uses more than 3 different clouds (the GUI version cannot handle it) " , ccMainAppInterface : : WRN_CONSOLE_MESSAGE ) ;
return ;
}
//now show a dialog where the user will be able to set the cloud roles
Classify3DMASCDialog classifDlg ( m_app , true ) ;
classifDlg . setWindowTitle ( " 3DMASC Train " ) ;
classifDlg . setCloudRoles ( cloudLabels ) ;
classifDlg . classifierFileLineEdit - > setText ( inputFilename ) ;
classifDlg . keepAttributesCheckBox - > setChecked ( s_keepAttributes ) ;
if ( ! classifDlg . exec ( ) )
{
//process cancelled by the user
return ;
}
s_keepAttributes = classifDlg . keepAttributesCheckBox - > isChecked ( ) ;
2019-01-19 21:40:38 +01:00
2019-01-21 00:10:47 +01:00
QString mainCloudLabel ;
classifDlg . getClouds ( loadedClouds , mainCloudLabel ) ;
}
static masc : : TrainParameters s_params ;
2018-11-04 00:06:47 +01:00
masc : : CorePoints corePoints ;
2018-11-04 23:31:48 +01:00
masc : : Feature : : Set features ;
2019-01-19 21:40:38 +01:00
if ( ! masc : : Tools : : LoadTrainingFile ( inputFilename , features , loadedClouds , corePoints , s_params ) )
2018-11-04 00:06:47 +01:00
{
2019-01-21 00:10:47 +01:00
m_app - > dispToConsole ( " Failed to load the training file " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2018-10-24 23:14:40 +02:00
return ;
}
2018-11-04 00:06:47 +01:00
ccHObject * group = new ccHObject ( " 3DMASC " ) ;
2019-01-21 00:10:47 +01:00
if ( ! useCloudsFromDB )
2018-11-04 00:06:47 +01:00
{
2019-01-21 00:10:47 +01:00
//add the loaded clouds to the main DB (so that we don't need to handle them anymore)
for ( masc : : Tools : : NamedClouds : : const_iterator it = loadedClouds . begin ( ) ; it ! = loadedClouds . end ( ) ; + + it )
{
group - > addChild ( it . value ( ) ) ;
}
2018-11-04 00:06:47 +01:00
}
2019-01-20 22:12:20 +01:00
//show the training dialog for the first time
Train3DMASCDialog trainDlg ( m_app - > getMainWindow ( ) ) ;
trainDlg . maxDepthSpinBox - > setValue ( s_params . rt . maxDepth ) ;
trainDlg . maxTreeCountSpinBox - > setValue ( s_params . rt . maxTreeCount ) ;
trainDlg . activeVarCountSpinBox - > setValue ( s_params . rt . activeVarCount ) ;
trainDlg . minSampleCountSpinBox - > setValue ( s_params . rt . minSampleCount ) ;
trainDlg . testDataRatioSpinBox - > setValue ( static_cast < int > ( s_params . testDataRatio * 100 ) ) ;
//display the loaded features and let the user select the ones to use
trainDlg . setResultText ( " Select features and press 'Run' " ) ;
std : : vector < FeatureSelection > originalFeatures ;
originalFeatures . reserve ( features . size ( ) ) ;
for ( const masc : : Feature : : Shared & f : features )
{
originalFeatures . push_back ( FeatureSelection ( f ) ) ;
trainDlg . addFeature ( f - > toString ( ) , originalFeatures . back ( ) . importance , originalFeatures . back ( ) . selected ) ;
}
if ( ! trainDlg . exec ( ) )
{
2019-01-21 00:10:47 +01:00
delete group ;
2019-01-20 22:12:20 +01:00
return ;
}
assert ( ! trainDlg . shouldSaveClassifier ( ) ) ; //the save button should be disabled at this point
//compute the core points (if necessary)
ccProgressDialog progressDlg ( true , m_app - > getMainWindow ( ) ) ;
if ( ! corePoints . prepare ( & progressDlg ) )
2018-11-04 00:06:47 +01:00
{
m_app - > dispToConsole ( " Failed to compute/prepare the core points! " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
delete group ;
return ;
}
if ( corePoints . cloud ! = corePoints . origin )
{
//auto-hide the other clouds
for ( ccPointCloud * pc : loadedClouds )
{
pc - > setEnabled ( false ) ;
}
//set an explicit name for the core points
QString corePointsName = corePoints . origin - > getName ( ) ;
switch ( corePoints . selectionMethod )
{
case masc : : CorePoints : : NONE :
break ;
case masc : : CorePoints : : RANDOM :
corePointsName + = " _SS_Random@ " + QString : : number ( corePoints . selectionParam ) ;
break ;
case masc : : CorePoints : : SPATIAL :
corePointsName + = " _SS_Spatial@ " + QString : : number ( corePoints . selectionParam ) ;
break ;
default :
assert ( false ) ;
}
corePoints . cloud - > setName ( QString ( " Core points (%1) " ) . arg ( corePointsName ) ) ;
group - > addChild ( corePoints . cloud ) ;
}
2019-01-21 00:10:47 +01:00
if ( group - > getChildrenNumber ( ) ! = 0 )
{
m_app - > addToDB ( group ) ;
QCoreApplication : : processEvents ( ) ;
}
else
{
delete group ;
group = nullptr ;
}
2018-11-04 00:06:47 +01:00
2019-01-20 22:12:20 +01:00
//train / test subsets
QScopedPointer < CCLib : : ReferenceCloud > trainSubset ( new CCLib : : ReferenceCloud ( corePoints . cloud ) ) ;
QScopedPointer < CCLib : : ReferenceCloud > testSubset ( new CCLib : : ReferenceCloud ( corePoints . cloud ) ) ;
float previousTrainSubsetRatio = - 1.0f ;
2019-01-21 00:10:47 +01:00
SFCollector generatedScalarFields ;
2019-01-20 22:12:20 +01:00
for ( int iteration = 0 ; ; + + iteration )
2018-10-24 11:30:39 +02:00
{
2019-01-20 22:12:20 +01:00
//look for selected features
features . clear ( ) ;
masc : : Feature : : Set toPrepare ;
for ( size_t i = 0 ; i < originalFeatures . size ( ) ; + + i )
{
originalFeatures [ i ] . selected = trainDlg . isFeatureSelected ( i ) ;
2018-10-22 19:01:30 +02:00
2019-01-20 22:12:20 +01:00
//if the feature is selected
if ( originalFeatures [ i ] . selected )
{
if ( ! originalFeatures [ i ] . prepared )
{
//we should prepare it first!
toPrepare . push_back ( originalFeatures [ i ] . feature ) ;
}
features . push_back ( originalFeatures [ i ] . feature ) ;
}
}
2018-12-01 14:39:11 +01:00
2019-01-20 22:12:20 +01:00
if ( features . empty ( ) )
2019-01-19 21:40:38 +01:00
{
2019-01-20 22:12:20 +01:00
m_app - > dispToConsole ( " No feature selected! " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
continue ;
}
//prepare the features
if ( ! toPrepare . empty ( ) )
{
progressDlg . setAutoClose ( false ) ; //we don't want the progress dialog to 'pop' for each feature
QString error ;
2019-01-21 00:10:47 +01:00
if ( ! masc : : Tools : : PrepareFeatures ( corePoints , toPrepare , error , & progressDlg , & generatedScalarFields ) )
2019-01-20 22:12:20 +01:00
{
m_app - > dispToConsole ( error , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2019-01-21 00:10:47 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2019-01-20 22:12:20 +01:00
return ;
}
progressDlg . setAutoClose ( true ) ; //restore the default behavior of the progress dialog
progressDlg . close ( ) ;
QCoreApplication : : processEvents ( ) ;
m_app - > redrawAll ( ) ;
//flag the prepared features as 'prepared' ;)
for ( FeatureSelection & fs : originalFeatures )
{
if ( fs . selected & & ! fs . prepared )
fs . prepared = true ;
}
2019-01-19 21:40:38 +01:00
}
2018-10-26 12:02:23 +02:00
2019-01-20 22:12:20 +01:00
masc : : Classifier classifier ;
//retrieve parameters
2019-01-19 21:40:38 +01:00
s_params . rt . maxDepth = trainDlg . maxDepthSpinBox - > value ( ) ;
s_params . rt . maxTreeCount = trainDlg . maxTreeCountSpinBox - > value ( ) ;
s_params . rt . activeVarCount = trainDlg . activeVarCountSpinBox - > value ( ) ;
s_params . rt . minSampleCount = trainDlg . minSampleCountSpinBox - > value ( ) ;
s_params . testDataRatio = trainDlg . testDataRatioSpinBox - > value ( ) / 100.0f ;
if ( s_params . testDataRatio < 0 | | s_params . testDataRatio > 0.99f )
2018-10-26 10:46:35 +02:00
{
2019-01-19 21:40:38 +01:00
assert ( false ) ;
m_app - > dispToConsole ( " Invalid test data ratio " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
}
2019-01-20 22:12:20 +01:00
else
2019-01-19 21:40:38 +01:00
{
2019-01-20 22:12:20 +01:00
if ( previousTrainSubsetRatio ! = s_params . testDataRatio )
2018-11-04 00:06:47 +01:00
{
2019-01-20 22:12:20 +01:00
//randomly select the training points
testSubset - > clear ( ) ;
trainSubset - > clear ( ) ;
if ( ! masc : : Tools : : RandomSubset ( corePoints . cloud , s_params . testDataRatio , testSubset . data ( ) , trainSubset . data ( ) ) )
{
m_app - > dispToConsole ( " Not enough memory " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2019-01-21 00:10:47 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2019-01-20 22:12:20 +01:00
return ;
}
previousTrainSubsetRatio = s_params . testDataRatio ;
2018-11-04 00:06:47 +01:00
}
2019-01-20 22:12:20 +01:00
//train the classifier
2019-01-19 21:40:38 +01:00
{
2019-01-20 22:12:20 +01:00
QString errorMessage ;
if ( ! classifier . train ( corePoints . cloud , s_params . rt , features , errorMessage , trainSubset . data ( ) , m_app , m_app - > getMainWindow ( ) ) )
2019-01-19 21:40:38 +01:00
{
2019-01-20 22:12:20 +01:00
m_app - > dispToConsole ( errorMessage , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2019-01-21 00:10:47 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2019-01-19 21:40:38 +01:00
return ;
}
2019-01-20 22:12:20 +01:00
trainDlg . setFirstRunDone ( ) ;
trainDlg . shouldSaveClassifier ( ) ;
2019-01-19 21:40:38 +01:00
}
2019-01-20 22:12:20 +01:00
//test the trained classifier
2019-01-19 21:40:38 +01:00
{
2019-01-20 22:12:20 +01:00
masc : : Classifier : : AccuracyMetrics metrics ;
QString errorMessage ;
if ( ! classifier . evaluate ( features , testSubset . data ( ) , metrics , errorMessage , m_app - > getMainWindow ( ) ) )
{
m_app - > dispToConsole ( errorMessage , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
2019-01-21 00:10:47 +01:00
generatedScalarFields . releaseAllSFs ( ) ;
2019-01-20 22:12:20 +01:00
return ;
}
QString resultText = QString ( " Correct guess = %1 / %2 --> accuracy = %3 " ) . arg ( metrics . goodGuess ) . arg ( metrics . sampleCount ) . arg ( metrics . ratio ) ;
m_app - > dispToConsole ( resultText , ccMainAppInterface : : STD_CONSOLE_MESSAGE ) ;
trainDlg . setResultText ( resultText ) ;
cv : : Mat importanceMat = classifier . getVarImportance ( ) ;
//m_app->dispToConsole(QString("Var importance size = %1 x %2").arg(importanceMat.rows).arg(importanceMat.cols));
assert ( static_cast < int > ( features . size ( ) ) = = importanceMat . rows ) ;
int selectedFeatureIndex = 0 ;
for ( size_t i = 0 ; i < originalFeatures . size ( ) ; + + i )
{
if ( originalFeatures [ i ] . selected )
{
//m_app->dispToConsole(QString("Feature #%1 importance = %2").arg(i + 1).arg(importanceMat.at<float>(i, 0)));
assert ( selectedFeatureIndex < importanceMat . rows ) ;
originalFeatures [ i ] . importance = importanceMat . at < float > ( selectedFeatureIndex , 0 ) ;
+ + selectedFeatureIndex ;
}
else
{
originalFeatures [ i ] . importance = std : : numeric_limits < float > : : quiet_NaN ( ) ;
}
trainDlg . setFeatureImportance ( i , originalFeatures [ i ] . importance ) ;
}
2019-01-19 21:40:38 +01:00
}
2018-11-04 23:31:48 +01:00
}
2018-10-26 12:02:23 +02:00
2019-01-20 22:12:20 +01:00
while ( true )
2018-11-04 00:06:47 +01:00
{
2019-01-20 22:12:20 +01:00
if ( ! trainDlg . exec ( ) )
2019-01-19 21:40:38 +01:00
{
2019-01-20 22:12:20 +01:00
//the dialog can be closed
2019-01-21 00:10:47 +01:00
if ( ! s_keepAttributes )
{
generatedScalarFields . releaseAllSFs ( ) ;
}
2019-01-19 21:40:38 +01:00
return ;
}
2019-01-20 22:12:20 +01:00
//if the save button has been clicked
if ( trainDlg . shouldSaveClassifier ( ) )
{
//ask for the output filename
QString outputFilename ;
{
QSettings settings ;
settings . beginGroup ( " 3DMASC " ) ;
QString outputPath = settings . value ( " FilePath " , QCoreApplication : : applicationDirPath ( ) ) . toString ( ) ;
outputFilename = QFileDialog : : getSaveFileName ( m_app - > getMainWindow ( ) , " Save 3DMASC classifier " , outputPath , " *.txt " ) ;
if ( outputFilename . isNull ( ) )
{
//process cancelled by the user
continue ;
}
settings . setValue ( " FilePath " , QFileInfo ( outputFilename ) . absolutePath ( ) ) ;
settings . endGroup ( ) ;
}
//save the classifier
if ( masc : : Tools : : SaveClassifier ( outputFilename , features , classifier , m_app - > getMainWindow ( ) ) )
{
m_app - > dispToConsole ( " Classifier succesfully saved to " + outputFilename , ccMainAppInterface : : STD_CONSOLE_MESSAGE ) ;
trainDlg . setClassifierSaved ( ) ;
}
else
{
m_app - > dispToConsole ( " Failed to save classifier file " ) ;
}
}
else //we will run the classifier another time
{
//stop the local loop
break ;
}
2018-11-04 00:06:47 +01:00
}
2018-10-26 12:02:23 +02:00
2019-01-20 22:12:20 +01:00
//we are going to restart the classification process
2018-11-04 00:06:47 +01:00
}
2018-10-22 19:01:30 +02:00
}
void q3DMASCPlugin : : registerCommands ( ccCommandLineInterface * cmd )
{
if ( ! cmd )
{
assert ( false ) ;
return ;
}
2019-01-19 00:49:29 +01:00
cmd - > registerCommand ( ccCommandLineInterface : : Command : : Shared ( new Command3DMASCClassif ) ) ;
2018-10-22 19:01:30 +02:00
}