ccRegistrationDlg.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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 "ccRegistrationDlg.h"
  18. //Local
  19. #include "mainwindow.h"
  20. //common
  21. #include <ccQtHelpers.h>
  22. //CCCoreLib
  23. #include <DgmOctree.h>
  24. #include <CloudSamplingTools.h>
  25. #include <GeometricalAnalysisTools.h>
  26. #include <ReferenceCloud.h>
  27. //CCPluginAPI
  28. #include <ccQtHelpers.h>
  29. //qCC_db
  30. #include <ccHObject.h>
  31. //Qt
  32. #include <QThread>
  33. //system
  34. #include <assert.h>
  35. static bool s_adjustScale = false;
  36. static unsigned s_randomSamplingLimit = 50000;
  37. static double s_rmsDifference = 1.0e-5;
  38. static int s_maxIterationCount = 20;
  39. static bool s_useErrorDifferenceCriterion = true;
  40. static int s_finalOverlap = 100;
  41. static int s_rotComboIndex = 0;
  42. static bool s_transCheckboxes[3] = { true, true, true };
  43. static int s_maxThreadCount = ccQtHelpers::GetMaxThreadCount();
  44. static bool s_pointsRemoval = false;
  45. static bool s_useDataSFAsWeights = false;
  46. static bool s_useModelSFAsWeights = false;
  47. static bool s_useC2MSignedDistances = false;
  48. static bool s_robustC2MSignedDistances = true;
  49. static int s_normalsMatchingOption = CCCoreLib::ICPRegistrationTools::NO_NORMAL;
  50. ccRegistrationDlg::ccRegistrationDlg(ccHObject* data, ccHObject* model, QWidget* parent/*=nullptr*/)
  51. : QDialog(parent, Qt::Tool)
  52. , Ui::RegistrationDialog()
  53. {
  54. assert(data && model);
  55. dataEntity = data;
  56. modelEntity = model;
  57. setupUi(this);
  58. QDoubleValidator* rmsValidator = new QDoubleValidator(rmsDifferenceLineEdit);
  59. rmsValidator->setNotation(QDoubleValidator::ScientificNotation);
  60. rmsValidator->setRange(GetAbsoluteMinRMSDecrease(), 1.0, 1);
  61. rmsDifferenceLineEdit->setValidator(rmsValidator);
  62. updateGUI();
  63. ccQtHelpers::SetButtonColor(dataColorButton, Qt::red);
  64. ccQtHelpers::SetButtonColor(modelColorButton, Qt::yellow);
  65. static const int MaxThreadCount = QThread::idealThreadCount();
  66. maxThreadCountSpinBox->setRange(1, MaxThreadCount);
  67. maxThreadCountSpinBox->setSuffix(QString(" / %1").arg(MaxThreadCount));
  68. //restore semi-persistent settings
  69. {
  70. //semi-persistent options
  71. maxThreadCountSpinBox->setValue(s_maxThreadCount);
  72. adjustScaleCheckBox->setChecked(s_adjustScale);
  73. randomSamplingLimitSpinBox->setValue(s_randomSamplingLimit);
  74. setMinRMSDecrease(s_rmsDifference);
  75. maxIterationCount->setValue(s_maxIterationCount);
  76. if (s_useErrorDifferenceCriterion)
  77. errorCriterion->setChecked(true);
  78. else
  79. iterationsCriterion->setChecked(true);
  80. overlapSpinBox->setValue(s_finalOverlap);
  81. rotComboBox->setCurrentIndex(s_rotComboIndex);
  82. TxCheckBox->setChecked(s_transCheckboxes[0]);
  83. TyCheckBox->setChecked(s_transCheckboxes[1]);
  84. TzCheckBox->setChecked(s_transCheckboxes[2]);
  85. pointsRemoval->setChecked(s_pointsRemoval);
  86. checkBoxUseDataSFAsWeights->setChecked(s_useDataSFAsWeights);
  87. checkBoxUseModelSFAsWeights->setChecked(s_useModelSFAsWeights);
  88. useC2MSignedDistancesCheckBox->setChecked(s_useC2MSignedDistances);
  89. robustC2MDistsCheckBox->setChecked(s_robustC2MSignedDistances);
  90. normalsComboBox->setCurrentIndex(s_normalsMatchingOption);
  91. }
  92. connect(swapButton, &QAbstractButton::clicked, this, &ccRegistrationDlg::swapModelAndData);
  93. }
  94. ccRegistrationDlg::~ccRegistrationDlg()
  95. {
  96. if (modelEntity)
  97. {
  98. modelEntity->enableTempColor(false);
  99. modelEntity->prepareDisplayForRefresh_recursive();
  100. }
  101. if (dataEntity)
  102. {
  103. dataEntity->enableTempColor(false);
  104. dataEntity->prepareDisplayForRefresh_recursive();
  105. }
  106. MainWindow::RefreshAllGLWindow(false);
  107. }
  108. void ccRegistrationDlg::saveParameters() const
  109. {
  110. s_maxThreadCount = getMaxThreadCount();
  111. s_adjustScale = adjustScale();
  112. s_randomSamplingLimit = randomSamplingLimit();
  113. s_rmsDifference = getMinRMSDecrease();
  114. s_maxIterationCount = getMaxIterationCount();
  115. s_useErrorDifferenceCriterion = errorCriterion->isChecked();
  116. s_finalOverlap = overlapSpinBox->value();
  117. s_rotComboIndex = rotComboBox->currentIndex();
  118. s_transCheckboxes[0] = TxCheckBox->isChecked();
  119. s_transCheckboxes[1] = TyCheckBox->isChecked();
  120. s_transCheckboxes[2] = TzCheckBox->isChecked();
  121. s_pointsRemoval = removeFarthestPoints();
  122. s_useDataSFAsWeights = checkBoxUseDataSFAsWeights->isChecked();
  123. s_useModelSFAsWeights = checkBoxUseModelSFAsWeights->isChecked();
  124. s_useC2MSignedDistances = useC2MSignedDistancesCheckBox->isChecked();
  125. s_robustC2MSignedDistances = robustC2MDistsCheckBox->isChecked();
  126. s_normalsMatchingOption = normalsComboBox->currentIndex();
  127. }
  128. ccHObject *ccRegistrationDlg::getDataEntity()
  129. {
  130. return dataEntity;
  131. }
  132. ccHObject *ccRegistrationDlg::getModelEntity()
  133. {
  134. return modelEntity;
  135. }
  136. bool ccRegistrationDlg::useDataSFAsWeights() const
  137. {
  138. return checkBoxUseDataSFAsWeights->isEnabled() && checkBoxUseDataSFAsWeights->isChecked();
  139. }
  140. bool ccRegistrationDlg::useModelSFAsWeights() const
  141. {
  142. return checkBoxUseModelSFAsWeights->isEnabled() && checkBoxUseModelSFAsWeights->isChecked();
  143. }
  144. bool ccRegistrationDlg::useC2MSignedDistances(bool& robust) const
  145. {
  146. robust = robustC2MDistsCheckBox->isChecked();
  147. return useC2MSignedDistancesCheckBox->isEnabled() && useC2MSignedDistancesCheckBox->isChecked();
  148. }
  149. CCCoreLib::ICPRegistrationTools::NORMALS_MATCHING ccRegistrationDlg::normalsMatchingOption() const
  150. {
  151. if (normalsComboBox->isEnabled())
  152. {
  153. return static_cast<CCCoreLib::ICPRegistrationTools::NORMALS_MATCHING>(normalsComboBox->currentIndex());
  154. }
  155. else
  156. {
  157. return CCCoreLib::ICPRegistrationTools::NO_NORMAL;
  158. }
  159. }
  160. bool ccRegistrationDlg::adjustScale() const
  161. {
  162. return adjustScaleCheckBox->isChecked();
  163. }
  164. bool ccRegistrationDlg::removeFarthestPoints() const
  165. {
  166. return pointsRemoval->isChecked();
  167. }
  168. unsigned ccRegistrationDlg::randomSamplingLimit() const
  169. {
  170. return randomSamplingLimitSpinBox->value();
  171. }
  172. unsigned ccRegistrationDlg::getMaxIterationCount() const
  173. {
  174. return static_cast<unsigned>(std::max(1,maxIterationCount->value()));
  175. }
  176. unsigned ccRegistrationDlg::getFinalOverlap() const
  177. {
  178. return static_cast<unsigned>(std::max(10,overlapSpinBox->value()));
  179. }
  180. int ccRegistrationDlg::getMaxThreadCount() const
  181. {
  182. return maxThreadCountSpinBox->value();
  183. }
  184. double ccRegistrationDlg::GetAbsoluteMinRMSDecrease()
  185. {
  186. return 1.0e-7;
  187. }
  188. double ccRegistrationDlg::getMinRMSDecrease() const
  189. {
  190. bool ok = true;
  191. double val = rmsDifferenceLineEdit->text().toDouble(&ok);
  192. if (!ok)
  193. {
  194. assert(false);
  195. val = std::numeric_limits<double>::quiet_NaN();
  196. }
  197. return val;
  198. }
  199. void ccRegistrationDlg::setMinRMSDecrease(double value)
  200. {
  201. if (std::isnan(value))
  202. {
  203. //last input value was invalid, restoring default
  204. value = 1.0e-5;
  205. }
  206. rmsDifferenceLineEdit->setText(QString::number(value, 'E', 1));
  207. }
  208. ccRegistrationDlg::ConvergenceMethod ccRegistrationDlg::getConvergenceMethod() const
  209. {
  210. if (errorCriterion->isChecked())
  211. return CCCoreLib::ICPRegistrationTools::MAX_ERROR_CONVERGENCE;
  212. else
  213. return CCCoreLib::ICPRegistrationTools::MAX_ITER_CONVERGENCE;
  214. }
  215. int ccRegistrationDlg::getTransformationFilters() const
  216. {
  217. int filters = CCCoreLib::RegistrationTools::SKIP_NONE;
  218. switch (rotComboBox->currentIndex())
  219. {
  220. case 0:
  221. break;
  222. case 1:
  223. filters |= CCCoreLib::RegistrationTools::SKIP_RYZ;
  224. break;
  225. case 2:
  226. filters |= CCCoreLib::RegistrationTools::SKIP_RXZ;
  227. break;
  228. case 3:
  229. filters |= CCCoreLib::RegistrationTools::SKIP_RXY;
  230. break;
  231. case 4:
  232. filters |= CCCoreLib::RegistrationTools::SKIP_ROTATION;
  233. break;
  234. default:
  235. assert(false);
  236. break;
  237. }
  238. if (!TxCheckBox->isChecked())
  239. filters |= CCCoreLib::RegistrationTools::SKIP_TX;
  240. if (!TyCheckBox->isChecked())
  241. filters |= CCCoreLib::RegistrationTools::SKIP_TY;
  242. if (!TzCheckBox->isChecked())
  243. filters |= CCCoreLib::RegistrationTools::SKIP_TZ;
  244. return filters;
  245. }
  246. void ccRegistrationDlg::updateGUI()
  247. {
  248. if (!modelEntity || !dataEntity)
  249. return;
  250. modelLineEdit->setText(modelEntity->getName());
  251. modelEntity->setVisible(true);
  252. modelEntity->setTempColor(ccColor::yellow);
  253. modelEntity->prepareDisplayForRefresh_recursive();
  254. dataLineEdit->setText(dataEntity->getName());
  255. dataEntity->setVisible(true);
  256. dataEntity->setTempColor(ccColor::red);
  257. dataEntity->prepareDisplayForRefresh_recursive();
  258. checkBoxUseDataSFAsWeights->setEnabled(dataEntity->hasDisplayedScalarField());
  259. checkBoxUseModelSFAsWeights->setEnabled(modelEntity->isKindOf(CC_TYPES::POINT_CLOUD) && modelEntity->hasDisplayedScalarField()); //only supported for clouds
  260. bool hasRefMesh = modelEntity->isKindOf(CC_TYPES::MESH);
  261. useC2MSignedDistancesCheckBox->setEnabled(hasRefMesh); //only supported if a mesh is the reference cloud
  262. robustC2MDistsCheckBox->setEnabled(hasRefMesh);
  263. normalsComboBox->setEnabled(dataEntity->hasNormals() && modelEntity->hasNormals()); //only supported if both the to-be-aligned and the reference entities have normals
  264. MainWindow::RefreshAllGLWindow(false);
  265. }
  266. void ccRegistrationDlg::swapModelAndData()
  267. {
  268. std::swap(dataEntity, modelEntity);
  269. updateGUI();
  270. }