From ec5507be5bc37ed3beac3221078312650364e7b9 Mon Sep 17 00:00:00 2001 From: Paul Leroy Date: Wed, 22 Mar 2023 23:45:19 +0100 Subject: [PATCH] save traces When the option is selected, overall accuracy, features and classifiers are saved --- Train3DMASCDialog.ui | 10 +++ confusionmatrix.cpp | 19 +++-- confusionmatrix.h | 2 + confusionmatrix.ui | 63 ++++------------ q3DMASC.cpp | 21 ++++++ q3DMASCTools.cpp | 2 +- qTrain3DMASCDialog.cpp | 160 +++++++++++++++++++++++++++++++++++++++++ qTrain3DMASCDialog.h | 21 ++++++ 8 files changed, 244 insertions(+), 54 deletions(-) diff --git a/Train3DMASCDialog.ui b/Train3DMASCDialog.ui index c79d19b..77afc53 100644 --- a/Train3DMASCDialog.ui +++ b/Train3DMASCDialog.ui @@ -259,6 +259,16 @@ + + + + <html><head/><body><p>If checked, a directory is created near the parameters file. Each time you click on Run, the features used to train the random forest will be stored and an entry will be created in a specific file to save the metrics associated with the classifier.</p></body></html> + + + Keep traces + + + diff --git a/confusionmatrix.cpp b/confusionmatrix.cpp index 25d498c..b68d467 100644 --- a/confusionmatrix.cpp +++ b/confusionmatrix.cpp @@ -89,7 +89,9 @@ float ConfusionMatrix::computeOverallAccuracy(cv::Mat& matrix) int nbClasses = matrix.rows; float totalTrue = 0; float totalFalse = 0; - float overallAccuracy = 0.; + + m_overallAccuracy = 0.0; + for (int realIdx = 0; realIdx < nbClasses; realIdx++) { for (int predictedIdx = 0; predictedIdx< nbClasses; predictedIdx++) @@ -101,11 +103,11 @@ float ConfusionMatrix::computeOverallAccuracy(cv::Mat& matrix) } } if ((totalTrue + totalFalse) != 0) - overallAccuracy = totalTrue / (totalTrue + totalFalse); + m_overallAccuracy = totalTrue / (totalTrue + totalFalse); else - overallAccuracy = CCCoreLib::NAN_VALUE; + m_overallAccuracy = CCCoreLib::NAN_VALUE; - return overallAccuracy; + return m_overallAccuracy; } void ConfusionMatrix::compute(std::vector& actual, std::vector& predicted) @@ -219,3 +221,12 @@ void ConfusionMatrix::compute(std::vector& actual, std::vectorui->tableWidget->setItem(2 + realIdx, 2 + nbClasses + F1_SCORE, newItem); } } + +void ConfusionMatrix::setSessionRun(QString session, int run) +{ + QString label; + + label = session + " / " + QString::number(run); + + this->ui->label_sessionRun->setText(label); +} diff --git a/confusionmatrix.h b/confusionmatrix.h index fb8e86c..5999664 100644 --- a/confusionmatrix.h +++ b/confusionmatrix.h @@ -29,6 +29,8 @@ public: void computePrecisionRecallF1Score(cv::Mat& matrix, cv::Mat& precisionRecallF1Score); float computeOverallAccuracy(cv::Mat& matrix); void compute(std::vector& actual, std::vector& predicted); + void setSessionRun(QString session, int run); + float m_overallAccuracy; private: Ui::ConfusionMatrix *ui; diff --git a/confusionmatrix.ui b/confusionmatrix.ui index ff62d0b..1216b87 100644 --- a/confusionmatrix.ui +++ b/confusionmatrix.ui @@ -13,8 +13,8 @@ Form - - + + Qt::ScrollBarAlwaysOff @@ -36,25 +36,6 @@ - - - - - true - - - - Overall accuracy - - - - - - - - - - - @@ -68,51 +49,35 @@ - - - - false - - - - true - - - - Session - - - - - - - false - + + - - - - - false - + + true - Run + Overall accuracy - - + + false + + + false + + - diff --git a/q3DMASC.cpp b/q3DMASC.cpp index 7d13295..dfbef64 100644 --- a/q3DMASC.cpp +++ b/q3DMASC.cpp @@ -390,6 +390,7 @@ void q3DMASCPlugin::doTrainAction() trainDlg.minSampleCountSpinBox->setValue(s_params.rt.minSampleCount); trainDlg.testDataRatioSpinBox->setValue(static_cast(s_params.testDataRatio * 100)); trainDlg.testDataRatioSpinBox->setEnabled(testCloud == nullptr); + trainDlg.setInputFilePath(inputFilename); //display the loaded features and let the user select the ones to use trainDlg.setResultText("Select features and press 'Run'"); @@ -689,6 +690,26 @@ void q3DMASCPlugin::doTrainAction() } trainDlg.sortByFeatureImportance(); + + // if the checkbox "Save traces" is checked + if (trainDlg.getSaveTrace()) + { + //save the classifier in the trace directory with a generic name depending on the run + QString tracePath = trainDlg.getTracePath(); + if (!tracePath.isEmpty()) + { + QString outputFilename = tracePath + "/run_" + QString::number(trainDlg.getRun()) + ".txt"; + if (masc::Tools::SaveClassifier(outputFilename, features, mainCloudLabel, 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"); + } + } + } } } diff --git a/q3DMASCTools.cpp b/q3DMASCTools.cpp index 3bd3655..df64dd8 100644 --- a/q3DMASCTools.cpp +++ b/q3DMASCTools.cpp @@ -51,7 +51,7 @@ bool Tools::SaveClassifier( QString filename, const masc::Classifier& classifier, QWidget* parent/*=nullptr*/) { - //first save the classifier data (same base filename but with the ymal extension) + //first save the classifier data (same base filename but with the yaml extension) QFileInfo fi(filename); QString yamlFilename = fi.baseName() + ".yaml"; QString yamlAbsoluteFilename = fi.absoluteDir().absoluteFilePath(yamlFilename); diff --git a/qTrain3DMASCDialog.cpp b/qTrain3DMASCDialog.cpp index f18c10a..b17e205 100644 --- a/qTrain3DMASCDialog.cpp +++ b/qTrain3DMASCDialog.cpp @@ -23,6 +23,10 @@ #include #include #include +#include +#include + +#include //System #include @@ -36,14 +40,43 @@ Train3DMASCDialog::Train3DMASCDialog(QWidget* parent/*=nullptr*/) , Ui::Train3DMASCDialog() , classifierSaved(false) , saveRequested(false) + , traceFileConfigured(false) + , m_traceFile(nullptr) { setupUi(this); + QDateTime dateTime = QDateTime::currentDateTime(); + m_baseName = "3dmasc_" + dateTime.toString("yyyyMMdd") + "_" + dateTime.toString("hh") + "h" + dateTime.toString("mm"); + run = 0; + + readSettings(); + connect(closePushButton, SIGNAL(clicked()), this, SLOT(onClose())); connect(savePushButton, SIGNAL(clicked()), this, SLOT(onSave())); connect(exportToolButton, SIGNAL(clicked()), this, SLOT(onExportResults())); } +Train3DMASCDialog::~Train3DMASCDialog() +{ + writeSettings(); + closeTraceFile(); +} + +void Train3DMASCDialog::readSettings() +{ + QSettings settings; + settings.beginGroup("3DMASC"); + bool saveTrace = settings.value("saveTrace", false).toBool(); + setCheckBoxSaveTrace(saveTrace); +} + +void Train3DMASCDialog::writeSettings() +{ + QSettings settings; + settings.beginGroup("3DMASC"); + settings.setValue("saveTrace", checkBox_keepTraces->isChecked()); +} + void Train3DMASCDialog::clearResults() { resultLabel->clear(); @@ -189,5 +222,132 @@ void Train3DMASCDialog::onExportResults() void Train3DMASCDialog::addConfusionMatrix(std::unique_ptr& ptr) { + run++; // increment the run number + ptr->setSessionRun(m_baseName, run); + saveTraces(*ptr); m_confusionMatrices.push_back(std::move(ptr)); } + +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"; + + 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; +} + +void Train3DMASCDialog::saveTraces(ConfusionMatrix &confusionMatrix) +{ + if (checkBox_keepTraces->isChecked()) + { + if (!traceFileConfigured) // if the trace file is not configured yet, do it + { + if (!openTraceFile()) + return; + } + else // save the trace + { + // save the run number and the overall accuracy + if (m_traceStream.device()) + m_traceStream << run << " " << confusionMatrix.m_overallAccuracy << Qt::endl; + // save the confusion matrix + // save the features + // save the classifier + } + } +} + +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; +} diff --git a/qTrain3DMASCDialog.h b/qTrain3DMASCDialog.h index c069031..76b10c3 100644 --- a/qTrain3DMASCDialog.h +++ b/qTrain3DMASCDialog.h @@ -19,6 +19,8 @@ //Qt #include +#include +#include #include @@ -33,6 +35,10 @@ public: //! Default constructor Train3DMASCDialog(QWidget* parent = nullptr); + ~Train3DMASCDialog(); + + void readSettings(); + void writeSettings(); void clearResults(); @@ -54,6 +60,14 @@ public: inline bool shouldSaveClassifier() const { return saveRequested; } void addConfusionMatrix(std::unique_ptr& ptr); + void setInputFilePath(QString filename); + void setCheckBoxSaveTrace(bool state); + bool openTraceFile(); + bool closeTraceFile(); + void saveTraces(ConfusionMatrix &confusionMatrix); + bool getSaveTrace(); + QString getTracePath(); + int getRun(); protected slots: @@ -66,4 +80,11 @@ protected: //members bool classifierSaved; bool saveRequested; std::vector> m_confusionMatrices; + bool traceFileConfigured; + QFile *m_traceFile; + QString m_tracePath; + QTextStream m_traceStream; + QString m_parameterFilePath; + QString m_baseName; + uint run; };