18 #include <QFileDialog>
20 #include <QMainWindow>
21 #include <QMessageBox>
34 if (
name.isEmpty())
name =
"unnamed";
41 assert(comboBox && dbRoot);
42 if (!comboBox || !dbRoot) {
48 int index = comboBox->currentIndex();
53 assert(comboBox->itemData(index).isValid());
54 unsigned uniqueID = comboBox->itemData(index).toUInt();
68 : QDialog(app ? app->getMainWindow() : 0),
73 m_corePointsCloud(nullptr) {
76 int maxThreadCount = QThread::idealThreadCount();
77 maxThreadCountSpinBox->setRange(1, maxThreadCount);
78 maxThreadCountSpinBox->setSuffix(QString(
" / %1").arg(maxThreadCount));
80 connect(showCloud1CheckBox, SIGNAL(toggled(
bool)),
this,
82 connect(showCloud2CheckBox, SIGNAL(toggled(
bool)),
this,
85 connect(loadParamsToolButton, SIGNAL(clicked()),
this,
87 connect(saveParamsToolButton, SIGNAL(clicked()),
this,
89 connect(swapCloudsToolButton, SIGNAL(clicked()),
this, SLOT(
swapClouds()));
90 connect(guessParamsPushButton, SIGNAL(clicked()),
this,
93 connect(projDestComboBox, SIGNAL(currentIndexChanged(
int)),
this,
96 connect(cpOtherCloudComboBox, SIGNAL(currentIndexChanged(
int)),
this,
98 connect(normalSourceComboBox, SIGNAL(currentIndexChanged(
int)),
this,
100 connect(cpUseCloud1RadioButton, SIGNAL(toggled(
bool)),
this,
102 connect(cpSubsampleRadioButton, SIGNAL(toggled(
bool)),
this,
104 connect(cpUseOtherCloudRadioButton, SIGNAL(toggled(
bool)),
this,
119 for (
size_t i = 0; i < clouds.size(); ++i) {
123 cpOtherCloudComboBox->addItem(
125 QVariant(clouds[i]->getUniqueID()));
126 normOriCloudComboBox->addItem(
128 QVariant(clouds[i]->getUniqueID()));
136 int defaultFieldIndex = -1,
137 QString defaultField = QString()) {
139 if (!combo || sfCount == 0) {
145 int selectedFieldIndex = -1;
146 bool defaultFieldFound =
false;
147 for (
unsigned i = 0; i < sfCount; ++i) {
149 combo->addItem(sfName);
150 if (selectedFieldIndex < 0 && !defaultField.isEmpty()) {
152 selectedFieldIndex =
static_cast<int>(i);
153 defaultFieldFound =
true;
158 if (selectedFieldIndex < 0) {
159 selectedFieldIndex = defaultFieldIndex;
161 combo->setCurrentIndex(selectedFieldIndex);
163 return defaultFieldFound;
170 assert(sx && sy && sz);
177 bool sxFound =
PopulateSFCombo(sx, cloud, std::min<int>(sfCount, 0),
"sx");
178 bool syFound =
PopulateSFCombo(sy, cloud, std::min<int>(sfCount, 1),
"sy");
179 bool szFound =
PopulateSFCombo(sz, cloud, std::min<int>(sfCount, 2),
"sz");
181 return sxFound && syFound && szFound;
185 precisionMapsGroupBox->setEnabled(
false);
193 bool wasChecked = precisionMapsGroupBox->isChecked();
198 precisionMapsGroupBox->setChecked(wasChecked && (auto1 && auto2));
199 precisionMapsGroupBox->setEnabled(
true);
209 if (!cloud1 || !cloud2) {
219 showCloud1CheckBox->blockSignals(
true);
220 showCloud1CheckBox->setChecked(cloud1->
isVisible());
221 showCloud1CheckBox->blockSignals(
false);
225 showCloud2CheckBox->blockSignals(
true);
226 showCloud2CheckBox->setChecked(cloud2->
isVisible());
227 showCloud2CheckBox->blockSignals(
false);
239 int selectedItem = normalSourceComboBox->currentIndex() >= 0
240 ? normalSourceComboBox->currentData().toInt()
242 switch (selectedItem) {
245 normParamsFrame->setEnabled(
false);
246 normalScaleDoubleSpinBox->setEnabled(
false);
249 normParamsFrame->setEnabled(
true);
250 normalScaleDoubleSpinBox->setEnabled(
true);
256 int previouslySelectedItem =
257 normalSourceComboBox->currentIndex() >= 0
258 ? normalSourceComboBox->currentData().toInt()
261 normalSourceComboBox->clear();
262 normalSourceComboBox->addItem(
"Compute normals (on core points)",
267 normalSourceComboBox->setCurrentIndex(lastIndex);
271 normalSourceComboBox->addItem(
272 "Use cloud #1 normals",
276 previouslySelectedItem < 0) {
277 normalSourceComboBox->setCurrentIndex(lastIndex);
282 if (cpUseOtherCloudRadioButton->isChecked()) {
287 normalSourceComboBox->addItem(
288 "Use core points normals",
291 if (previouslySelectedItem ==
293 previouslySelectedItem < 0) {
294 normalSourceComboBox->setCurrentIndex(lastIndex);
304 }
else if (cpUseCloud1RadioButton->isChecked()) {
306 }
else if (cpUseOtherCloudRadioButton->isChecked()) {
315 if (normOriUseCloudRadioButton->isChecked()) {
347 if (normalSourceComboBox->currentIndex() >= 0) {
348 int selectedItem = normalSourceComboBox->currentData().toInt();
358 if (normMultiScaleRadioButton->isChecked()) {
360 }
else if (normVertRadioButton->isChecked()) {
362 }
else if (normHorizRadioButton->isChecked()) {
371 switch (projDestComboBox->currentIndex()) {
392 return useOriginalCloudCheckBox->isEnabled() &&
393 useOriginalCloudCheckBox->isChecked();
397 return maxThreadCountSpinBox->value();
401 return useMinPoints4StatCheckBox->isChecked()
402 ?
static_cast<unsigned>(
403 std::max(0, minPoints4StatSpinBox->value()))
408 QSettings settings(
"qM3C2");
415 settings.value(
"NormalScale", normalScaleDoubleSpinBox->value())
418 settings.value(
"NormalMode",
421 double normMinScale =
422 settings.value(
"NormalMinScale", minScaleDoubleSpinBox->value())
425 settings.value(
"NormalStep", stepScaleDoubleSpinBox->value())
427 double normMaxScale =
428 settings.value(
"NormalMaxScale", maxScaleDoubleSpinBox->value())
430 bool normUseCorePoints =
431 settings.value(
"NormalUseCorePoints",
432 normUseCorePointsCheckBox->isChecked())
434 int normPreferredOri =
435 settings.value(
"NormalPreferedOri",
436 normOriPreferredComboBox->currentIndex())
440 settings.value(
"SearchScale", cylDiameterDoubleSpinBox->value())
443 settings.value(
"SearchDepth", cylHalfHeightDoubleSpinBox->value())
446 double subsampleRadius = settings.value(
"SubsampleRadius",
447 cpSubsamplingDoubleSpinBox->value())
449 bool subsampleEnabled = settings.value(
"SubsampleEnabled",
450 cpSubsampleRadioButton->isChecked())
453 double registrationError =
454 settings.value(
"RegistrationError", rmsDoubleSpinBox->value())
456 bool registrationErrorEnabled =
457 settings.value(
"RegistrationErrorEnabled", rmsCheckBox->isChecked())
460 bool useSinglePass4Depth =
461 settings.value(
"UseSinglePass4Depth",
462 useSinglePass4DepthCheckBox->isChecked())
464 bool positiveSearchOnly =
465 settings.value(
"PositiveSearchOnly",
466 positiveSearchOnlyCheckBox->isChecked())
468 bool useMedian = settings.value(
"UseMedian", useMedianCheckBox->isChecked())
471 bool useMinPoints4Stat =
472 settings.value(
"UseMinPoints4Stat",
473 useMinPoints4StatCheckBox->isChecked())
476 settings.value(
"MinPoints4Stat", minPoints4StatSpinBox->value())
480 settings.value(
"ProjDestIndex", projDestComboBox->currentIndex())
482 bool useOriginalCloud =
483 settings.value(
"UseOriginalCloud",
484 useOriginalCloudCheckBox->isChecked())
487 bool exportStdDevInfo =
488 settings.value(
"ExportStdDevInfo",
489 exportStdDevInfoCheckBox->isChecked())
491 bool exportDensityAtProjScale =
492 settings.value(
"ExportDensityAtProjScale",
493 exportDensityAtProjScaleCheckBox->isChecked())
497 settings.value(
"MaxThreadCount", maxThreadCountSpinBox->maximum())
500 bool usePrecisionMaps = settings.value(
"UsePrecisionMaps",
501 precisionMapsGroupBox->isChecked())
503 double pm1Scale = settings.value(
"PM1Scale", pm1ScaleDoubleSpinBox->value())
505 double pm2Scale = settings.value(
"PM2Scale", pm2ScaleDoubleSpinBox->value())
510 switch (normModeInt) {
514 for (
int i = 0; i < normalSourceComboBox->count(); ++i) {
515 if (normalSourceComboBox->itemData(i) == normModeInt) {
516 normalSourceComboBox->setCurrentIndex(i);
523 "Can't restore the previous normal computation method "
524 "(cloud #1 or core points has no normals)");
529 normDefaultRadioButton->setChecked(
true);
533 normMultiScaleRadioButton->setChecked(
true);
537 normVertRadioButton->setChecked(
true);
541 normHorizRadioButton->setChecked(
true);
549 minScaleDoubleSpinBox->setValue(normMinScale);
550 stepScaleDoubleSpinBox->setValue(normStep);
551 maxScaleDoubleSpinBox->setValue(normMaxScale);
552 normUseCorePointsCheckBox->setChecked(normUseCorePoints);
553 normOriPreferredComboBox->setCurrentIndex(normPreferredOri);
555 cylDiameterDoubleSpinBox->setValue(seachScale);
556 cylHalfHeightDoubleSpinBox->setValue(searchDepth);
558 cpSubsamplingDoubleSpinBox->setValue(subsampleRadius);
559 cpSubsampleRadioButton->setChecked(subsampleEnabled);
561 rmsCheckBox->setChecked(registrationErrorEnabled);
562 rmsDoubleSpinBox->setValue(registrationError);
564 useSinglePass4DepthCheckBox->setChecked(useSinglePass4Depth);
565 positiveSearchOnlyCheckBox->setChecked(positiveSearchOnly);
566 useMedianCheckBox->setChecked(useMedian);
568 useMinPoints4StatCheckBox->setChecked(useMinPoints4Stat);
569 minPoints4StatSpinBox->setValue(minPoints4Stat);
571 projDestComboBox->setCurrentIndex(projDestIndex);
572 useOriginalCloudCheckBox->setChecked(useOriginalCloud);
574 exportStdDevInfoCheckBox->setChecked(exportStdDevInfo);
575 exportDensityAtProjScaleCheckBox->setChecked(exportDensityAtProjScale);
577 maxThreadCountSpinBox->setValue(maxThreadCount);
579 precisionMapsGroupBox->setChecked(usePrecisionMaps);
580 pm1ScaleDoubleSpinBox->setValue(pm1Scale);
581 pm2ScaleDoubleSpinBox->setValue(pm2Scale);
585 QSettings settings(
"qM3C2");
591 settings.setValue(
"NormalScale", normalScaleDoubleSpinBox->value());
592 settings.setValue(
"NormalMode",
594 settings.setValue(
"NormalMinScale", minScaleDoubleSpinBox->value());
595 settings.setValue(
"NormalStep", stepScaleDoubleSpinBox->value());
596 settings.setValue(
"NormalMaxScale", maxScaleDoubleSpinBox->value());
597 settings.setValue(
"NormalUseCorePoints",
598 normUseCorePointsCheckBox->isChecked());
599 settings.setValue(
"NormalPreferedOri",
600 normOriPreferredComboBox->currentIndex());
602 settings.setValue(
"SearchScale", cylDiameterDoubleSpinBox->value());
603 settings.setValue(
"SearchDepth", cylHalfHeightDoubleSpinBox->value());
605 settings.setValue(
"SubsampleRadius", cpSubsamplingDoubleSpinBox->value());
606 settings.setValue(
"SubsampleEnabled", cpSubsampleRadioButton->isChecked());
608 settings.setValue(
"RegistrationError", rmsDoubleSpinBox->value());
609 settings.setValue(
"RegistrationErrorEnabled", rmsCheckBox->isChecked());
611 settings.setValue(
"UseSinglePass4Depth",
612 useSinglePass4DepthCheckBox->isChecked());
613 settings.setValue(
"PositiveSearchOnly",
614 positiveSearchOnlyCheckBox->isChecked());
615 settings.setValue(
"UseMedian", useMedianCheckBox->isChecked());
617 settings.setValue(
"UseMinPoints4Stat",
618 useMinPoints4StatCheckBox->isChecked());
619 settings.setValue(
"MinPoints4Stat", minPoints4StatSpinBox->value());
621 settings.setValue(
"ProjDestIndex", projDestComboBox->currentIndex());
622 settings.setValue(
"UseOriginalCloud",
623 useOriginalCloudCheckBox->isChecked());
625 settings.setValue(
"ExportStdDevInfo",
626 exportStdDevInfoCheckBox->isChecked());
627 settings.setValue(
"ExportDensityAtProjScale",
628 exportDensityAtProjScaleCheckBox->isChecked());
630 settings.setValue(
"MaxThreadCount", maxThreadCountSpinBox->value());
632 settings.setValue(
"UsePrecisionMaps", precisionMapsGroupBox->isChecked());
633 settings.setValue(
"PM1Scale", pm1ScaleDoubleSpinBox->value());
634 settings.setValue(
"PM2Scale", pm2ScaleDoubleSpinBox->value());
641 QSettings settings(
"qM3C2");
642 QString currentPath =
646 filename = QFileDialog::getOpenFileName(
this,
"Load M3C2 parameters",
647 currentPath,
"*.txt");
651 currentPath = QFileInfo(
filename).absolutePath();
652 settings.setValue(
"currentPath", currentPath);
659 QSettings fileSettings(
filename, QSettings::IniFormat);
661 if (!fileSettings.contains(
"M3C2VER")) {
662 QMessageBox::critical(
this,
"Invalid file",
663 "File doesn't seem to be a valid M3C2 parameters "
664 "file ('M3C2VER' not found)!");
677 QSettings settings(
"qM3C2");
678 QString currentPath =
682 filename = QFileDialog::getSaveFileName(
683 this,
"Save M3C2 parameters",
684 currentPath + QString(
"/m3c2_params.txt"),
"*.txt");
688 currentPath = QFileInfo(
filename).absolutePath();
689 settings.setValue(
"currentPath", currentPath);
694 QSettings fileSettings(
filename, QSettings::IniFormat);
696 fileSettings.setValue(
"M3C2VER", QVariant::fromValue<int>(1));
710 normalScaleDoubleSpinBox->setValue(
params.normScale);
711 cylDiameterDoubleSpinBox->setValue(
params.projScale);
712 cylHalfHeightDoubleSpinBox->setValue(
params.projDepth);
713 if (
params.preferredDimension >= 0 &&
params.preferredDimension < 3) {
716 normOriPreferredComboBox->setCurrentIndex(
717 params.preferredDimension * 2);
720 minScaleDoubleSpinBox->setValue(
params.normScale / 2);
721 stepScaleDoubleSpinBox->setValue(
params.normScale / 2);
722 maxScaleDoubleSpinBox->setValue(
params.normScale * 2);
724 cpSubsamplingDoubleSpinBox->setValue(
params.projScale / 2);
cmdLineReadable * params[]
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
virtual bool isVisible() const
Returns whether entity is visible or not.
virtual void setVisible(bool state)
Sets entity visibility.
Hierarchical CLOUDVIEWER Object.
ccHObject * find(unsigned uniqueID)
Finds an entity in this object hierarchy.
unsigned filterChildren(Container &filteredChildren, bool recursive=false, CV_CLASS_ENUM filter=CV_TYPES::OBJECT, bool strict=false) const
Collects the children corresponding to a certain pattern.
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
virtual QString getName() const
Returns object name.
virtual unsigned getUniqueID() const
Returns object unique ID.
bool isA(CV_CLASS_ENUM type) const
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool hasScalarFields() const override
Returns whether one or more scalar fields are instantiated.
unsigned getNumberOfScalarFields() const
Returns the number of associated (and active) scalar fields.
const char * getScalarFieldName(int index) const
Returns the name of a specific scalar field.
void setValue(std::size_t index, ScalarType value)
Main application interface (for plugins)
virtual void updateUI()=0
virtual ccHObject * dbRootObject()=0
Returns DB root (as a ccHObject)
virtual void refreshAll(bool only2D=false, bool forceRedraw=true)=0
Redraws all GL windows that have the 'refresh' flag on.
void guessParams(bool fastMode)
Guess parameters from the cloud #1.
qM3C2Normals::ComputationMode getNormalsComputationMode() const
Returns selected normals computation mode.
void onUpdateNormalComboBoxChanged(int)
ecvMainAppInterface * m_app
void setCloud1Visibility(bool)
void loadParamsFromFile()
unsigned getMinPointsForStats(unsigned defaultValue=5) const
ExportOptions
Exportation options.
void loadParamsFromPersistentSettings()
Loads parameters from persistent settings.
ccPointCloud * getNormalsOrientationCloud() const
Returns the cloud to be used for normals orientation (if any)
void updateNormalComboBox()
Updates the normalSourceComboBox.
ccPointCloud * m_corePointsCloud
qM3C2Dialog(ccPointCloud *cloud1, ccPointCloud *cloud2, ecvMainAppInterface *app)
Default constructor.
void loadParamsFrom(const QSettings &settings)
Load parameters from QSettings.
ExportOptions getExportOption() const
Returns selected export option.
void setupPrecisionMapsTab()
Setups the precision maps tab.
void projDestIndexChanged(int)
void setCloud2Visibility(bool)
ccPointCloud * getCorePointsCloud() const
Get core points cloud (if any)
int getMaxThreadCount() const
Returns the max number of threads to use.
void setClouds(ccPointCloud *cloud1, ccPointCloud *cloud2)
Sets clouds.
bool keepOriginalCloud() const
void saveParamsTo(QSettings &settings)
Saves parameters to QSettings.
void saveParamsToPersistentSettings()
Saves parameters to persistent settings.
ComputationMode
Normals computation mode.
@ USE_CORE_POINTS_NORMALS
constexpr QRegularExpression::PatternOption CaseInsensitive
QString defaultDocPath()
Shortcut for getting the documents location path.
static bool s_firstTimeInit
bool PopulatePMFields(QComboBox *sx, QComboBox *sy, QComboBox *sz, const ccPointCloud &cloud)
static QString GetEntityName(ccHObject *obj)
static ccPointCloud * GetCloudFromCombo(QComboBox *comboBox, ccHObject *dbRoot)
bool PopulateSFCombo(QComboBox *combo, const ccPointCloud &cloud, int defaultFieldIndex=-1, QString defaultField=QString())