Files
q3DMASC/qTrain3DMASCDialog.cpp
T

366 lines
10 KiB
C++
Raw Normal View History

//##########################################################################
//# #
//# 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"
//Qt
#include <QTableWidgetItem>
#include <QMessageBox>
#include <QFileDialog>
#include <QSettings>
#include <QTextStream>
2023-03-22 23:45:19 +01:00
#include <QStandardPaths>
#include <QDateTime>
#include <ccLog.h>
//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;
Train3DMASCDialog::Train3DMASCDialog(QWidget* parent/*=nullptr*/)
: QDialog(parent)
, Ui::Train3DMASCDialog()
, classifierSaved(false)
, saveRequested(false)
2023-03-22 23:45:19 +01:00
, traceFileConfigured(false)
, m_traceFile(nullptr)
, run(0)
{
setupUi(this);
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();
connect(closePushButton, SIGNAL(clicked()), this, SLOT(onClose()));
connect(savePushButton, SIGNAL(clicked()), this, SLOT(onSave()));
connect(exportToolButton, SIGNAL(clicked()), this, SLOT(onExportResults()));
}
2023-03-22 23:45:19 +01:00
Train3DMASCDialog::~Train3DMASCDialog()
{
writeSettings();
closeTraceFile();
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");
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");
settings.setValue("keepAttributes", keepAttributesCheckBox->isChecked());
2023-03-22 23:45:19 +01:00
settings.setValue("saveTrace", checkBox_keepTraces->isChecked());
}
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));
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);
}
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-03-27 15:03:58 +01:00
for (int index = 0; index < tableWidget->rowCount(); ++index)
{
2019-03-27 15:03:58 +01:00
QTableWidgetItem* item = tableWidget->item(index, 0);
if (item->text() == featureName)
{
return (item->checkState() == Qt::Checked);
}
}
2019-03-27 15:03:58 +01:00
assert(false);
return false;
}
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-03-27 15:03:58 +01:00
for (int index = 0; index < tableWidget->rowCount(); ++index)
{
2019-03-27 15:03:58 +01:00
if (tableWidget->item(index, 0)->text() == featureName)
{
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-03-27 15:03:58 +01:00
assert(false);
}
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();
}
2023-03-28 17:19:41 +02:00
void Train3DMASCDialog::onExportResults(QString filePath/*=""*/)
{
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;
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;
}
2022-10-17 17:33:15 +02:00
}
2023-01-18 23:57:59 +01:00
void Train3DMASCDialog::addConfusionMatrixAndSaveTraces(ConfusionMatrix* confusionMatrix)
2023-01-18 23:57:59 +01: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;
}
void Train3DMASCDialog::saveTraces(ConfusionMatrix *confusionMatrix)
2023-03-22 23:45:19 +01: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;
}
// 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;
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;
}