2019-01-19 21:40:38 +01:00
//##########################################################################
//# #
//# CLOUDCOMPARE PLUGIN: qCANUPO #
//# #
//# 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: UNIVERSITE EUROPEENNE DE BRETAGNE #
//# #
//##########################################################################
# include "qTrain3DMASCDialog.h"
2019-01-20 22:12:20 +01:00
//Qt
# include <QTableWidgetItem>
# include <QMessageBox>
2019-03-29 10:34:17 +01:00
# include <QFileDialog>
# include <QSettings>
# include <QTextStream>
2023-03-22 23:45:19 +01:00
# include <QStandardPaths>
# include <QDateTime>
# include <ccLog.h>
2019-01-20 22:12:20 +01:00
//System
# include <assert.h>
2023-01-18 23:57:59 +01:00
# include <iostream>
2019-03-25 22:45:39 +01:00
static const int FeatureImportanceColumn = 1 ;
2019-01-19 21:40:38 +01:00
Train3DMASCDialog : : Train3DMASCDialog ( QWidget * parent /*=nullptr*/ )
: QDialog ( parent )
, Ui : : Train3DMASCDialog ( )
2019-01-20 22:12:20 +01:00
, classifierSaved ( false )
, saveRequested ( false )
2023-03-22 23:45:19 +01:00
, traceFileConfigured ( false )
, m_traceFile ( nullptr )
2023-03-27 22:52:23 +02:00
, run ( 0 )
2019-01-19 21:40:38 +01:00
{
setupUi ( this ) ;
2019-01-20 22:12:20 +01:00
2023-03-22 23:45:19 +01:00
QDateTime dateTime = QDateTime : : currentDateTime ( ) ;
2023-03-28 17:19:41 +02:00
m_baseName = " 3dmasc_ " + dateTime . toString ( " yyyyMMdd_hh " )
+ " h " + dateTime . toString ( " mm " ) ;
2023-03-22 23:45:19 +01:00
readSettings ( ) ;
2019-01-20 22:12:20 +01:00
connect ( closePushButton , SIGNAL ( clicked ( ) ) , this , SLOT ( onClose ( ) ) ) ;
connect ( savePushButton , SIGNAL ( clicked ( ) ) , this , SLOT ( onSave ( ) ) ) ;
2019-03-29 10:34:17 +01:00
connect ( exportToolButton , SIGNAL ( clicked ( ) ) , this , SLOT ( onExportResults ( ) ) ) ;
2019-01-20 22:12:20 +01:00
}
2023-03-22 23:45:19 +01:00
Train3DMASCDialog : : ~ Train3DMASCDialog ( )
{
writeSettings ( ) ;
closeTraceFile ( ) ;
2023-03-27 22:52:23 +02:00
for ( auto m : toDeleteLater )
{
if ( m ! = nullptr )
delete m ;
}
2023-03-22 23:45:19 +01:00
}
void Train3DMASCDialog : : readSettings ( )
{
QSettings settings ;
settings . beginGroup ( " 3DMASC " ) ;
2023-03-27 22:52:23 +02:00
bool keepAttributes = settings . value ( " keepAttributes " , false ) . toBool ( ) ;
this - > keepAttributesCheckBox - > setChecked ( keepAttributes ) ;
2023-03-22 23:45:19 +01:00
bool saveTrace = settings . value ( " saveTrace " , false ) . toBool ( ) ;
setCheckBoxSaveTrace ( saveTrace ) ;
}
void Train3DMASCDialog : : writeSettings ( )
{
QSettings settings ;
settings . beginGroup ( " 3DMASC " ) ;
2023-03-27 22:52:23 +02:00
settings . setValue ( " keepAttributes " , keepAttributesCheckBox - > isChecked ( ) ) ;
2023-03-22 23:45:19 +01:00
settings . setValue ( " saveTrace " , checkBox_keepTraces - > isChecked ( ) ) ;
}
2019-01-20 22:12:20 +01:00
void Train3DMASCDialog : : clearResults ( )
{
resultLabel - > clear ( ) ;
tableWidget - > clear ( ) ;
}
int Train3DMASCDialog : : addFeature ( QString name , float importance , bool isChecked /*=true*/ )
{
int index = tableWidget - > rowCount ( ) ;
tableWidget - > setRowCount ( index + 1 ) ;
QTableWidgetItem * nameItem = new QTableWidgetItem ( name ) ;
nameItem - > setCheckState ( isChecked ? Qt : : Checked : Qt : : Unchecked ) ;
tableWidget - > setItem ( index , 0 , nameItem ) ;
2022-10-25 16:47:19 +02:00
QTableWidgetItem * importanceItem = new QTableWidgetItem ( isnan ( importance ) ? QString ( ) : QString : : number ( importance ) ) ;
2019-01-20 22:12:20 +01:00
tableWidget - > setItem ( index , 1 , importanceItem ) ;
return index ;
}
2022-10-17 17:33:15 +02:00
int Train3DMASCDialog : : addScale ( double scale , bool isChecked /*=true*/ )
{
2022-10-27 18:55:04 +02:00
int index = tableWidgetScales - > rowCount ( ) ;
tableWidgetScales - > setRowCount ( index + 1 ) ;
2022-10-17 17:33:15 +02:00
QTableWidgetItem * nameItem = new QTableWidgetItem ( QString : : number ( scale ) ) ;
nameItem - > setCheckState ( isChecked ? Qt : : Checked : Qt : : Unchecked ) ;
2022-10-27 18:55:04 +02:00
tableWidgetScales - > setItem ( index , 0 , nameItem ) ;
2022-10-17 17:33:15 +02:00
return index ;
}
2022-10-28 17:05:36 +02:00
void Train3DMASCDialog : : scaleStateChanged ( QTableWidgetItem * item )
{
// if the scale is not checked, remove automatically features based on this scale
QString scale = " _SC " + item - > text ( ) + " _ " ;
for ( int row = 0 ; row < tableWidget - > rowCount ( ) ; row + + )
{
QTableWidgetItem * nameItem = tableWidget - > item ( row , 0 ) ;
QString name = nameItem - > text ( ) ;
if ( name . contains ( scale ) )
nameItem - > setCheckState ( item - > checkState ( ) ) ;
}
}
void Train3DMASCDialog : : connectScaleSelectionToFeatureSelection ( )
{
connect ( tableWidgetScales , & QTableWidget : : itemChanged , this , & Train3DMASCDialog : : scaleStateChanged ) ;
}
2019-01-20 22:12:20 +01:00
void Train3DMASCDialog : : setResultText ( QString text )
{
resultLabel - > setText ( text ) ;
}
void Train3DMASCDialog : : setFirstRunDone ( )
{
runPushButton - > setText ( tr ( " Retry " ) ) ;
savePushButton - > setEnabled ( true ) ;
}
2019-03-27 15:03:58 +01:00
bool Train3DMASCDialog : : isFeatureSelected ( QString featureName ) const
2019-01-20 22:12:20 +01:00
{
2019-03-27 15:03:58 +01:00
for ( int index = 0 ; index < tableWidget - > rowCount ( ) ; + + index )
2019-01-20 22:12:20 +01:00
{
2019-03-27 15:03:58 +01:00
QTableWidgetItem * item = tableWidget - > item ( index , 0 ) ;
if ( item - > text ( ) = = featureName )
{
return ( item - > checkState ( ) = = Qt : : Checked ) ;
}
2019-01-20 22:12:20 +01:00
}
2019-03-27 15:03:58 +01:00
assert ( false ) ;
return false ;
2019-01-20 22:12:20 +01:00
}
2019-03-25 22:45:39 +01:00
void Train3DMASCDialog : : sortByFeatureImportance ( )
{
2019-03-28 21:29:03 +01:00
tableWidget - > sortByColumn ( FeatureImportanceColumn , Qt : : DescendingOrder ) ;
2019-03-25 22:45:39 +01:00
}
2019-03-27 15:03:58 +01:00
void Train3DMASCDialog : : setFeatureImportance ( QString featureName , float importance )
2019-01-20 22:12:20 +01:00
{
2019-03-27 15:03:58 +01:00
for ( int index = 0 ; index < tableWidget - > rowCount ( ) ; + + index )
2019-01-20 22:12:20 +01:00
{
2019-03-27 15:03:58 +01:00
if ( tableWidget - > item ( index , 0 ) - > text ( ) = = featureName )
{
2019-03-29 10:34:17 +01:00
QTableWidgetItem * item = tableWidget - > item ( index , FeatureImportanceColumn ) ;
2022-10-25 16:47:19 +02:00
item - > setText ( isnan ( importance ) ? QString ( ) : QString : : number ( importance , ' f ' , 6 ) ) ;
2019-03-27 15:03:58 +01:00
return ;
}
2019-01-20 22:12:20 +01:00
}
2019-03-27 15:03:58 +01:00
assert ( false ) ;
2019-01-20 22:12:20 +01:00
}
void Train3DMASCDialog : : onClose ( )
{
if ( ! classifierSaved & & QMessageBox : : question ( this , " Classifier not saved " , " Classifier not saved. Do you confirm you want to close the tool? " , QMessageBox : : Yes , QMessageBox : : No ) = = QMessageBox : : No )
return ;
reject ( ) ;
}
void Train3DMASCDialog : : onSave ( )
{
saveRequested = true ;
accept ( ) ;
2019-01-19 21:40:38 +01:00
}
2019-03-29 10:34:17 +01:00
2023-03-28 17:19:41 +02:00
void Train3DMASCDialog : : onExportResults ( QString filePath /*=""*/ )
2019-03-29 10:34:17 +01:00
{
QSettings settings ;
settings . beginGroup ( " 3DMASC " ) ;
QString outputPath = settings . value ( " FilePath " , QCoreApplication : : applicationDirPath ( ) ) . toString ( ) ;
QString outputFilename = QFileDialog : : getSaveFileName ( this , " Export feature importance matrix " , outputPath , " *.csv " ) ;
if ( outputFilename . isNull ( ) )
{
//process cancelled by the user
return ;
}
settings . setValue ( " FilePath " , QFileInfo ( outputFilename ) . absolutePath ( ) ) ;
settings . endGroup ( ) ;
//save the file
QFile file ( outputFilename ) ;
if ( ! file . open ( QFile : : WriteOnly | QFile : : Text ) )
{
QMessageBox : : critical ( this , " Error " , " Failed to open file for writing: " + outputFilename ) ;
return ;
}
QTextStream stream ( & file ) ;
2022-10-20 08:52:55 +02:00
stream < < " Feature;Importance " < < Qt : : endl ;
2019-03-29 10:34:17 +01:00
for ( int index = 0 ; index < tableWidget - > rowCount ( ) ; + + index )
{
QString featureName = tableWidget - > item ( index , 0 ) - > text ( ) ;
QString importance = tableWidget - > item ( index , FeatureImportanceColumn ) - > text ( ) ;
2022-10-20 08:52:55 +02:00
stream < < featureName < < " ; " < < importance < < Qt : : endl ;
2019-03-29 10:34:17 +01:00
}
2022-10-17 17:33:15 +02:00
}
2023-01-18 23:57:59 +01:00
2023-03-27 22:52:23 +02:00
void Train3DMASCDialog : : addConfusionMatrixAndSaveTraces ( ConfusionMatrix * confusionMatrix )
2023-01-18 23:57:59 +01:00
{
2023-03-27 22:52:23 +02:00
toDeleteLater . push_back ( confusionMatrix ) ;
saveTraces ( confusionMatrix ) ;
2023-01-18 23:57:59 +01:00
}
2023-03-22 23:45:19 +01:00
void Train3DMASCDialog : : setInputFilePath ( QString filePath )
{
m_parameterFilePath = filePath ;
}
void Train3DMASCDialog : : setCheckBoxSaveTrace ( bool state )
{
checkBox_keepTraces - > setChecked ( state ) ;
}
bool Train3DMASCDialog : : openTraceFile ( )
{
QString traceFileName ;
QString traceFilePath ;
// get currentPath
QFileInfo info ( m_parameterFilePath ) ;
QDir parameterDir = QDir ( info . path ( ) ) ;
QFileDialog dialog ( this ) ;
dialog . setFileMode ( QFileDialog : : DirectoryOnly ) ;
dialog . setWindowTitle ( " Choose a valid directory for the traces " ) ;
dialog . setDirectory ( QStandardPaths : : standardLocations ( QStandardPaths : : DocumentsLocation ) . at ( 0 ) ) ;
if ( ! m_traceFile ) // create trace file if it does not exists already
{
m_tracePath = parameterDir . absolutePath ( ) + " / " + m_baseName ;
traceFileName = m_baseName + " .txt " ;
2023-03-28 17:19:41 +02:00
if ( parameterDir . exists ( m_baseName ) )
{
QDateTime dateTime = QDateTime : : currentDateTime ( ) ;
m_baseName + = " min " + dateTime . toString ( " ss " ) + " s " ;
}
2023-03-22 23:45:19 +01:00
if ( ! parameterDir . mkdir ( m_baseName ) ) // create a specific directory to store the traces
{
ccLog : : Error ( " impossible to save in the default directory: " + m_tracePath ) ;
// impossible to save in the default directory, you have to propose another path
if ( dialog . exec ( ) )
m_tracePath = dialog . selectedFiles ( ) . at ( 0 ) ;
else
return false ;
}
else
ccLog : : Print ( " directory for traces created: " + m_tracePath ) ;
traceFilePath = m_tracePath + " / " + traceFileName ;
m_traceFile = new QFile ( traceFilePath ) ;
if ( ! m_traceFile - > open ( QIODevice : : WriteOnly | QIODevice : : Text ) )
{
ccLog : : Error ( " impossible to open trace file: " + traceFilePath ) ;
delete m_traceFile ;
m_tracePath . clear ( ) ;
return false ;
}
}
if ( m_traceFile & & m_traceFile - > isOpen ( ) )
{
traceFileConfigured = true ;
ccLog : : Print ( " save trace in: " + traceFilePath ) ;
m_traceStream . setDevice ( m_traceFile ) ;
m_traceStream < < " run overallAccuracy \n " ;
return true ;
}
else
return false ;
}
bool Train3DMASCDialog : : closeTraceFile ( )
{
if ( m_traceFile )
if ( m_traceFile - > isOpen ( ) )
{
ccLog : : Print ( " [3DMASC] traces stored in: " + m_traceFile - > fileName ( ) ) ;
m_traceFile - > close ( ) ;
}
return true ;
}
2023-03-27 22:52:23 +02:00
void Train3DMASCDialog : : saveTraces ( ConfusionMatrix * confusionMatrix )
2023-03-22 23:45:19 +01:00
{
2023-03-27 22:52:23 +02:00
run + + ; // increment the run number
confusionMatrix - > setSessionRun ( m_baseName , run ) ;
2023-03-22 23:45:19 +01:00
if ( checkBox_keepTraces - > isChecked ( ) )
{
if ( ! traceFileConfigured ) // if the trace file is not configured yet, do it
{
if ( ! openTraceFile ( ) )
return ;
}
2023-03-27 22:52:23 +02:00
// save the trace
// save the run number and the overall accuracy
if ( m_traceStream . device ( ) )
2023-06-05 10:06:21 +02:00
m_traceStream < < run < < " " < < confusionMatrix - > getOverallAccuracy ( ) < < Qt : : endl ;
2023-03-27 22:52:23 +02:00
confusionMatrix - > save ( m_tracePath + " / " + " run_ " + QString : : number ( run ) + " _confusion_matrix.txt " ) ;
2023-03-22 23:45:19 +01:00
}
}
bool Train3DMASCDialog : : getSaveTrace ( )
{
return checkBox_keepTraces - > isChecked ( ) ;
}
QString Train3DMASCDialog : : getTracePath ( )
{
if ( traceFileConfigured )
{
QFileInfo fi ( * m_traceFile ) ;
return fi . absoluteDir ( ) . absolutePath ( ) ;
}
else if ( openTraceFile ( ) )
{
QFileInfo fi ( * m_traceFile ) ;
return fi . absoluteDir ( ) . absolutePath ( ) ;
}
else
return QString ( ) ;
}
int Train3DMASCDialog : : getRun ( )
{
return run ;
}