24 #include <QApplication>
25 #include <QFileDialog>
26 #include <QMessageBox>
58 QList<QAction*> group;
64 ":/CC/plugin/q3DMASCPlugin/3DMASC TRAIN.png")));
74 ":/CC/plugin/q3DMASCPlugin/3DMASC CLASSIFY.png")));
94 QString inputFilename;
97 settings.beginGroup(
"3DMASC");
99 settings.value(
"FilePath",
100 QCoreApplication::applicationDirPath())
102 inputFilename = QFileDialog::getOpenFileName(
105 if (inputFilename.isNull()) {
109 settings.setValue(
"FilePath", QFileInfo(inputFilename).absolutePath());
113 QList<QString> cloudLabels;
114 QString corePointsLabel;
115 bool filenamesSpecified =
false;
116 QMap<QString, QString> rolesAndNames;
118 inputFilename, cloudLabels, corePointsLabel, filenamesSpecified,
124 if (cloudLabels.empty()) {
128 }
else if (cloudLabels.size() >
129 4 + (cloudLabels.contains(
"TEST") ? 1 : 0)) {
131 "This classifier uses more than 4 clouds (the GUI version "
139 classifDlg.
setCloudRoles(cloudLabels, corePointsLabel, rolesAndNames);
140 classifDlg.label_trainOrClassify->setText(corePointsLabel +
141 " will be classified");
142 classifDlg.classifierFileLineEdit->setText(inputFilename);
143 classifDlg.testCloudComboBox->hide();
144 classifDlg.testLabel->hide();
145 if (!classifDlg.exec()) {
149 static bool s_keepAttributes =
150 classifDlg.keepAttributesCheckBox->isChecked();
153 QString mainCloudLabel = corePointsLabel;
168 if (clouds.contains(
"TEST")) {
170 clouds.remove(
"TEST");
181 progressDlg.setAutoClose(
false);
186 &generatedScalarFields)) {
192 QCoreApplication::processEvents();
196 QString errorMessage;
207 generatedScalarFields.
releaseSFs(s_keepAttributes);
221 QSettings settings(
"OSUR",
"q3DMASC");
222 settings.setValue(
"TrainParameters/maxDepth",
params.rt.maxDepth);
223 settings.setValue(
"TrainParameters/minSampleCount",
224 params.rt.minSampleCount);
225 settings.setValue(
"TrainParameters/activeVarCount",
226 params.rt.activeVarCount);
227 settings.setValue(
"TrainParameters/maxTreeCount",
params.rt.maxTreeCount);
231 QSettings settings(
"OSUR",
"q3DMASC");
232 params.rt.maxDepth = settings.value(
"TrainParameters/maxDepth", 25).toInt();
233 params.rt.minSampleCount =
234 settings.value(
"TrainParameters/minSampleCount", 10).toInt();
235 params.rt.activeVarCount =
236 settings.value(
"TrainParameters/activeVarCount", 0).toInt();
238 settings.value(
"TrainParameters/maxTreeCount", 100).toInt();
245 QString inputFilename;
248 settings.beginGroup(
"3DMASC");
250 settings.value(
"FilePath",
251 QCoreApplication::applicationDirPath())
253 inputFilename = QFileDialog::getOpenFileName(
256 if (inputFilename.isNull()) {
260 settings.setValue(
"FilePath", QFileInfo(inputFilename).absolutePath());
265 QList<QString> cloudLabels;
266 QString corePointsLabel;
267 bool filenamesSpecified =
false;
268 QMap<QString, QString> rolesAndNames;
270 inputFilename, cloudLabels, corePointsLabel, filenamesSpecified,
276 if (cloudLabels.empty()) {
287 bool useCloudsFromDB =
288 (!filenamesSpecified ||
290 "Use clouds in db (yes) or clouds specified "
293 QMessageBox::No) == QMessageBox::Yes);
294 QString mainCloudLabel = corePointsLabel;
295 if (useCloudsFromDB) {
296 if (cloudLabels.size() > 4 + (cloudLabels.contains(
"TEST") ? 1 : 0)) {
298 "This classifier uses more than 4 different clouds (the "
299 "GUI version cannot handle it)",
306 classifDlg.setWindowTitle(
"3DMASC Train");
307 classifDlg.
setCloudRoles(cloudLabels, corePointsLabel, rolesAndNames);
308 classifDlg.label_trainOrClassify->setText(
309 "The classifier will be trained on " + corePointsLabel);
310 classifDlg.classifierFileLineEdit->setText(inputFilename);
311 classifDlg.keepAttributesCheckBox
313 if (!classifDlg.exec()) {
321 corePoints.origin = loadedClouds[mainCloudLabel];
329 std::vector<double> scales;
344 if (mainCloudLabel.isEmpty()) {
350 "Missing 'Classification' field on core points cloud",
356 if (!useCloudsFromDB) {
359 for (masc::Tools::NamedClouds::const_iterator it = loadedClouds.begin();
360 it != loadedClouds.end(); ++it) {
365 for (masc::Tools::NamedClouds::iterator it = loadedClouds.begin();
366 it != loadedClouds.end(); ++it) {
376 bool needTestSuite =
false;
378 std::vector<double> scalesTest;
379 if (loadedClouds.contains(
"TEST")) {
380 testCloud = loadedClouds[
"TEST"];
381 loadedClouds.remove(
"TEST");
385 needTestSuite =
true;
388 loadedCloudsTest = loadedClouds;
389 loadedCloudsTest[mainCloudLabel] = testCloud;
395 scalesTest, loadedCloudsTest,
398 "Failed to load the training file (for TEST)",
407 trainDlg.setWindowModality(
409 trainDlg.maxDepthSpinBox->setValue(
s_params.rt.maxDepth);
410 trainDlg.maxTreeCountSpinBox->setValue(
s_params.rt.maxTreeCount);
411 trainDlg.activeVarCountSpinBox->setValue(
s_params.rt.activeVarCount);
412 trainDlg.minSampleCountSpinBox->setValue(
s_params.rt.minSampleCount);
413 trainDlg.testDataRatioSpinBox->setValue(
414 static_cast<int>(
s_params.testDataRatio * 100));
415 trainDlg.testDataRatioSpinBox->setEnabled(testCloud ==
nullptr);
421 std::vector<FeatureSelection> originalFeatures;
422 originalFeatures.reserve(features.size());
425 trainDlg.
addFeature(f->toString(), originalFeatures.back().importance,
426 originalFeatures.back().selected);
429 for (
double scale : scales) trainDlg.
addScale(scale,
true);
432 std::vector<FeatureSelection> originalFeaturesTest;
433 if (testCloud && needTestSuite) {
434 originalFeaturesTest.reserve(featuresTest.size());
440 static bool s_keepAttributes = trainDlg.keepAttributesCheckBox->isChecked();
441 if (!trainDlg.exec()) {
450 progressDlg.setAutoClose(
false);
464 QString corePointsName =
corePoints.origin->getName();
469 corePointsName +=
"_SS_Random@" +
473 corePointsName +=
"_SS_Spatial@" +
480 QString(
"Core points (%1)").arg(corePointsName));
486 QCoreApplication::processEvents();
493 QSharedPointer<cloudViewer::ReferenceCloud> trainSubset, testSubset;
494 float previousTestSubsetRatio = -1.0f;
500 for (
int iteration = 0;; ++iteration) {
504 for (
size_t i = 0; i < originalFeatures.size(); ++i) {
506 originalFeatures[i].feature->toString());
509 if (originalFeatures[i].selected) {
510 if (!originalFeatures[i].prepared) {
512 toPrepare.push_back(originalFeatures[i].feature);
514 features.push_back(originalFeatures[i].feature);
519 if (features.empty()) {
524 if (!toPrepare.empty()) {
529 &generatedScalarFields)) {
537 QCoreApplication::processEvents();
542 if (fs.selected && !fs.prepared) fs.prepared =
true;
547 s_params.rt.maxDepth = trainDlg.maxDepthSpinBox->value();
548 s_params.rt.maxTreeCount = trainDlg.maxTreeCountSpinBox->value();
550 trainDlg.activeVarCountSpinBox->value();
552 trainDlg.minSampleCountSpinBox->value();
553 float testDataRatio = 0.0f;
557 testDataRatio =
s_params.testDataRatio =
558 trainDlg.testDataRatioSpinBox->value() / 100.0f;
559 if (testDataRatio < 0.0f || testDataRatio > 0.99f) {
562 "Invalid test data ratio",
566 }
else if (previousTestSubsetRatio != testDataRatio) {
570 trainSubset->clear();
580 testSubset.data(), trainSubset.data())) {
582 "Not enough memory to generate the test "
589 previousTestSubsetRatio = testDataRatio;
599 QString errorMessage;
601 featureSources, errorMessage,
602 trainSubset.data(),
m_app,
621 featuresTest.clear();
623 for (
size_t i = 0; i < originalFeaturesTest.size();
625 originalFeaturesTest[i]
627 originalFeatures[i].feature->toString());
629 if (originalFeaturesTest[i].selected) {
630 if (!originalFeaturesTest[i].prepared) {
632 toPrepareTest.push_back(
633 originalFeaturesTest[i].feature);
635 featuresTest.push_back(
636 originalFeaturesTest[i].feature);
641 if (!toPrepareTest.empty()) {
643 corePointsTest.
cloud = testCloud;
644 corePointsTest.
origin = testCloud;
645 corePointsTest.
role = mainCloudLabel;
647 QCoreApplication::processEvents();
650 corePointsTest, toPrepareTest,
error,
652 &generatedScalarFieldsTest)) {
655 ERR_CONSOLE_MESSAGE);
661 QCoreApplication::processEvents();
666 if (fs.selected && !fs.prepared)
674 QString errorMessage;
677 testCloud ? testCloud :
corePoints.cloud, metrics,
678 errorMessage, trainDlg,
679 testCloud ?
nullptr : testSubset.data(),
680 testCloud ?
"Classification_prediction"
693 QString(
"Correct guess = %1 / %2 --> accuracy = %3")
704 assert(
static_cast<int>(features.size()) == importanceMat.rows);
705 int selectedFeatureIndex = 0;
706 for (
size_t i = 0; i < originalFeatures.size(); ++i) {
707 if (originalFeatures[i].selected) {
711 assert(selectedFeatureIndex < importanceMat.rows);
712 originalFeatures[i].importance =
713 importanceMat.at<
float>(selectedFeatureIndex,
715 ++selectedFeatureIndex;
717 originalFeatures[i].importance =
718 std::numeric_limits<float>::quiet_NaN();
721 originalFeatures[i].feature->toString(),
722 originalFeatures[i].importance);
732 if (!tracePath.isEmpty()) {
733 QString outputFilePath =
734 tracePath +
"/run_" +
735 QString::number(trainDlg.
getRun()) +
".txt";
737 outputFilePath, features, mainCloudLabel,
740 "Classifier succesfully saved to " +
746 "Failed to save classifier file");
750 QString
filename = tracePath +
"/run_" +
751 QString::number(trainDlg.
getRun()) +
752 "_feature_importance.txt";
754 if (!file.open(QFile::Text | QFile::WriteOnly)) {
756 QString(
"Can't open file '%1' for writing")
759 QTextStream stream(&file);
761 for (
size_t i = 0; i < originalFeatures.size(); ++i) {
762 if (originalFeatures[i].selected) {
763 stream << originalFeatures[i]
765 <<
" " << originalFeatures[i].importance
777 if (!trainDlg.exec()) {
780 if (trainDlg.keepAttributesCheckBox->isChecked())
781 s_keepAttributes =
true;
783 s_keepAttributes =
false;
784 generatedScalarFields.
releaseSFs(s_keepAttributes);
785 generatedScalarFieldsTest.
releaseSFs(s_keepAttributes);
792 QString outputFilename;
795 settings.beginGroup(
"3DMASC");
797 settings.value(
"FilePath",
799 applicationDirPath())
801 outputFilename = QFileDialog::getSaveFileName(
803 outputPath,
"*.txt");
804 if (outputFilename.isNull()) {
808 settings.setValue(
"FilePath",
809 QFileInfo(outputFilename).absolutePath());
815 mainCloudLabel, classifier,
818 "Classifier succesfully saved to " + outputFilename,
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
3DMASC plugin 'classify' dialog
void setCloudRoles(const QList< QString > &roles, QString &corePointsLabel, const QMap< QString, QString > &rolesAndNames)
Sets the clouds roles.
void getClouds(QMap< QString, ccPointCloud * > &clouds) const
Returns the selected point clouds.
void releaseSFs(bool keepByDefault)
3DMASC plugin 'train' dialog
void connectScaleSelectionToFeatureSelection()
void setClassifierSaved()
void setInputFilePath(QString filename)
bool isFeatureSelected(QString featureName) const
void sortByFeatureImportance()
int addFeature(QString name, float importance, bool isChecked=true)
Adds a feature (entry) to the results table.
int addScale(double scale, bool isChecked=true)
bool shouldSaveClassifier() const
void setFeatureImportance(QString featureName, float importance)
void setResultText(QString text)
virtual bool registerCommand(Command::Shared command)=0
Registers a new command.
Hierarchical CLOUDVIEWER Object.
unsigned getChildrenNumber() const
Returns the number of children.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
virtual void setEnabled(bool state)
Sets the "enabled" property.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
Standard ECV plugin interface.
ecvMainAppInterface * m_app
Main application interface.
A very simple point cloud (no point duplication)
Main application interface (for plugins)
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual QMainWindow * getMainWindow()=0
Returns main window.
virtual void addToDB(ccHObject *obj, bool updateZoom=false, bool autoExpandDBTree=true, bool checkDimensions=false, bool autoRedraw=true)=0
virtual void dispToConsole(QString message, ConsoleMessageLevel level=STD_CONSOLE_MESSAGE)=0
virtual void redrawAll(bool only2D=false, bool forceRedraw=true)
Graphical progress indicator (thread-safe)
bool isValid() const
Returns whether the classifier is valid or not.
bool evaluate(const Feature::Source::Set &featureSources, ccPointCloud *testCloud, AccuracyMetrics &metrics, QString &errorMessage, Train3DMASCDialog &train3DMASCDialog, cloudViewer::ReferenceCloud *testSubset=nullptr, QString outputSFName=QString(), QWidget *parentWidget=nullptr, ecvMainAppInterface *app=nullptr)
Evaluates the classifier.
bool classify(const Feature::Source::Set &featureSources, ccPointCloud *cloud, QString &errorMessage, QWidget *parentWidget=nullptr, ecvMainAppInterface *app=nullptr)
Applies the classifier.
cv::Mat getVarImportance() const
bool train(const ccPointCloud *cloud, const RandomTreesParams ¶ms, const Feature::Source::Set &featureSources, QString &errorMessage, cloudViewer::ReferenceCloud *trainSubset=nullptr, ecvMainAppInterface *app=nullptr, QWidget *parentWidget=nullptr)
Train the classifier.
void loadTrainParameters(masc::TrainParameters ¶ms)
QAction * m_classifyAction
Calssify action.
q3DMASCPlugin(QObject *parent=nullptr)
Default constructor.
QAction * m_trainAction
Train action.
void onNewSelection(const ccHObject::Container &selectedEntities) override
ccHObject::Container m_selectedEntities
Currently selected entities.
void saveTrainParameters(const masc::TrainParameters ¶ms)
void registerCommands(ccCommandLineInterface *cmd) override
Optional: registers commands (for the command line mode)
QList< QAction * > getActions() override
Get a list of actions for this plugin.
static void error(char *msg)
QTextStream & endl(QTextStream &stream)
static bool ShowClassifyDisclaimer(ecvMainAppInterface *app)
static bool ShowTrainDisclaimer(ecvMainAppInterface *app)
qHoughNormalsDialog::Parameters s_params
FeatureSelection(masc::Feature::Shared f=masc::Feature::Shared(nullptr))
masc::Feature::Shared feature
QSharedPointer< Command > Shared
Shared type.
Classifier accuracy metrics.
ccPointCloud * cloud
Core points cloud.
ccPointCloud * origin
Origin cloud.
QString role
Core points 'role'.
std::vector< Source > Set
static bool ExtractSources(const Set &features, Source::Set &sources)
Extracts the set of 'sources' from a set of features.
std::vector< Shared > Set
Set of features.
QSharedPointer< Feature > Shared
Shared type.