ccAlignDlg.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. //##########################################################################
  2. //# #
  3. //# CLOUDCOMPARE #
  4. //# #
  5. //# This program is free software; you can redistribute it and/or modify #
  6. //# it under the terms of the GNU General Public License as published by #
  7. //# the Free Software Foundation; version 2 or later of the License. #
  8. //# #
  9. //# This program is distributed in the hope that it will be useful, #
  10. //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
  11. //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
  12. //# GNU General Public License for more details. #
  13. //# #
  14. //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
  15. //# #
  16. //##########################################################################
  17. #include "ccAlignDlg.h"
  18. #include "ui_alignDlg.h"
  19. #include "mainwindow.h"
  20. //common
  21. #include <ccQtHelpers.h>
  22. //CCCoreLib
  23. #include <CloudSamplingTools.h>
  24. #include <GeometricalAnalysisTools.h>
  25. //qCC_db
  26. #include <ccGenericPointCloud.h>
  27. #include <ccProgressDialog.h>
  28. ccAlignDlg::ccAlignDlg(ccGenericPointCloud* data, ccGenericPointCloud* model, QWidget* parent)
  29. : QDialog(parent, Qt::Tool)
  30. , m_ui( new Ui::AlignDialog )
  31. {
  32. m_ui->setupUi(this);
  33. m_ui->samplingMethod->addItem( tr( "None" ) );
  34. m_ui->samplingMethod->addItem( tr( "Random" ) );
  35. m_ui->samplingMethod->addItem( tr( "Space" ) );
  36. m_ui->samplingMethod->addItem( tr( "Octree" ) );
  37. m_ui->samplingMethod->setCurrentIndex(NONE);
  38. ccQtHelpers::SetButtonColor(m_ui->dataColorButton, Qt::red);
  39. ccQtHelpers::SetButtonColor(m_ui->modelColorButton, Qt::yellow);
  40. dataObject = data;
  41. modelObject = model;
  42. setColorsAndLabels();
  43. changeSamplingMethod(m_ui->samplingMethod->currentIndex());
  44. toggleNbMaxCandidates(m_ui->isNbCandLimited->isChecked());
  45. connect(m_ui->swapButton, &QPushButton::clicked, this, &ccAlignDlg::swapModelAndData);
  46. connect(m_ui->modelSample, &QSlider::sliderReleased, this, &ccAlignDlg::modelSliderReleased);
  47. connect(m_ui->dataSample, &QSlider::sliderReleased, this, &ccAlignDlg::dataSliderReleased);
  48. connect(m_ui->modelSamplingRate, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccAlignDlg::modelSamplingRateChanged);
  49. connect(m_ui->dataSamplingRate, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &ccAlignDlg::dataSamplingRateChanged);
  50. connect(m_ui->deltaEstimation, &QPushButton::clicked, this, &ccAlignDlg::estimateDelta);
  51. connect(m_ui->samplingMethod, qOverload<int>(&QComboBox::currentIndexChanged), this, &ccAlignDlg::changeSamplingMethod);
  52. connect(m_ui->isNbCandLimited, &QCheckBox::toggled, this, &ccAlignDlg::toggleNbMaxCandidates);
  53. }
  54. ccAlignDlg::~ccAlignDlg()
  55. {
  56. modelObject->enableTempColor(false);
  57. dataObject->enableTempColor(false);
  58. delete m_ui;
  59. }
  60. unsigned ccAlignDlg::getNbTries()
  61. {
  62. return m_ui->nbTries->value();
  63. }
  64. double ccAlignDlg::getOverlap()
  65. {
  66. return m_ui->overlap->value();
  67. }
  68. double ccAlignDlg::getDelta()
  69. {
  70. return m_ui->delta->value();
  71. }
  72. ccGenericPointCloud* ccAlignDlg::getModelObject()
  73. {
  74. return modelObject;
  75. }
  76. ccGenericPointCloud* ccAlignDlg::getDataObject()
  77. {
  78. return dataObject;
  79. }
  80. ccAlignDlg::CC_SAMPLING_METHOD ccAlignDlg::getSamplingMethod()
  81. {
  82. return (CC_SAMPLING_METHOD)m_ui->samplingMethod->currentIndex();
  83. }
  84. bool ccAlignDlg::isNumberOfCandidatesLimited()
  85. {
  86. return m_ui->isNbCandLimited->isChecked();
  87. }
  88. unsigned ccAlignDlg::getMaxNumberOfCandidates()
  89. {
  90. return m_ui->nbMaxCandidates->value();
  91. }
  92. CCCoreLib::ReferenceCloud* ccAlignDlg::getSampledModel()
  93. {
  94. CCCoreLib::ReferenceCloud* sampledCloud = nullptr;
  95. switch (getSamplingMethod())
  96. {
  97. case SPACE:
  98. {
  99. CCCoreLib::CloudSamplingTools::SFModulationParams modParams(false);
  100. sampledCloud = CCCoreLib::CloudSamplingTools::resampleCloudSpatially( modelObject,
  101. static_cast<PointCoordinateType>(m_ui->modelSamplingRate->value()),
  102. modParams);
  103. }
  104. break;
  105. case OCTREE:
  106. if (modelObject->getOctree())
  107. {
  108. sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudWithOctreeAtLevel( modelObject,
  109. static_cast<unsigned char>(m_ui->modelSamplingRate->value()),
  110. CCCoreLib::CloudSamplingTools::NEAREST_POINT_TO_CELL_CENTER,
  111. nullptr,
  112. modelObject->getOctree().data());
  113. }
  114. else
  115. {
  116. ccLog::Error("[ccAlignDlg::getSampledModel] Failed to get/compute model octree!");
  117. }
  118. break;
  119. case RANDOM:
  120. {
  121. sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudRandomly( modelObject,
  122. static_cast<unsigned>(m_ui->modelSamplingRate->value()));
  123. }
  124. break;
  125. default:
  126. {
  127. sampledCloud = new CCCoreLib::ReferenceCloud(modelObject);
  128. if (!sampledCloud->addPointIndex(0, modelObject->size()))
  129. {
  130. delete sampledCloud;
  131. sampledCloud = nullptr;
  132. ccLog::Error("[ccAlignDlg::getSampledModel] Not enough memory!");
  133. }
  134. }
  135. break;
  136. }
  137. return sampledCloud;
  138. }
  139. CCCoreLib::ReferenceCloud* ccAlignDlg::getSampledData()
  140. {
  141. CCCoreLib::ReferenceCloud* sampledCloud = nullptr;
  142. switch (getSamplingMethod())
  143. {
  144. case SPACE:
  145. {
  146. CCCoreLib::CloudSamplingTools::SFModulationParams modParams(false);
  147. sampledCloud = CCCoreLib::CloudSamplingTools::resampleCloudSpatially(dataObject,
  148. static_cast<PointCoordinateType>(m_ui->dataSamplingRate->value()),modParams);
  149. }
  150. break;
  151. case OCTREE:
  152. if (dataObject->getOctree())
  153. {
  154. sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudWithOctreeAtLevel( dataObject,
  155. static_cast<unsigned char>(m_ui->dataSamplingRate->value()),
  156. CCCoreLib::CloudSamplingTools::NEAREST_POINT_TO_CELL_CENTER,
  157. nullptr,
  158. dataObject->getOctree().data());
  159. }
  160. else
  161. {
  162. ccLog::Error("[ccAlignDlg::getSampledData] Failed to get/compute data octree!");
  163. }
  164. break;
  165. case RANDOM:
  166. {
  167. sampledCloud = CCCoreLib::CloudSamplingTools::subsampleCloudRandomly(dataObject, (unsigned)(m_ui->dataSamplingRate->value()));
  168. }
  169. break;
  170. default:
  171. {
  172. sampledCloud = new CCCoreLib::ReferenceCloud(dataObject);
  173. if (!sampledCloud->addPointIndex(0,dataObject->size()))
  174. {
  175. delete sampledCloud;
  176. sampledCloud = nullptr;
  177. ccLog::Error("[ccAlignDlg::getSampledData] Not enough memory!");
  178. }
  179. }
  180. break;
  181. }
  182. return sampledCloud;
  183. }
  184. void ccAlignDlg::setColorsAndLabels()
  185. {
  186. if (!modelObject || !dataObject)
  187. return;
  188. m_ui->modelCloud->setText(modelObject->getName());
  189. modelObject->setVisible(true);
  190. modelObject->setTempColor(ccColor::red);
  191. modelObject->prepareDisplayForRefresh_recursive();
  192. m_ui->dataCloud->setText(dataObject->getName());
  193. dataObject->setVisible(true);
  194. dataObject->setTempColor(ccColor::yellow);
  195. dataObject->prepareDisplayForRefresh_recursive();
  196. MainWindow::RefreshAllGLWindow(false);
  197. }
  198. void ccAlignDlg::swapModelAndData()
  199. {
  200. std::swap(dataObject,modelObject);
  201. setColorsAndLabels();
  202. changeSamplingMethod(m_ui->samplingMethod->currentIndex());
  203. }
  204. void ccAlignDlg::modelSliderReleased()
  205. {
  206. double rate = static_cast<double>(m_ui->modelSample->sliderPosition()) / m_ui->modelSample->maximum();
  207. if (getSamplingMethod() == SPACE)
  208. rate = 1.0 - rate;
  209. rate *= m_ui->modelSamplingRate->maximum();
  210. m_ui->modelSamplingRate->setValue(rate);
  211. modelSamplingRateChanged(rate);
  212. }
  213. void ccAlignDlg::dataSliderReleased()
  214. {
  215. double rate = static_cast<double>(m_ui->dataSample->sliderPosition()) / m_ui->dataSample->maximum();
  216. if (getSamplingMethod() == SPACE)
  217. rate = 1.0 - rate;
  218. rate *= m_ui->dataSamplingRate->maximum();
  219. m_ui->dataSamplingRate->setValue(rate);
  220. dataSamplingRateChanged(rate);
  221. }
  222. void ccAlignDlg::modelSamplingRateChanged(double value)
  223. {
  224. QString message("An error occurred");
  225. CC_SAMPLING_METHOD method = getSamplingMethod();
  226. float rate = static_cast<float>(m_ui->modelSamplingRate->value()) / m_ui->modelSamplingRate->maximum();
  227. if (method == SPACE)
  228. rate = 1.0f - rate;
  229. m_ui->modelSample->setSliderPosition(static_cast<int>(rate * m_ui->modelSample->maximum()));
  230. switch (method)
  231. {
  232. case SPACE:
  233. {
  234. CCCoreLib::ReferenceCloud* tmpCloud = getSampledModel(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
  235. if (tmpCloud)
  236. {
  237. message = QString("distance units (%1 remaining points)").arg(tmpCloud->size());
  238. delete tmpCloud;
  239. }
  240. }
  241. break;
  242. case RANDOM:
  243. {
  244. message = QString("remaining points (%1%)").arg(rate*100.0f,0,'f',1);
  245. }
  246. break;
  247. case OCTREE:
  248. {
  249. CCCoreLib::ReferenceCloud* tmpCloud = getSampledModel(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
  250. if (tmpCloud)
  251. {
  252. message = QString("%1 remaining points").arg(tmpCloud->size());
  253. delete tmpCloud;
  254. }
  255. }
  256. break;
  257. default:
  258. {
  259. unsigned remaining = static_cast<unsigned>(rate * modelObject->size());
  260. message = QString("%1 remaining points").arg(remaining);
  261. }
  262. break;
  263. }
  264. m_ui->modelRemaining->setText(message);
  265. }
  266. void ccAlignDlg::dataSamplingRateChanged(double value)
  267. {
  268. QString message("An error occurred");
  269. CC_SAMPLING_METHOD method = getSamplingMethod();
  270. double rate = static_cast<float>(m_ui->dataSamplingRate->value() / m_ui->dataSamplingRate->maximum());
  271. if (method == SPACE)
  272. rate = 1.0 - rate;
  273. m_ui->dataSample->setSliderPosition(static_cast<int>(rate * m_ui->dataSample->maximum()));
  274. switch (method)
  275. {
  276. case SPACE:
  277. {
  278. CCCoreLib::ReferenceCloud* tmpCloud = getSampledData(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
  279. if (tmpCloud)
  280. {
  281. message = QString("distance units (%1 remaining points)").arg(tmpCloud->size());
  282. delete tmpCloud;
  283. }
  284. }
  285. break;
  286. case RANDOM:
  287. {
  288. message = QString("remaining points (%1%)").arg(rate*100.0, 0, 'f', 1);
  289. }
  290. break;
  291. case OCTREE:
  292. {
  293. CCCoreLib::ReferenceCloud* tmpCloud = getSampledData(); //DGM FIXME: wow! you generate a spatially sampled cloud just to display its size?!
  294. if (tmpCloud)
  295. {
  296. message = QString("%1 remaining points").arg(tmpCloud->size());
  297. delete tmpCloud;
  298. }
  299. }
  300. break;
  301. default:
  302. {
  303. unsigned remaining = static_cast<unsigned>(rate * dataObject->size());
  304. message = QString("%1 remaining points").arg(remaining);
  305. }
  306. break;
  307. }
  308. m_ui->dataRemaining->setText(message);
  309. }
  310. void ccAlignDlg::estimateDelta()
  311. {
  312. ccProgressDialog pDlg(false, this);
  313. CCCoreLib::ReferenceCloud* sampledData = getSampledData();
  314. //we have to work on a copy of the cloud in order to prevent the algorithms from modifying the original cloud.
  315. CCCoreLib::PointCloud cloud;
  316. {
  317. if (!cloud.reserve(sampledData->size()))
  318. {
  319. ccLog::Error("Not enough memory");
  320. return;
  321. }
  322. for (unsigned i = 0; i < sampledData->size(); i++)
  323. {
  324. cloud.addPoint(*sampledData->getPoint(i));
  325. }
  326. if (!cloud.enableScalarField())
  327. {
  328. ccLog::Error("Not enough memory");
  329. return;
  330. }
  331. }
  332. if (CCCoreLib::GeometricalAnalysisTools::ComputeLocalDensityApprox(&cloud, CCCoreLib::GeometricalAnalysisTools::DENSITY_KNN, &pDlg) != CCCoreLib::GeometricalAnalysisTools::NoError)
  333. {
  334. ccLog::Error("Failed to compute approx. density");
  335. return;
  336. }
  337. unsigned count = 0;
  338. double meanDensity = 0;
  339. double meanSqrDensity = 0;
  340. for (unsigned i = 0; i < cloud.size(); i++)
  341. {
  342. ScalarType value = cloud.getPointScalarValue(i);
  343. if (value == value)
  344. {
  345. meanDensity += value;
  346. meanSqrDensity += static_cast<double>(value)*value;
  347. count++;
  348. }
  349. }
  350. if (count)
  351. {
  352. meanDensity /= count;
  353. meanSqrDensity /= count;
  354. }
  355. double dev = meanSqrDensity - (meanDensity*meanDensity);
  356. m_ui->delta->setValue(meanDensity + dev);
  357. delete sampledData;
  358. }
  359. void ccAlignDlg::changeSamplingMethod(int index)
  360. {
  361. //Reste a changer les textes d'aide
  362. switch (index)
  363. {
  364. case SPACE:
  365. {
  366. //model
  367. {
  368. m_ui->modelSamplingRate->setDecimals(4);
  369. int oldSliderPos = m_ui->modelSample->sliderPosition();
  370. CCVector3 bbMin;
  371. CCVector3 bbMax;
  372. modelObject->getBoundingBox(bbMin, bbMax);
  373. double dist = (bbMin-bbMax).norm();
  374. m_ui->modelSamplingRate->setMaximum(dist);
  375. m_ui->modelSample->setSliderPosition(oldSliderPos);
  376. m_ui->modelSamplingRate->setSingleStep(0.01);
  377. m_ui->modelSamplingRate->setMinimum(0.);
  378. }
  379. //data
  380. {
  381. m_ui->dataSamplingRate->setDecimals(4);
  382. int oldSliderPos = m_ui->dataSample->sliderPosition();
  383. CCVector3 bbMin;
  384. CCVector3 bbMax;
  385. dataObject->getBoundingBox(bbMin, bbMax);
  386. double dist = (bbMin-bbMax).norm();
  387. m_ui->dataSamplingRate->setMaximum(dist);
  388. m_ui->dataSample->setSliderPosition(oldSliderPos);
  389. m_ui->dataSamplingRate->setSingleStep(0.01);
  390. m_ui->dataSamplingRate->setMinimum(0.);
  391. }
  392. }
  393. break;
  394. case RANDOM:
  395. {
  396. //model
  397. {
  398. m_ui->modelSamplingRate->setDecimals(0);
  399. m_ui->modelSamplingRate->setMaximum(static_cast<float>(modelObject->size()));
  400. m_ui->modelSamplingRate->setSingleStep(1.);
  401. m_ui->modelSamplingRate->setMinimum(0.);
  402. }
  403. //data
  404. {
  405. m_ui->dataSamplingRate->setDecimals(0);
  406. m_ui->dataSamplingRate->setMaximum(static_cast<float>(dataObject->size()));
  407. m_ui->dataSamplingRate->setSingleStep(1.);
  408. m_ui->dataSamplingRate->setMinimum(0.);
  409. }
  410. }
  411. break;
  412. case OCTREE:
  413. {
  414. //model
  415. {
  416. if (!modelObject->getOctree())
  417. modelObject->computeOctree();
  418. m_ui->modelSamplingRate->setDecimals(0);
  419. m_ui->modelSamplingRate->setMaximum(static_cast<double>(CCCoreLib::DgmOctree::MAX_OCTREE_LEVEL));
  420. m_ui->modelSamplingRate->setMinimum(1.);
  421. m_ui->modelSamplingRate->setSingleStep(1.);
  422. }
  423. //data
  424. {
  425. if (!dataObject->getOctree())
  426. dataObject->computeOctree();
  427. m_ui->dataSamplingRate->setDecimals(0);
  428. m_ui->dataSamplingRate->setMaximum(static_cast<double>(CCCoreLib::DgmOctree::MAX_OCTREE_LEVEL));
  429. m_ui->dataSamplingRate->setMinimum(1.);
  430. m_ui->dataSamplingRate->setSingleStep(1.);
  431. }
  432. }
  433. break;
  434. default:
  435. {
  436. //model
  437. {
  438. m_ui->modelSamplingRate->setDecimals(2);
  439. m_ui->modelSamplingRate->setMaximum(100.);
  440. m_ui->modelSamplingRate->setSingleStep(0.01);
  441. m_ui->modelSamplingRate->setMinimum(0.);
  442. }
  443. //data
  444. {
  445. m_ui->dataSamplingRate->setDecimals(2);
  446. m_ui->dataSamplingRate->setMaximum(100.);
  447. m_ui->dataSamplingRate->setSingleStep(0.01);
  448. m_ui->dataSamplingRate->setMinimum(0.);
  449. }
  450. }
  451. break;
  452. }
  453. if (index == NONE)
  454. {
  455. //model
  456. m_ui->modelSample->setSliderPosition(m_ui->modelSample->maximum());
  457. m_ui->modelSample->setEnabled(false);
  458. m_ui->modelSamplingRate->setEnabled(false);
  459. //data
  460. m_ui->dataSample->setSliderPosition(m_ui->dataSample->maximum());
  461. m_ui->dataSample->setEnabled(false);
  462. m_ui->dataSamplingRate->setEnabled(false);
  463. }
  464. else
  465. {
  466. //model
  467. m_ui->modelSample->setEnabled(true);
  468. m_ui->modelSamplingRate->setEnabled(true);
  469. //data
  470. m_ui->dataSample->setEnabled(true);
  471. m_ui->dataSamplingRate->setEnabled(true);
  472. }
  473. modelSliderReleased();
  474. dataSliderReleased();
  475. }
  476. void ccAlignDlg::toggleNbMaxCandidates(bool activ)
  477. {
  478. m_ui->nbMaxCandidates->setEnabled(activ);
  479. }