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-10-25 10:09:38 +02:00
# include "Features.h"
2018-10-22 19:01:30 +02:00
//qCC_db
# include <ccPointCloud.h>
//Qt
# include <QtGui>
# include <QtCore>
# include <QApplication>
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
m_classifyAction - > setEnabled ( selectedEntities . size ( ) = = 1 & & selectedEntities [ 0 ] - > isA ( CC_TYPES : : POINT_CLOUD ) ) ;
}
if ( m_trainAction )
{
m_trainAction - > setEnabled ( m_app & & m_app - > dbRootObject ( ) & & m_app - > dbRootObject ( ) - > getChildrenNumber ( ) ! = 0 ) ; //need some loaded entities to train the classifier!
}
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-10-26 10:41:22 +02:00
//TODO
}
void q3DMASCPlugin : : doTrainAction ( )
{
//disclaimer accepted?
if ( ! ShowTrainDisclaimer ( m_app ) )
return ;
//if (m_selectedEntities.size() != 2
// || !m_selectedEntities[0]->isA(CC_TYPES::POINT_CLOUD)
// || !m_selectedEntities[1]->isA(CC_TYPES::POINT_CLOUD))
//{
// m_app->dispToConsole("Select two point clouds!",ccMainAppInterface::ERR_CONSOLE_MESSAGE);
// return;
//}
//
//ccPointCloud* cloud1 = static_cast<ccPointCloud*>(m_selectedEntities[0]);
//ccPointCloud* cloud2 = static_cast<ccPointCloud*>(m_selectedEntities[1]);
2018-10-22 19:01:30 +02:00
if ( m_selectedEntities . empty ( ) | | ! m_selectedEntities . front ( ) - > isA ( CC_TYPES : : POINT_CLOUD ) )
{
m_app - > dispToConsole ( " Select one and only one point cloud! " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
ccPointCloud * cloud = static_cast < ccPointCloud * > ( m_selectedEntities . front ( ) ) ;
2018-10-26 10:41:22 +02:00
masc : : TrainParameters params ;
2018-10-24 23:14:40 +02:00
if ( params . testDataRatio < 0 | | params . testDataRatio > 0.99f )
{
m_app - > dispToConsole ( " Invalid test data ratio " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
2018-10-26 10:41:22 +02:00
Feature : : Set features ;
2018-10-24 11:30:39 +02:00
{
2018-10-26 10:41:22 +02:00
features . push_back ( Feature : : Shared ( new PointFeature ( cloud , PointFeature : : Z , Feature : : DimZ , " Z " ) ) ) ;
features . push_back ( Feature : : Shared ( new PointFeature ( cloud , PointFeature : : Intensity , Feature : : ScalarField , " Intensity " ) ) ) ;
2018-10-24 11:30:39 +02:00
}
2018-10-22 19:01:30 +02:00
2018-10-26 10:46:35 +02:00
QString outputFilename = QCoreApplication : : applicationDirPath ( ) + " /classifier.yaml " ;
2018-10-26 12:02:23 +02:00
//randomly select the training points
QScopedPointer < CCLib : : ReferenceCloud > trainSubset ( new CCLib : : ReferenceCloud ( cloud ) ) ;
QScopedPointer < CCLib : : ReferenceCloud > testSubset ( new CCLib : : ReferenceCloud ( cloud ) ) ;
if ( ! masc : : Tools : : RandomSubset ( cloud , params . testDataRatio , trainSubset . data ( ) , testSubset . data ( ) ) )
{
m_app - > dispToConsole ( " Not enough memory " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
2018-10-26 10:41:22 +02:00
masc : : Classifier classifier ;
2018-10-26 10:46:35 +02:00
if ( QFile ( outputFilename ) . exists ( ) )
2018-10-25 21:59:00 +02:00
{
2018-10-26 10:46:35 +02:00
if ( ! classifier . fromFile ( outputFilename , m_app - > getMainWindow ( ) ) )
{
m_app - > dispToConsole ( " Failed to load previous classifier file " , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
m_app - > dispToConsole ( " Previous classifier loaded " , ccMainAppInterface : : WRN_CONSOLE_MESSAGE ) ;
}
else
{
QString errorMessage ;
2018-10-26 12:02:23 +02:00
if ( ! classifier . train ( params . rt , features , errorMessage , trainSubset . data ( ) , m_app - > getMainWindow ( ) ) )
2018-10-26 10:46:35 +02:00
{
m_app - > dispToConsole ( errorMessage , ccMainAppInterface : : ERR_CONSOLE_MESSAGE ) ;
return ;
}
//save the classifier
classifier . toFile ( outputFilename , m_app - > getMainWindow ( ) ) ;
m_app - > dispToConsole ( " Classifier succesfully created " , ccMainAppInterface : : WRN_CONSOLE_MESSAGE ) ;
2018-10-25 21:59:00 +02:00
}
2018-10-26 12:02:23 +02: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 ) ;
return ;
}
m_app - > dispToConsole ( QString ( " Correct = %1 / %2 --> accuracy = %3 " ) . arg ( metrics . goodGuess ) . arg ( metrics . sampleCount ) . arg ( metrics . ratio ) , ccMainAppInterface : : STD_CONSOLE_MESSAGE ) ;
2018-10-22 19:01:30 +02:00
}
void q3DMASCPlugin : : registerCommands ( ccCommandLineInterface * cmd )
{
if ( ! cmd )
{
assert ( false ) ;
return ;
}
//cmd->registerCommand(ccCommandLineInterface::Command::Shared(new CommandCanupoClassif));
}