//########################################################################## //# # //# 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 #include #include #include #include #include #include #include //System #include static const int FeatureImportanceColumn = 1; Train3DMASCDialog::Train3DMASCDialog(QWidget* parent/*=nullptr*/) : QDialog(parent) , Ui::Train3DMASCDialog() , classifierSaved(false) , saveRequested(false) , traceFileConfigured(false) , m_traceFile(nullptr) , run(0) { setupUi(this); QDateTime dateTime = QDateTime::currentDateTime(); m_baseName = "3dmasc_" + dateTime.toString("yyyyMMdd_hh") + "h" + dateTime.toString("mm"); 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(); for (auto m : toDeleteLater) { if (m != nullptr) delete m; } } void Train3DMASCDialog::readSettings() { QSettings settings; settings.beginGroup("3DMASC"); bool keepAttributes = settings.value("keepAttributes", false).toBool(); this->keepAttributesCheckBox->setChecked(keepAttributes); bool saveTrace = settings.value("saveTrace", false).toBool(); setCheckBoxSaveTrace(saveTrace); } void Train3DMASCDialog::writeSettings() { QSettings settings; settings.beginGroup("3DMASC"); settings.setValue("keepAttributes", keepAttributesCheckBox->isChecked()); 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); QTableWidgetItem* importanceItem = new QTableWidgetItem(std::isnan(importance) ? QString() : QString::number(importance)); tableWidget->setItem(index, 1, importanceItem); return index; } int Train3DMASCDialog::addScale(double scale, bool isChecked/*=true*/) { int index = tableWidgetScales->rowCount(); tableWidgetScales->setRowCount(index + 1); QTableWidgetItem* nameItem = new QTableWidgetItem(QString::number(scale)); nameItem->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked); tableWidgetScales->setItem(index, 0, nameItem); return index; } 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); } bool Train3DMASCDialog::isFeatureSelected(QString featureName) const { for (int index = 0; index < tableWidget->rowCount(); ++index) { QTableWidgetItem* item = tableWidget->item(index, 0); if (item->text() == featureName) { return (item->checkState() == Qt::Checked); } } assert(false); return false; } void Train3DMASCDialog::sortByFeatureImportance() { tableWidget->sortByColumn(FeatureImportanceColumn, Qt::DescendingOrder); } void Train3DMASCDialog::setFeatureImportance(QString featureName, float importance) { for (int index = 0; index < tableWidget->rowCount(); ++index) { if (tableWidget->item(index, 0)->text() == featureName) { QTableWidgetItem* item = tableWidget->item(index, FeatureImportanceColumn); item->setText(std::isnan(importance) ? QString() : QString::number(importance, 'f', 6)); return; } } 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(); } 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); 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(); stream << featureName << ";" << importance << Qt::endl; } } void Train3DMASCDialog::addConfusionMatrixAndSaveTraces(ConfusionMatrix* confusionMatrix) { toDeleteLater.push_back(confusionMatrix); saveTraces(confusionMatrix); } 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::Directory); 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.exists(m_baseName)) { QDateTime dateTime = QDateTime::currentDateTime(); m_baseName += "min" + dateTime.toString("ss") + "s"; } 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->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) { run++; // increment the run number confusionMatrix->setSessionRun(m_baseName, run); 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()) m_traceStream << run << " " << confusionMatrix->getOverallAccuracy() << Qt::endl; confusionMatrix->save(m_tracePath + "/" + "run_" + QString::number(run) + "_confusion_matrix.txt"); } } 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; }