| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- //##########################################################################
- //# #
- //# CLOUDCOMPARE #
- //# #
- //# 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: EDF R&D / TELECOM ParisTech (ENST-TSI) #
- //# #
- //##########################################################################
- #include "ccAlignDlg.h"
- #include "ui_alignDlg.h"
- #include "mainwindow.h"
- //common
- #include <ccQtHelpers.h>
- //CCCoreLib
- #include <CloudSamplingTools.h>
- #include <GeometricalAnalysisTools.h>
- //qCC_db
- #include <ccGenericPointCloud.h>
- #include <ccProgressDialog.h>
- ccAlignDlg::ccAlignDlg(ccGenericPointCloud* data, ccGenericPointCloud* model, QWidget* parent)
- : QDialog(parent, Qt::Tool)
- , m_ui( new Ui::AlignDialog )
- {
- m_ui->setupUi(this);
- m_ui->samplingMethod->addItem( tr( "None" ) );
- m_ui->samplingMethod->addItem( tr( "Random" ) );
- m_ui->samplingMethod->addItem( tr( "Space" ) );
- m_ui->samplingMethod->addItem( tr( "Octree" ) );
- m_ui->samplingMethod->setCurrentIndex(NONE);
- ccQtHelpers::SetButtonColor(m_ui->dataColorButton, Qt::red);
- ccQtHelpers::SetButtonColor(m_ui->modelColorButton, Qt::yellow);
- dataObject = data;
- modelObject = model;
- setColorsAndLabels();
- changeSamplingMethod(m_ui->samplingMethod->currentIndex());
- toggleNbMaxCandidates(m_ui->isNbCandLimited->isChecked());
- connect(m_ui->swapButton, &QPushButton::clicked, this, &ccAlignDlg::swapModelAndData);
- connect(m_ui->modelSample, &QSlider::sliderReleased, this, &ccAlignDlg::modelSliderReleased);
- connect(m_ui->dataSample, &QSlider::sliderReleased, this, &ccAlignDlg::dataSliderReleased);
- connect(m_ui->modelSamplingRate, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccAlignDlg::modelSamplingRateChanged);
- connect(m_ui->dataSamplingRate, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccAlignDlg::dataSamplingRateChanged);
- connect(m_ui->deltaEstimation, &QPushButton::clicked, this, &ccAlignDlg::estimateDelta);
- connect(m_ui->samplingMethod, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccAlignDlg::changeSamplingMethod);
- connect(m_ui->isNbCandLimited, &QCheckBox::toggled, this, &ccAlignDlg::toggleNbMaxCandidates);
- }
- ccAlignDlg::~ccAlignDlg()
- {
- modelObject->enableTempColor(false);
- dataObject->enableTempColor(false);
-
- delete m_ui;
- }
- unsigned ccAlignDlg::getNbTries()
- {
- return m_ui->nbTries->value();
- }
- double ccAlignDlg::getOverlap()
- {
- return m_ui->overlap->value();
- }
- double ccAlignDlg::getDelta()
- {
- return m_ui->delta->value();
- }
- ccGenericPointCloud* ccAlignDlg::getModelObject()
- {
- return modelObject;
- }
- ccGenericPointCloud* ccAlignDlg::getDataObject()
- {
- return dataObject;
- }
- ccAlignDlg::CC_SAMPLING_METHOD ccAlignDlg::getSamplingMethod()
- {
- return (CC_SAMPLING_METHOD)m_ui->samplingMethod->currentIndex();
- }
- bool ccAlignDlg::isNumberOfCandidatesLimited()
- {
- return m_ui->isNbCandLimited->isChecked();
- }
- unsigned ccAlignDlg::getMaxNumberOfCandidates()
- {
- return m_ui->nbMaxCandidates->value();
- }
- CCCoreLib::ReferenceCloud* ccAlignDlg::getSampledModel()
- {
- CCCoreLib::ReferenceCloud* sampledCloud = nullptr;
- switch (getSamplingMethod())
- {
- case SPACE:
- {
- CCCoreLib::CloudSamplingTools::SFModulationParams modParams(false);
- sampledCloud = CCCoreLib::CloudSamplingTools::resampleCloudSpatially( modelObject,
- static_cast<PointCoordinateType>(m_ui->modelSamplingRate->value()),
- modParams);
- }
- break;
- case OCTREE:
- if (modelObject->getOctree())
- {
- sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudWithOctreeAtLevel( modelObject,
- static_cast<unsigned char>(m_ui->modelSamplingRate->value()),
- CCCoreLib::CloudSamplingTools::NEAREST_POINT_TO_CELL_CENTER,
- nullptr,
- modelObject->getOctree().data());
- }
- else
- {
- ccLog::Error("[ccAlignDlg::getSampledModel] Failed to get/compute model octree!");
- }
- break;
- case RANDOM:
- {
- sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudRandomly( modelObject,
- static_cast<unsigned>(m_ui->modelSamplingRate->value()));
- }
- break;
- default:
- {
- sampledCloud = new CCCoreLib::ReferenceCloud(modelObject);
- if (!sampledCloud->addPointIndex(0, modelObject->size()))
- {
- delete sampledCloud;
- sampledCloud = nullptr;
- ccLog::Error("[ccAlignDlg::getSampledModel] Not enough memory!");
- }
- }
- break;
- }
- return sampledCloud;
- }
- CCCoreLib::ReferenceCloud* ccAlignDlg::getSampledData()
- {
- CCCoreLib::ReferenceCloud* sampledCloud = nullptr;
- switch (getSamplingMethod())
- {
- case SPACE:
- {
- CCCoreLib::CloudSamplingTools::SFModulationParams modParams(false);
- sampledCloud = CCCoreLib::CloudSamplingTools::resampleCloudSpatially(dataObject,
- static_cast<PointCoordinateType>(m_ui->dataSamplingRate->value()),modParams);
- }
- break;
- case OCTREE:
- if (dataObject->getOctree())
- {
- sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudWithOctreeAtLevel( dataObject,
- static_cast<unsigned char>(m_ui->dataSamplingRate->value()),
- CCCoreLib::CloudSamplingTools::NEAREST_POINT_TO_CELL_CENTER,
- nullptr,
- dataObject->getOctree().data());
- }
- else
- {
- ccLog::Error("[ccAlignDlg::getSampledData] Failed to get/compute data octree!");
- }
- break;
- case RANDOM:
- {
- sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudRandomly(dataObject, (unsigned)(m_ui->dataSamplingRate->value()));
- }
- break;
- default:
- {
- sampledCloud = new CCCoreLib::ReferenceCloud(dataObject);
- if (!sampledCloud->addPointIndex(0,dataObject->size()))
- {
- delete sampledCloud;
- sampledCloud = nullptr;
- ccLog::Error("[ccAlignDlg::getSampledData] Not enough memory!");
- }
- }
- break;
- }
- return sampledCloud;
- }
- void ccAlignDlg::setColorsAndLabels()
- {
- if (!modelObject || !dataObject)
- return;
- m_ui->modelCloud->setText(modelObject->getName());
- modelObject->setVisible(true);
- modelObject->setTempColor(ccColor::red);
- modelObject->prepareDisplayForRefresh_recursive();
- m_ui->dataCloud->setText(dataObject->getName());
- dataObject->setVisible(true);
- dataObject->setTempColor(ccColor::yellow);
- dataObject->prepareDisplayForRefresh_recursive();
- MainWindow::RefreshAllGLWindow(false);
- }
- void ccAlignDlg::swapModelAndData()
- {
- std::swap(dataObject,modelObject);
- setColorsAndLabels();
- changeSamplingMethod(m_ui->samplingMethod->currentIndex());
- }
- void ccAlignDlg::modelSliderReleased()
- {
- double rate = static_cast<double>(m_ui->modelSample->sliderPosition()) / m_ui->modelSample->maximum();
- if (getSamplingMethod() == SPACE)
- rate = 1.0 - rate;
- rate *= m_ui->modelSamplingRate->maximum();
- m_ui->modelSamplingRate->setValue(rate);
- modelSamplingRateChanged(rate);
- }
- void ccAlignDlg::dataSliderReleased()
- {
- double rate = static_cast<double>(m_ui->dataSample->sliderPosition()) / m_ui->dataSample->maximum();
- if (getSamplingMethod() == SPACE)
- rate = 1.0 - rate;
- rate *= m_ui->dataSamplingRate->maximum();
- m_ui->dataSamplingRate->setValue(rate);
- dataSamplingRateChanged(rate);
- }
- void ccAlignDlg::modelSamplingRateChanged(double value)
- {
- QString message("An error occurred");
- CC_SAMPLING_METHOD method = getSamplingMethod();
- float rate = static_cast<float>(m_ui->modelSamplingRate->value()) / m_ui->modelSamplingRate->maximum();
- if (method == SPACE)
- rate = 1.0f - rate;
- m_ui->modelSample->setSliderPosition(static_cast<int>(rate * m_ui->modelSample->maximum()));
- switch (method)
- {
- case SPACE:
- {
- CCCoreLib::ReferenceCloud* tmpCloud = getSampledModel(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
- if (tmpCloud)
- {
- message = QString("distance units (%1 remaining points)").arg(tmpCloud->size());
- delete tmpCloud;
- }
- }
- break;
- case RANDOM:
- {
- message = QString("remaining points (%1%)").arg(rate*100.0f,0,'f',1);
- }
- break;
- case OCTREE:
- {
- CCCoreLib::ReferenceCloud* tmpCloud = getSampledModel(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
- if (tmpCloud)
- {
- message = QString("%1 remaining points").arg(tmpCloud->size());
- delete tmpCloud;
- }
- }
- break;
- default:
- {
- unsigned remaining = static_cast<unsigned>(rate * modelObject->size());
- message = QString("%1 remaining points").arg(remaining);
- }
- break;
- }
-
- m_ui->modelRemaining->setText(message);
- }
- void ccAlignDlg::dataSamplingRateChanged(double value)
- {
- QString message("An error occurred");
- CC_SAMPLING_METHOD method = getSamplingMethod();
- double rate = static_cast<float>(m_ui->dataSamplingRate->value() / m_ui->dataSamplingRate->maximum());
- if (method == SPACE)
- rate = 1.0 - rate;
- m_ui->dataSample->setSliderPosition(static_cast<int>(rate * m_ui->dataSample->maximum()));
- switch (method)
- {
- case SPACE:
- {
- CCCoreLib::ReferenceCloud* tmpCloud = getSampledData(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
- if (tmpCloud)
- {
- message = QString("distance units (%1 remaining points)").arg(tmpCloud->size());
- delete tmpCloud;
- }
- }
- break;
- case RANDOM:
- {
- message = QString("remaining points (%1%)").arg(rate*100.0, 0, 'f', 1);
- }
- break;
- case OCTREE:
- {
- CCCoreLib::ReferenceCloud* tmpCloud = getSampledData(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
- if (tmpCloud)
- {
- message = QString("%1 remaining points").arg(tmpCloud->size());
- delete tmpCloud;
- }
- }
- break;
- default:
- {
- unsigned remaining = static_cast<unsigned>(rate * dataObject->size());
- message = QString("%1 remaining points").arg(remaining);
- }
- break;
- }
-
- m_ui->dataRemaining->setText(message);
- }
- void ccAlignDlg::estimateDelta()
- {
- ccProgressDialog pDlg(false, this);
- CCCoreLib::ReferenceCloud* sampledData = getSampledData();
- //we have to work on a copy of the cloud in order to prevent the algorithms from modifying the original cloud.
- CCCoreLib::PointCloud cloud;
- {
- if (!cloud.reserve(sampledData->size()))
- {
- ccLog::Error("Not enough memory");
- return;
- }
- for (unsigned i = 0; i < sampledData->size(); i++)
- {
- cloud.addPoint(*sampledData->getPoint(i));
- }
- if (!cloud.enableScalarField())
- {
- ccLog::Error("Not enough memory");
- return;
- }
- }
- if (CCCoreLib::GeometricalAnalysisTools::ComputeLocalDensityApprox(&cloud, CCCoreLib::GeometricalAnalysisTools::DENSITY_KNN, &pDlg) != CCCoreLib::GeometricalAnalysisTools::NoError)
- {
- ccLog::Error("Failed to compute approx. density");
- return;
- }
- unsigned count = 0;
- double meanDensity = 0;
- double meanSqrDensity = 0;
- for (unsigned i = 0; i < cloud.size(); i++)
- {
- ScalarType value = cloud.getPointScalarValue(i);
- if (value == value)
- {
- meanDensity += value;
- meanSqrDensity += static_cast<double>(value)*value;
- count++;
- }
- }
- if (count)
- {
- meanDensity /= count;
- meanSqrDensity /= count;
- }
- double dev = meanSqrDensity - (meanDensity*meanDensity);
- m_ui->delta->setValue(meanDensity + dev);
- delete sampledData;
- }
- void ccAlignDlg::changeSamplingMethod(int index)
- {
- //Reste a changer les textes d'aide
- switch (index)
- {
- case SPACE:
- {
- //model
- {
- m_ui->modelSamplingRate->setDecimals(4);
- int oldSliderPos = m_ui->modelSample->sliderPosition();
- CCVector3 bbMin;
- CCVector3 bbMax;
- modelObject->getBoundingBox(bbMin, bbMax);
- double dist = (bbMin-bbMax).norm();
- m_ui->modelSamplingRate->setMaximum(dist);
- m_ui->modelSample->setSliderPosition(oldSliderPos);
- m_ui->modelSamplingRate->setSingleStep(0.01);
- m_ui->modelSamplingRate->setMinimum(0.);
- }
- //data
- {
- m_ui->dataSamplingRate->setDecimals(4);
- int oldSliderPos = m_ui->dataSample->sliderPosition();
- CCVector3 bbMin;
- CCVector3 bbMax;
- dataObject->getBoundingBox(bbMin, bbMax);
- double dist = (bbMin-bbMax).norm();
- m_ui->dataSamplingRate->setMaximum(dist);
- m_ui->dataSample->setSliderPosition(oldSliderPos);
- m_ui->dataSamplingRate->setSingleStep(0.01);
- m_ui->dataSamplingRate->setMinimum(0.);
- }
- }
- break;
- case RANDOM:
- {
- //model
- {
- m_ui->modelSamplingRate->setDecimals(0);
- m_ui->modelSamplingRate->setMaximum(static_cast<float>(modelObject->size()));
- m_ui->modelSamplingRate->setSingleStep(1.);
- m_ui->modelSamplingRate->setMinimum(0.);
- }
- //data
- {
- m_ui->dataSamplingRate->setDecimals(0);
- m_ui->dataSamplingRate->setMaximum(static_cast<float>(dataObject->size()));
- m_ui->dataSamplingRate->setSingleStep(1.);
- m_ui->dataSamplingRate->setMinimum(0.);
- }
- }
- break;
- case OCTREE:
- {
- //model
- {
- if (!modelObject->getOctree())
- modelObject->computeOctree();
- m_ui->modelSamplingRate->setDecimals(0);
- m_ui->modelSamplingRate->setMaximum(static_cast<double>(CCCoreLib::DgmOctree::MAX_OCTREE_LEVEL));
- m_ui->modelSamplingRate->setMinimum(1.);
- m_ui->modelSamplingRate->setSingleStep(1.);
- }
- //data
- {
- if (!dataObject->getOctree())
- dataObject->computeOctree();
- m_ui->dataSamplingRate->setDecimals(0);
- m_ui->dataSamplingRate->setMaximum(static_cast<double>(CCCoreLib::DgmOctree::MAX_OCTREE_LEVEL));
- m_ui->dataSamplingRate->setMinimum(1.);
- m_ui->dataSamplingRate->setSingleStep(1.);
- }
- }
- break;
- default:
- {
- //model
- {
- m_ui->modelSamplingRate->setDecimals(2);
- m_ui->modelSamplingRate->setMaximum(100.);
- m_ui->modelSamplingRate->setSingleStep(0.01);
- m_ui->modelSamplingRate->setMinimum(0.);
- }
- //data
- {
- m_ui->dataSamplingRate->setDecimals(2);
- m_ui->dataSamplingRate->setMaximum(100.);
- m_ui->dataSamplingRate->setSingleStep(0.01);
- m_ui->dataSamplingRate->setMinimum(0.);
- }
- }
- break;
- }
- if (index == NONE)
- {
- //model
- m_ui->modelSample->setSliderPosition(m_ui->modelSample->maximum());
- m_ui->modelSample->setEnabled(false);
- m_ui->modelSamplingRate->setEnabled(false);
- //data
- m_ui->dataSample->setSliderPosition(m_ui->dataSample->maximum());
- m_ui->dataSample->setEnabled(false);
- m_ui->dataSamplingRate->setEnabled(false);
- }
- else
- {
- //model
- m_ui->modelSample->setEnabled(true);
- m_ui->modelSamplingRate->setEnabled(true);
- //data
- m_ui->dataSample->setEnabled(true);
- m_ui->dataSamplingRate->setEnabled(true);
- }
- modelSliderReleased();
- dataSliderReleased();
- }
- void ccAlignDlg::toggleNbMaxCandidates(bool activ)
- {
- m_ui->nbMaxCandidates->setEnabled(activ);
- }
|