ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
qTrain3DMASCDialog.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "qTrain3DMASCDialog.h"
9 
10 // Qt
11 #include <CVLog.h>
12 #include <QtCompat.h>
13 
14 #include <QDateTime>
15 #include <QFileDialog>
16 #include <QMessageBox>
17 #include <QSettings>
18 #include <QStandardPaths>
19 #include <QTableWidgetItem>
20 
21 // System
22 #include <assert.h>
23 
24 #include <iostream>
25 
26 static const int FeatureImportanceColumn = 1;
27 
28 Train3DMASCDialog::Train3DMASCDialog(QWidget* parent /*=nullptr*/)
29  : QDialog(parent),
31  classifierSaved(false),
32  saveRequested(false),
33  traceFileConfigured(false),
34  m_traceFile(nullptr),
35  run(0) {
36  setupUi(this);
37 
38  QDateTime dateTime = QDateTime::currentDateTime();
39  m_baseName = "3dmasc_" + dateTime.toString("yyyyMMdd_hh") + "h" +
40  dateTime.toString("mm");
41 
42  readSettings();
43 
44  connect(closePushButton, SIGNAL(clicked()), this, SLOT(onClose()));
45  connect(savePushButton, SIGNAL(clicked()), this, SLOT(onSave()));
46  connect(exportToolButton, SIGNAL(clicked()), this, SLOT(onExportResults()));
47 }
48 
50  writeSettings();
52  for (auto m : toDeleteLater) {
53  if (m != nullptr) delete m;
54  }
55 }
56 
58  QSettings settings;
59  settings.beginGroup("3DMASC");
60  bool keepAttributes = settings.value("keepAttributes", false).toBool();
61  this->keepAttributesCheckBox->setChecked(keepAttributes);
62  bool saveTrace = settings.value("saveTrace", false).toBool();
63  setCheckBoxSaveTrace(saveTrace);
64 }
65 
67  QSettings settings;
68  settings.beginGroup("3DMASC");
69  settings.setValue("keepAttributes", keepAttributesCheckBox->isChecked());
70  settings.setValue("saveTrace", checkBox_keepTraces->isChecked());
71 }
72 
74  resultLabel->clear();
75  tableWidget->clear();
76 }
77 
79  float importance,
80  bool isChecked /*=true*/) {
81  int index = tableWidget->rowCount();
82  tableWidget->setRowCount(index + 1);
83 
84  QTableWidgetItem* nameItem = new QTableWidgetItem(name);
85  nameItem->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked);
86  tableWidget->setItem(index, 0, nameItem);
87 
88  QTableWidgetItem* importanceItem = new QTableWidgetItem(
89  std::isnan(importance) ? QString() : QString::number(importance));
90  tableWidget->setItem(index, 1, importanceItem);
91 
92  return index;
93 }
94 
95 int Train3DMASCDialog::addScale(double scale, bool isChecked /*=true*/) {
96  int index = tableWidgetScales->rowCount();
97  tableWidgetScales->setRowCount(index + 1);
98 
99  QTableWidgetItem* nameItem = new QTableWidgetItem(QString::number(scale));
100  nameItem->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked);
101  tableWidgetScales->setItem(index, 0, nameItem);
102 
103  return index;
104 }
105 
106 void Train3DMASCDialog::scaleStateChanged(QTableWidgetItem* item) {
107  // if the scale is not checked, remove automatically features based on this
108  // scale
109  QString scale = "_SC" + item->text() + "_";
110  for (int row = 0; row < tableWidget->rowCount(); row++) {
111  QTableWidgetItem* nameItem = tableWidget->item(row, 0);
112  QString name = nameItem->text();
113  if (name.contains(scale)) nameItem->setCheckState(item->checkState());
114  }
115 }
116 
118  connect(tableWidgetScales, &QTableWidget::itemChanged, this,
120 }
121 
123  resultLabel->setText(text);
124 }
125 
127  runPushButton->setText(tr("Retry"));
128  savePushButton->setEnabled(true);
129 }
130 
131 bool Train3DMASCDialog::isFeatureSelected(QString featureName) const {
132  for (int index = 0; index < tableWidget->rowCount(); ++index) {
133  QTableWidgetItem* item = tableWidget->item(index, 0);
134  if (item->text() == featureName) {
135  return (item->checkState() == Qt::Checked);
136  }
137  }
138 
139  assert(false);
140  return false;
141 }
142 
144  tableWidget->sortByColumn(FeatureImportanceColumn, Qt::DescendingOrder);
145 }
146 
148  float importance) {
149  for (int index = 0; index < tableWidget->rowCount(); ++index) {
150  if (tableWidget->item(index, 0)->text() == featureName) {
151  QTableWidgetItem* item =
152  tableWidget->item(index, FeatureImportanceColumn);
153  item->setText(std::isnan(importance)
154  ? QString()
155  : QString::number(importance, 'f', 6));
156  return;
157  }
158  }
159 
160  assert(false);
161 }
162 
164  if (!classifierSaved &&
165  QMessageBox::question(this, "Classifier not saved",
166  "Classifier not saved. Do you confirm you want "
167  "to close the tool?",
168  QMessageBox::Yes,
169  QMessageBox::No) == QMessageBox::No)
170  return;
171 
172  reject();
173 }
174 
176  saveRequested = true;
177  accept();
178 }
179 
180 void Train3DMASCDialog::onExportResults(QString filePath /*=""*/) {
181  QSettings settings;
182  settings.beginGroup("3DMASC");
183  QString outputPath =
184  settings.value("FilePath", QCoreApplication::applicationDirPath())
185  .toString();
186  QString outputFilename = QFileDialog::getSaveFileName(
187  this, "Export feature importance matrix", outputPath, "*.csv");
188  if (outputFilename.isNull()) {
189  // process cancelled by the user
190  return;
191  }
192  settings.setValue("FilePath", QFileInfo(outputFilename).absolutePath());
193  settings.endGroup();
194 
195  // save the file
196  QFile file(outputFilename);
197  if (!file.open(QFile::WriteOnly | QFile::Text)) {
198  QMessageBox::critical(
199  this, "Error",
200  "Failed to open file for writing: " + outputFilename);
201  return;
202  }
203 
204  QTextStream stream(&file);
205  stream << "Feature;Importance" << QtCompat::endl;
206  for (int index = 0; index < tableWidget->rowCount(); ++index) {
207  QString featureName = tableWidget->item(index, 0)->text();
208  QString importance =
209  tableWidget->item(index, FeatureImportanceColumn)->text();
210  stream << featureName << ";" << importance << QtCompat::endl;
211  }
212 }
213 
215  ConfusionMatrix* confusionMatrix) {
216  toDeleteLater.push_back(confusionMatrix);
217  saveTraces(confusionMatrix);
218 }
219 
220 void Train3DMASCDialog::setInputFilePath(QString filePath) {
221  m_parameterFilePath = filePath;
222 }
223 
225  checkBox_keepTraces->setChecked(state);
226 }
227 
229  QString traceFileName;
230  QString traceFilePath;
231 
232  // get currentPath
233  QFileInfo info(m_parameterFilePath);
234  QDir parameterDir = QDir(info.path());
235 
236  QFileDialog dialog(this);
237 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
238  // Qt5.15+ and Qt6: Use new API (DirectoryOnly is deprecated)
239  dialog.setFileMode(QFileDialog::Directory);
240  dialog.setOption(QFileDialog::ShowDirsOnly, true);
241 #else
242  // Qt5.12-5.14: Use old API
243  dialog.setFileMode(QFileDialog::DirectoryOnly);
244 #endif
245  dialog.setWindowTitle("Choose a valid directory for the traces");
246  dialog.setDirectory(
247  QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation)
248  .at(0));
249 
250  if (!m_traceFile) // create trace file if it does not exists already
251  {
252  m_tracePath = parameterDir.absolutePath() + "/" + m_baseName;
253  traceFileName = m_baseName + ".txt";
254 
255  if (parameterDir.exists(m_baseName)) {
256  QDateTime dateTime = QDateTime::currentDateTime();
257  m_baseName += "min" + dateTime.toString("ss") + "s";
258  }
259  if (!parameterDir.mkdir(m_baseName)) // create a specific directory to
260  // store the traces
261  {
262  CVLog::Error("impossible to save in the default directory: " +
263  m_tracePath);
264  // impossible to save in the default directory, you have to propose
265  // another path
266  if (dialog.exec())
267  m_tracePath = dialog.selectedFiles().at(0);
268  else
269  return false;
270  } else
271  CVLog::Print("directory for traces created: " + m_tracePath);
272 
273  traceFilePath = m_tracePath + "/" + traceFileName;
274  m_traceFile = new QFile(traceFilePath);
275 
276  if (!m_traceFile->open(QIODevice::WriteOnly | QIODevice::Text)) {
277  CVLog::Error("impossible to open trace file: " + traceFilePath);
278  delete m_traceFile;
279  m_tracePath.clear();
280  return false;
281  }
282  }
283 
284  if (m_traceFile->isOpen()) {
285  traceFileConfigured = true;
286  CVLog::Print("save trace in: " + traceFilePath);
287  m_traceStream.setDevice(m_traceFile);
288  m_traceStream << "run overallAccuracy\n";
289  return true;
290  } else {
291  return false;
292  }
293 }
294 
296  if (m_traceFile)
297  if (m_traceFile->isOpen()) {
298  CVLog::Print("[3DMASC] traces stored in: " +
299  m_traceFile->fileName());
300  m_traceFile->close();
301  }
302 
303  return true;
304 }
305 
307  run++; // increment the run number
308  confusionMatrix->setSessionRun(m_baseName, run);
309  if (checkBox_keepTraces->isChecked()) {
310  if (!traceFileConfigured) // if the trace file is not configured yet,
311  // do it
312  {
313  if (!openTraceFile()) return;
314  }
315  // save the trace
316 
317  // save the run number and the overall accuracy
318  if (m_traceStream.device())
319  m_traceStream << run << " " << confusionMatrix->getOverallAccuracy()
320  << QtCompat::endl;
321  confusionMatrix->save(m_tracePath + "/" + "run_" +
322  QString::number(run) + "_confusion_matrix.txt");
323  }
324 }
325 
327  return checkBox_keepTraces->isChecked();
328 }
329 
331  if (traceFileConfigured) {
332  QFileInfo fi(*m_traceFile);
333  return fi.absoluteDir().absolutePath();
334  } else if (openTraceFile()) {
335  QFileInfo fi(*m_traceFile);
336  return fi.absoluteDir().absolutePath();
337  } else
338  return QString();
339 }
340 
std::string name
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
void setSessionRun(QString session, int run)
bool save(QString filePath)
3DMASC plugin 'train' dialog
void connectScaleSelectionToFeatureSelection()
void saveTraces(ConfusionMatrix *confusionMatrix)
void addConfusionMatrixAndSaveTraces(ConfusionMatrix *ptr)
void setInputFilePath(QString filename)
bool isFeatureSelected(QString featureName) const
QTextStream m_traceStream
int addFeature(QString name, float importance, bool isChecked=true)
Adds a feature (entry) to the results table.
int addScale(double scale, bool isChecked=true)
void setCheckBoxSaveTrace(bool state)
void scaleStateChanged(QTableWidgetItem *item)
Train3DMASCDialog(QWidget *parent=nullptr)
Default constructor.
std::vector< ConfusionMatrix * > toDeleteLater
void setFeatureImportance(QString featureName, float importance)
void onExportResults(QString filePath="")
void setResultText(QString text)
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
Rgb at(size_t color_id)
static const int FeatureImportanceColumn