diff --git a/CMakeLists.txt b/CMakeLists.txt index de33d8d..68f5992 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ if (PLUGIN_STANDARD_3DMASC) project( Q3DMASC_PLUGIN ) AddPlugin( NAME ${PROJECT_NAME} ) - set(Q3DMASC_PLUGIN_VERSION "1.0") + set(Q3DMASC_PLUGIN_VERSION "1.0+") include( CMakePolicies NO_POLICY_SCOPE ) diff --git a/Classify3DMASCDialog.ui b/Classify3DMASCDialog.ui index 7b147d2..e2c8b25 100644 --- a/Classify3DMASCDialog.ui +++ b/Classify3DMASCDialog.ui @@ -65,14 +65,14 @@ - PC2 + - - CTX + - @@ -112,14 +112,14 @@ - PC1 + - - PCX + - diff --git a/q3DMASC.cpp b/q3DMASC.cpp index fb6252c..65506bf 100644 --- a/q3DMASC.cpp +++ b/q3DMASC.cpp @@ -120,7 +120,8 @@ void q3DMASCPlugin::doClassifyAction() QList cloudLabels; QString corePointsLabel; bool filenamesSpecified = false; - if (!masc::Tools::LoadClassifierCloudLabels(inputFilename, cloudLabels, corePointsLabel, filenamesSpecified)) + QMap labelMapName; + if (!masc::Tools::LoadClassifierCloudLabels(inputFilename, cloudLabels, corePointsLabel, filenamesSpecified, labelMapName)) { m_app->dispToConsole("Failed to read classifier file (see Console)", ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; @@ -138,7 +139,7 @@ void q3DMASCPlugin::doClassifyAction() //now show a dialog where the user will be able to set the cloud roles Classify3DMASCDialog classifDlg(m_app); - classifDlg.setCloudRoles(cloudLabels, corePointsLabel); + classifDlg.setCloudRoles(cloudLabels, corePointsLabel, labelMapName); classifDlg.label_trainOrClassify->setText(corePointsLabel + " will be classified"); classifDlg.classifierFileLineEdit->setText(inputFilename); classifDlg.testCloudComboBox->hide(); @@ -260,7 +261,8 @@ void q3DMASCPlugin::doTrainAction() QList cloudLabels; QString corePointsLabel; bool filenamesSpecified = false; - if (!masc::Tools::LoadClassifierCloudLabels(inputFilename, cloudLabels, corePointsLabel, filenamesSpecified)) + QMap rolesAndNames; + if (!masc::Tools::LoadClassifierCloudLabels(inputFilename, cloudLabels, corePointsLabel, filenamesSpecified, rolesAndNames)) { m_app->dispToConsole("Failed to read classifier file (see Console)", ccMainAppInterface::ERR_CONSOLE_MESSAGE); return; @@ -288,7 +290,7 @@ void q3DMASCPlugin::doTrainAction() //now show a dialog where the user will be able to set the cloud roles Classify3DMASCDialog classifDlg(m_app, true); classifDlg.setWindowTitle("3DMASC Train"); - classifDlg.setCloudRoles(cloudLabels, corePointsLabel); + classifDlg.setCloudRoles(cloudLabels, corePointsLabel, rolesAndNames); classifDlg.label_trainOrClassify->setText("The classifier will be trained on " + corePointsLabel); classifDlg.classifierFileLineEdit->setText(inputFilename); classifDlg.keepAttributesCheckBox->hide(); // this parameter is set in the trainDlg dialog diff --git a/q3DMASCCommands.h b/q3DMASCCommands.h index 223530b..51b6398 100644 --- a/q3DMASCCommands.h +++ b/q3DMASCCommands.h @@ -165,7 +165,8 @@ struct Command3DMASCClassif : public ccCommandLineInterface::Command QList cloudLabels; QString corePointsLabel; bool filenamesSpecified = false; - if (!masc::Tools::LoadClassifierCloudLabels(classifierFilename, cloudLabels, corePointsLabel, filenamesSpecified)) + QMap labelsAndNames; + if (!masc::Tools::LoadClassifierCloudLabels(classifierFilename, cloudLabels, corePointsLabel, filenamesSpecified, labelsAndNames)) { return cmd.error("Failed to read classifier file"); } diff --git a/q3DMASCTools.cpp b/q3DMASCTools.cpp index 7ceb73f..8b1f7b4 100644 --- a/q3DMASCTools.cpp +++ b/q3DMASCTools.cpp @@ -112,11 +112,12 @@ bool Tools::SaveClassifier( QString filename, return true; } -bool Tools::LoadClassifierCloudLabels(QString filename, QList& labels, QString& corePointsLabel, bool& filenamesSpecified) +bool Tools::LoadClassifierCloudLabels(QString filename, QList& labels, QString& corePointsLabel, bool& filenamesSpecified, QMap& labelsAndNames) { //just in case corePointsLabel.clear(); labels.clear(); + labelsAndNames.clear(); QFile file(filename); if (!file.open(QFile::Text | QFile::ReadOnly)) @@ -155,6 +156,7 @@ bool Tools::LoadClassifierCloudLabels(QString filename, QList& labels, return false; } labels.push_back(label); + labelsAndNames[label] = tokens.back(); if (tokens.size() > 1) ++filenameCount; diff --git a/q3DMASCTools.h b/q3DMASCTools.h index bb3eb6c..a3f142a 100644 --- a/q3DMASCTools.h +++ b/q3DMASCTools.h @@ -40,7 +40,7 @@ namespace masc static bool LoadTrainingFile(QString filename, Feature::Set& rawFeatures, std::vector& scales, NamedClouds& loadedClouds, TrainParameters& parameters, CorePoints* corePoints = nullptr, QWidget* parent = nullptr); - static bool LoadClassifierCloudLabels(QString filename, QList& labels, QString& corePointsLabel, bool& filenamesSpecified); + static bool LoadClassifierCloudLabels(QString filename, QList& labels, QString& corePointsLabel, bool& filenamesSpecified, QMap& labelsAndNames); static bool LoadClassifier(QString filename, NamedClouds& clouds, Feature::Set& rawFeatures, masc::Classifier& classifier, QWidget* parent = nullptr); diff --git a/qClassify3DMASCDialog.cpp b/qClassify3DMASCDialog.cpp index d2c825d..d9a6ba9 100644 --- a/qClassify3DMASCDialog.cpp +++ b/qClassify3DMASCDialog.cpp @@ -28,10 +28,7 @@ #include #include #include -//#include -//system -#include static ccPointCloud* GetCloudFromCombo(QComboBox* comboBox, ccHObject* dbRoot) { @@ -91,6 +88,8 @@ Classify3DMASCDialog::Classify3DMASCDialog(ccMainAppInterface* app, bool trainMo } } + testCloudComboBox->addItem("", 0); + //if 3 clouds are loaded, then there's chances that the first one is the global cloud! cloud1ComboBox->setCurrentIndex(/*cloudCount > 0 ? (cloudCount > 2 ? 1 : 0) : */-1); connect(cloud1ComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCloudChanged(int))); @@ -140,70 +139,109 @@ void Classify3DMASCDialog::writeSettings() settings.setValue("keepAttributes", keepAttributesCheckBox->isChecked()); } -void Classify3DMASCDialog::setCloudRoles(const QList& roles, QString corePointsLabel) +void Classify3DMASCDialog::setComboBoxIndex(QMap rolesAndNames, QLabel* label, QMap namesAndUniqueIds, QComboBox* comboBox) +{ + QString name; + + if (label != testLabel) + { + name = rolesAndNames[label->text()]; + } + else + { + name = rolesAndNames[QString("TEST")]; // this is because the label text of testLabel is 'TEST on (optional)' + } + QMap::iterator it = namesAndUniqueIds.find(name); + if (it != namesAndUniqueIds.end()) + { + int index = comboBox->findData(it.value()); + if (index != -1) + { + comboBox->setCurrentIndex(index); + } + } +} + +void Classify3DMASCDialog::setCloudRoles(const QList& roles, QString& corePointsLabel, const QMap& rolesAndNames) { int index = 0; for (const QString& role : roles) { - switch (index) + if (role != "TEST") { - case 0: - cloud1Label->setText(role); - if (corePointsLabel.isEmpty()) // if "core_points:" is not in the parameter file, the corePointsLabel is the first encountered role - corePointsLabel = role; -// cloud1RadioButton->setChecked(true); - break; - case 1: - cloud2Label->setText(role); - if (corePointsLabel == role) -// cloud2RadioButton->setChecked(true); - break; - case 2: - cloud3Label->setText(role); - if (corePointsLabel == role) -// cloud3RadioButton->setChecked(true); - break; - case 3: - cloud4Label->setText(role); - if (corePointsLabel == role) -// cloud4RadioButton->setChecked(true); - break; - default: - //this dialog can't handle more than 3 roles! - break; + switch (index) + { + case 0: + cloud1Label->setText(role); + if (corePointsLabel.isEmpty()) // if "core_points:" is not in the parameter file, the corePointsLabel is the first encountered role + { + corePointsLabel = role; + } + break; + case 1: + cloud2Label->setText(role); + break; + case 2: + cloud3Label->setText(role); + break; + case 3: + cloud4Label->setText(role); + break; + default: + //this dialog can't handle more than 3 roles! + break; + } + ++index; } - ++index; } if (index < 1) { -// cloud1RadioButton->setEnabled(false); cloud1ComboBox->setEnabled(false); } if (index < 2) { -// cloud2RadioButton->setEnabled(false); -// cloud2RadioButton->setVisible(false); cloud2ComboBox->setEnabled(false); cloud2ComboBox->setVisible(false); cloud2Label->setVisible(false); } if (index < 3) { -// cloud3RadioButton->setEnabled(false); -// cloud3RadioButton->setVisible(false); cloud3ComboBox->setEnabled(false); cloud3ComboBox->setVisible(false); cloud3Label->setVisible(false); } if (index < 4) { -// cloud4RadioButton->setEnabled(false); -// cloud4RadioButton->setVisible(false); cloud4ComboBox->setEnabled(false); cloud4ComboBox->setVisible(false); cloud4Label->setVisible(false); } + + // now we will try to preset the combo boxes depending on the names which are in the parameter file + QMap namesAndUniqueIds; + + // build a map 'name : uniqueId' of the available clouds in the database tree (duplicate names are not handled, simply keep the first occurrence) + ccHObject::Container clouds; + if (m_app->dbRootObject()) + { + m_app->dbRootObject()->filterChildren(clouds, true, CC_TYPES::POINT_CLOUD); + } + for (size_t i = 0; i < clouds.size(); ++i) + { + if (clouds[i]->isA(CC_TYPES::POINT_CLOUD)) //as filterChildren only test 'isKindOf' + { + QVariant uniqueID(clouds[i]->getUniqueID()); + namesAndUniqueIds[clouds[i]->getName().toUpper()] = uniqueID; + } + } + + // preset the combo boxes if possible + setComboBoxIndex(rolesAndNames, cloud1Label, namesAndUniqueIds, cloud1ComboBox); + setComboBoxIndex(rolesAndNames, cloud2Label, namesAndUniqueIds, cloud2ComboBox); + setComboBoxIndex(rolesAndNames, cloud3Label, namesAndUniqueIds, cloud3ComboBox); + setComboBoxIndex(rolesAndNames, cloud4Label, namesAndUniqueIds, cloud4ComboBox); + setComboBoxIndex(rolesAndNames, testLabel, namesAndUniqueIds, testCloudComboBox); } void Classify3DMASCDialog::getClouds(QMap& clouds) const @@ -214,42 +252,26 @@ void Classify3DMASCDialog::getClouds(QMap& clouds) const return; } -// if (cloud1RadioButton->isEnabled()) if (cloud1ComboBox->isEnabled()) { clouds.insert(cloud1Label->text(), GetCloudFromCombo(cloud1ComboBox, m_app->dbRootObject())); -// if (cloud1RadioButton->isChecked()) -// { -// mainCloud = cloud1Label->text(); -// } } -// if (cloud2RadioButton->isEnabled()) + if (cloud2ComboBox->isEnabled()) { clouds.insert(cloud2Label->text(), GetCloudFromCombo(cloud2ComboBox, m_app->dbRootObject())); -// if (cloud2RadioButton->isChecked()) -// { -// mainCloud = cloud2Label->text(); -// } } -// if (cloud3RadioButton->isEnabled()) + if (cloud3ComboBox->isEnabled()) { clouds.insert(cloud3Label->text(), GetCloudFromCombo(cloud3ComboBox, m_app->dbRootObject())); -// if (cloud3RadioButton->isChecked()) -// { -// mainCloud = cloud3Label->text(); -// } } -// if (cloud4RadioButton->isEnabled()) + if (cloud4ComboBox->isEnabled()) { clouds.insert(cloud4Label->text(), GetCloudFromCombo(cloud4ComboBox, m_app->dbRootObject())); -// if (cloud4RadioButton->isChecked()) -// { -// mainCloud = cloud4Label->text(); -// } } + if (testCloudComboBox->currentIndex() >= 0) { clouds.insert("TEST", GetCloudFromCombo(testCloudComboBox, m_app->dbRootObject())); diff --git a/qClassify3DMASCDialog.h b/qClassify3DMASCDialog.h index a9599ec..1d6de90 100644 --- a/qClassify3DMASCDialog.h +++ b/qClassify3DMASCDialog.h @@ -42,7 +42,10 @@ public: void writeSettings(); //! Sets the clouds roles - void setCloudRoles(const QList& roles, QString corePointsLabel); + void setCloudRoles(const QList& roles, QString &corePointsLabel, const QMap &rolesAndNames); + + //! Preset the combo boxes if possible with the names specified in the parameter file + void setComboBoxIndex(QMap rolesAndNames, QLabel* label, QMap namesAndUniqueIds, QComboBox* comboBox); //! Returns the selected point clouds void getClouds(QMap& clouds) const;