ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvAlignDlg.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "ecvAlignDlg.h"
9 
10 #include "MainWindow.h"
11 #include "ui_alignDlg.h"
12 
13 // common
14 #include <ecvQtHelpers.h>
15 
16 // CV_CORE_LIB
17 #include <CVPointCloud.h>
18 #include <CloudSamplingTools.h>
19 #include <DgmOctree.h>
21 #include <ReferenceCloud.h>
22 
23 // CV_DB_LIB
24 #include <ecvDisplayTools.h>
25 #include <ecvGenericPointCloud.h>
26 #include <ecvProgressDialog.h>
27 
29  ccGenericPointCloud* model,
30  QWidget* parent)
31  : QDialog(parent, Qt::Tool), m_ui(new Ui::AlignDialog) {
32  m_ui->setupUi(this);
33 
34  m_ui->samplingMethod->addItem("None");
35  m_ui->samplingMethod->addItem("Random");
36  m_ui->samplingMethod->addItem("Space");
37  m_ui->samplingMethod->addItem("Octree");
38  m_ui->samplingMethod->setCurrentIndex(NONE);
39 
40  ccQtHelpers::SetButtonColor(m_ui->dataColorButton, Qt::red);
41  ccQtHelpers::SetButtonColor(m_ui->modelColorButton, Qt::yellow);
42 
43  dataObject = data;
44  modelObject = model;
46 
47  changeSamplingMethod(m_ui->samplingMethod->currentIndex());
48  toggleNbMaxCandidates(m_ui->isNbCandLimited->isChecked());
49 
50  connect(m_ui->swapButton, &QPushButton::clicked, this,
52  connect(m_ui->modelSample, &QSlider::sliderReleased, this,
54  connect(m_ui->dataSample, &QSlider::sliderReleased, this,
56  connect(m_ui->deltaEstimation, &QPushButton::clicked, this,
58  connect(m_ui->isNbCandLimited, &QCheckBox::toggled, this,
60  connect(m_ui->samplingMethod,
61  static_cast<void (QComboBox::*)(int)>(
62  &QComboBox::currentIndexChanged),
64  connect(m_ui->dataSamplingRate,
65  static_cast<void (QDoubleSpinBox::*)(double)>(
66  &QDoubleSpinBox::valueChanged),
68  connect(m_ui->modelSamplingRate,
69  static_cast<void (QDoubleSpinBox::*)(double)>(
70  &QDoubleSpinBox::valueChanged),
72 }
73 
77 }
78 
79 unsigned ccAlignDlg::getNbTries() { return m_ui->nbTries->value(); }
80 
81 double ccAlignDlg::getOverlap() { return m_ui->overlap->value(); }
82 
83 double ccAlignDlg::getDelta() { return m_ui->delta->value(); }
84 
86 
88 
90  return (CC_SAMPLING_METHOD)m_ui->samplingMethod->currentIndex();
91 }
92 
94  return m_ui->isNbCandLimited->isChecked();
95 }
96 
98  return m_ui->nbMaxCandidates->value();
99 }
100 
102  cloudViewer::ReferenceCloud* sampledCloud = nullptr;
103 
104  switch (getSamplingMethod()) {
105  case SPACE: {
107  false);
108  sampledCloud =
110  modelObject,
111  static_cast<PointCoordinateType>(
112  m_ui->modelSamplingRate->value()),
113  modParams);
114  } break;
115  case OCTREE:
116  if (modelObject->getOctree()) {
117  sampledCloud = cloudViewer::CloudSamplingTools::
119  modelObject,
120  static_cast<unsigned char>(
121  m_ui->modelSamplingRate->value()),
123  NEAREST_POINT_TO_CELL_CENTER,
124  nullptr, modelObject->getOctree().data());
125  } else {
126  CVLog::Error(
127  "[ccAlignDlg::getSampledModel] Failed to get/compute "
128  "model octree!");
129  }
130  break;
131  case RANDOM: {
132  sampledCloud =
134  modelObject,
135  static_cast<unsigned>(
136  m_ui->modelSamplingRate->value()));
137  } break;
138  default: {
139  sampledCloud = new cloudViewer::ReferenceCloud(modelObject);
140  if (!sampledCloud->addPointIndex(0, modelObject->size())) {
141  delete sampledCloud;
142  sampledCloud = nullptr;
143  CVLog::Error(
144  "[ccAlignDlg::getSampledModel] Not enough memory!");
145  }
146  } break;
147  }
148 
149  return sampledCloud;
150 }
151 
153  cloudViewer::ReferenceCloud* sampledCloud = nullptr;
154 
155  switch (getSamplingMethod()) {
156  case SPACE: {
158  false);
159  sampledCloud =
161  dataObject,
162  static_cast<PointCoordinateType>(
163  m_ui->dataSamplingRate->value()),
164  modParams);
165  } break;
166  case OCTREE:
167  if (dataObject->getOctree()) {
168  sampledCloud = cloudViewer::CloudSamplingTools::
170  dataObject,
171  static_cast<unsigned char>(
172  m_ui->dataSamplingRate->value()),
174  NEAREST_POINT_TO_CELL_CENTER,
175  nullptr, dataObject->getOctree().data());
176  } else {
177  CVLog::Error(
178  "[ccAlignDlg::getSampledData] Failed to get/compute "
179  "data octree!");
180  }
181  break;
182  case RANDOM: {
183  sampledCloud =
185  dataObject,
186  (unsigned)(m_ui->dataSamplingRate->value()));
187  } break;
188  default: {
189  sampledCloud = new cloudViewer::ReferenceCloud(dataObject);
190  if (!sampledCloud->addPointIndex(0, dataObject->size())) {
191  delete sampledCloud;
192  sampledCloud = nullptr;
193  CVLog::Error("[ccAlignDlg::getSampledData] Not enough memory!");
194  }
195  } break;
196  }
197 
198  return sampledCloud;
199 }
200 
202  if (!modelObject || !dataObject) return;
203 
204  m_ui->modelCloud->setText(modelObject->getName());
205  modelObject->setVisible(true);
207 
208  m_ui->dataCloud->setText(dataObject->getName());
209  dataObject->setVisible(true);
211 
213 }
214 
215 // SLOTS
219  changeSamplingMethod(m_ui->samplingMethod->currentIndex());
220 }
221 
223  double rate = static_cast<double>(m_ui->modelSample->sliderPosition()) /
224  m_ui->modelSample->maximum();
225  if (getSamplingMethod() == SPACE) rate = 1.0 - rate;
226  rate *= m_ui->modelSamplingRate->maximum();
227  m_ui->modelSamplingRate->setValue(rate);
229 }
230 
232  double rate = static_cast<double>(m_ui->dataSample->sliderPosition()) /
233  m_ui->dataSample->maximum();
234  if (getSamplingMethod() == SPACE) rate = 1.0 - rate;
235  rate *= m_ui->dataSamplingRate->maximum();
236  m_ui->dataSamplingRate->setValue(rate);
238 }
239 
241  QString message("An error occurred");
242 
244  float rate = static_cast<float>(m_ui->modelSamplingRate->value()) /
245  m_ui->modelSamplingRate->maximum();
246  if (method == SPACE) rate = 1.0f - rate;
247  m_ui->modelSample->setSliderPosition(
248  static_cast<int>(rate * m_ui->modelSample->maximum()));
249 
250  switch (method) {
251  case SPACE: {
252  cloudViewer::ReferenceCloud* tmpCloud =
253  getSampledModel(); // DGM FIXME: wow! you generate a
254  // spatially sampled cloud just to
255  // display its size?!
256  if (tmpCloud) {
257  message = QString("distance units (%1 remaining points)")
258  .arg(tmpCloud->size());
259  delete tmpCloud;
260  }
261  } break;
262  case RANDOM: {
263  message = QString("remaining points (%1%)")
264  .arg(rate * 100.0f, 0, 'f', 1);
265  } break;
266  case OCTREE: {
267  cloudViewer::ReferenceCloud* tmpCloud =
268  getSampledModel(); // DGM FIXME: wow! you generate a
269  // spatially sampled cloud just to
270  // display its size?!
271  if (tmpCloud) {
272  message = QString("%1 remaining points").arg(tmpCloud->size());
273  delete tmpCloud;
274  }
275  } break;
276  default: {
277  unsigned remaining =
278  static_cast<unsigned>(rate * modelObject->size());
279  message = QString("%1 remaining points").arg(remaining);
280  } break;
281  }
282 
283  m_ui->modelRemaining->setText(message);
284 }
285 
287  QString message("An error occurred");
288 
290  double rate = static_cast<float>(m_ui->dataSamplingRate->value() /
291  m_ui->dataSamplingRate->maximum());
292  if (method == SPACE) rate = 1.0 - rate;
293  m_ui->dataSample->setSliderPosition(
294  static_cast<int>(rate * m_ui->dataSample->maximum()));
295 
296  switch (method) {
297  case SPACE: {
298  cloudViewer::ReferenceCloud* tmpCloud =
299  getSampledData(); // DGM FIXME: wow! you generate a
300  // spatially sampled cloud just to
301  // display its size?!
302  if (tmpCloud) {
303  message = QString("distance units (%1 remaining points)")
304  .arg(tmpCloud->size());
305  delete tmpCloud;
306  }
307  } break;
308  case RANDOM: {
309  message = QString("remaining points (%1%)")
310  .arg(rate * 100.0, 0, 'f', 1);
311  } break;
312  case OCTREE: {
313  cloudViewer::ReferenceCloud* tmpCloud =
314  getSampledData(); // DGM FIXME: wow! you generate a
315  // spatially sampled cloud just to
316  // display its size?!
317  if (tmpCloud) {
318  message = QString("%1 remaining points").arg(tmpCloud->size());
319  delete tmpCloud;
320  }
321  } break;
322  default: {
323  unsigned remaining =
324  static_cast<unsigned>(rate * dataObject->size());
325  message = QString("%1 remaining points").arg(remaining);
326  } break;
327  }
328 
329  m_ui->dataRemaining->setText(message);
330 }
331 
333  ecvProgressDialog pDlg(false, this);
334 
336 
337  // we have to work on a copy of the cloud in order to prevent the algorithms
338  // from modifying the original cloud.
340  {
341  cloud.reserve(sampledData->size());
342  for (unsigned i = 0; i < sampledData->size(); i++)
343  cloud.addPoint(*sampledData->getPoint(i));
344  cloud.enableScalarField();
345  }
346 
350  CVLog::Error("Failed to compute approx. density");
351  return;
352  }
353  unsigned count = 0;
354  double meanDensity = 0;
355  double meanSqrDensity = 0;
356  for (unsigned i = 0; i < cloud.size(); i++) {
357  ScalarType value = cloud.getPointScalarValue(i);
358  if (value == value) {
359  meanDensity += value;
360  meanSqrDensity += static_cast<double>(value) * value;
361  count++;
362  }
363  }
364 
365  if (count) {
366  meanDensity /= count;
367  meanSqrDensity /= count;
368  }
369  double dev = meanSqrDensity - (meanDensity * meanDensity);
370 
371  m_ui->delta->setValue(meanDensity + dev);
372  delete sampledData;
373 }
374 
376  // Reste a changer les textes d'aide
377  switch (index) {
378  case SPACE: {
379  // model
380  {
381  m_ui->modelSamplingRate->setDecimals(4);
382  int oldSliderPos = m_ui->modelSample->sliderPosition();
383  CCVector3 bbMin;
384  CCVector3 bbMax;
385  modelObject->getBoundingBox(bbMin, bbMax);
386  double dist = (bbMin - bbMax).norm();
387  m_ui->modelSamplingRate->setMaximum(dist);
388  m_ui->modelSample->setSliderPosition(oldSliderPos);
389  m_ui->modelSamplingRate->setSingleStep(0.01);
390  m_ui->modelSamplingRate->setMinimum(0.);
391  }
392  // data
393  {
394  m_ui->dataSamplingRate->setDecimals(4);
395  int oldSliderPos = m_ui->dataSample->sliderPosition();
396  CCVector3 bbMin;
397  CCVector3 bbMax;
398  dataObject->getBoundingBox(bbMin, bbMax);
399  double dist = (bbMin - bbMax).norm();
400  m_ui->dataSamplingRate->setMaximum(dist);
401  m_ui->dataSample->setSliderPosition(oldSliderPos);
402  m_ui->dataSamplingRate->setSingleStep(0.01);
403  m_ui->dataSamplingRate->setMinimum(0.);
404  }
405  } break;
406  case RANDOM: {
407  // model
408  {
409  m_ui->modelSamplingRate->setDecimals(0);
410  m_ui->modelSamplingRate->setMaximum(
411  static_cast<float>(modelObject->size()));
412  m_ui->modelSamplingRate->setSingleStep(1.);
413  m_ui->modelSamplingRate->setMinimum(0.);
414  }
415  // data
416  {
417  m_ui->dataSamplingRate->setDecimals(0);
418  m_ui->dataSamplingRate->setMaximum(
419  static_cast<float>(dataObject->size()));
420  m_ui->dataSamplingRate->setSingleStep(1.);
421  m_ui->dataSamplingRate->setMinimum(0.);
422  }
423  } break;
424  case OCTREE: {
425  // model
426  {
428  m_ui->modelSamplingRate->setDecimals(0);
429  m_ui->modelSamplingRate->setMaximum(static_cast<double>(
431  m_ui->modelSamplingRate->setMinimum(1.);
432  m_ui->modelSamplingRate->setSingleStep(1.);
433  }
434  // data
435  {
437  m_ui->dataSamplingRate->setDecimals(0);
438  m_ui->dataSamplingRate->setMaximum(static_cast<double>(
440  m_ui->dataSamplingRate->setMinimum(1.);
441  m_ui->dataSamplingRate->setSingleStep(1.);
442  }
443  } break;
444  default: {
445  // model
446  {
447  m_ui->modelSamplingRate->setDecimals(2);
448  m_ui->modelSamplingRate->setMaximum(100.);
449  m_ui->modelSamplingRate->setSingleStep(0.01);
450  m_ui->modelSamplingRate->setMinimum(0.);
451  }
452  // data
453  {
454  m_ui->dataSamplingRate->setDecimals(2);
455  m_ui->dataSamplingRate->setMaximum(100.);
456  m_ui->dataSamplingRate->setSingleStep(0.01);
457  m_ui->dataSamplingRate->setMinimum(0.);
458  }
459  } break;
460  }
461 
462  if (index == NONE) {
463  // model
464  m_ui->modelSample->setSliderPosition(m_ui->modelSample->maximum());
465  m_ui->modelSample->setEnabled(false);
466  m_ui->modelSamplingRate->setEnabled(false);
467  // data
468  m_ui->dataSample->setSliderPosition(m_ui->dataSample->maximum());
469  m_ui->dataSample->setEnabled(false);
470  m_ui->dataSamplingRate->setEnabled(false);
471  } else {
472  // model
473  m_ui->modelSample->setEnabled(true);
474  m_ui->modelSamplingRate->setEnabled(true);
475  // data
476  m_ui->dataSample->setEnabled(true);
477  m_ui->dataSamplingRate->setEnabled(true);
478  }
479 
482 }
483 
485  m_ui->nbMaxCandidates->setEnabled(activ);
486 }
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int count
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
void setColorsAndLabels()
double getDelta()
Definition: ecvAlignDlg.cpp:83
virtual ~ccAlignDlg()
Definition: ecvAlignDlg.cpp:74
ccGenericPointCloud * dataObject
'Data' cloud (static)
Definition: ecvAlignDlg.h:62
void dataSliderReleased()
ccGenericPointCloud * getDataObject()
Definition: ecvAlignDlg.cpp:87
unsigned getMaxNumberOfCandidates()
Definition: ecvAlignDlg.cpp:97
cloudViewer::ReferenceCloud * getSampledModel()
CC_SAMPLING_METHOD getSamplingMethod()
Definition: ecvAlignDlg.cpp:89
double getOverlap()
Definition: ecvAlignDlg.cpp:81
ccGenericPointCloud * modelObject
'Model' cloud (static)
Definition: ecvAlignDlg.h:59
void modelSamplingRateChanged(double value)
void toggleNbMaxCandidates(bool activ)
cloudViewer::ReferenceCloud * getSampledData()
ccGenericPointCloud * getModelObject()
Definition: ecvAlignDlg.cpp:85
ccAlignDlg(ccGenericPointCloud *data, ccGenericPointCloud *model, QWidget *parent=nullptr)
Definition: ecvAlignDlg.cpp:28
void swapModelAndData()
unsigned getNbTries()
Definition: ecvAlignDlg.cpp:79
void modelSliderReleased()
Ui::AlignDialog * m_ui
Definition: ecvAlignDlg.h:66
void dataSamplingRateChanged(double value)
void estimateDelta()
bool isNumberOfCandidatesLimited()
Definition: ecvAlignDlg.cpp:93
void changeSamplingMethod(int index)
virtual void setTempColor(const ecvColor::Rgb &col, bool autoActivate=true)
Sets current temporary (unique)
virtual void setVisible(bool state)
Sets entity visibility.
virtual void enableTempColor(bool state)
Set temporary color activation state.
A 3D cloud interface with associated features (color, normals, octree, etc.)
virtual ccOctree::Shared computeOctree(cloudViewer::GenericProgressCallback *progressCb=nullptr, bool autoAddChild=true)
Computes the cloud octree.
virtual ccOctree::Shared getOctree() const
Returns the associated octree (if any)
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
static void SetButtonColor(QAbstractButton *button, const QColor &col)
Sets a button background color.
Definition: ecvQtHelpers.h:17
Several point cloud resampling algorithms (octree-based, random, etc.)
static ReferenceCloud * subsampleCloudRandomly(GenericIndexedCloudPersist *cloud, unsigned newNumberOfPoints, GenericProgressCallback *progressCb=nullptr)
Subsamples a point cloud (process based on random selections)
static ReferenceCloud * subsampleCloudWithOctreeAtLevel(GenericIndexedCloudPersist *cloud, unsigned char octreeLevel, SUBSAMPLING_CELL_METHOD subsamplingMethod, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Subsamples a point cloud (process based on the octree)
static ReferenceCloud * resampleCloudSpatially(GenericIndexedCloudPersist *cloud, PointCoordinateType minDistance, const SFModulationParams &modParams, DgmOctree *octree=nullptr, GenericProgressCallback *progressCb=nullptr)
Resamples a point cloud (process based on inter point distance)
static const int MAX_OCTREE_LEVEL
Max octree subdivision level.
Definition: DgmOctree.h:67
virtual void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax)=0
Returns the cloud bounding box.
virtual unsigned size() const =0
Returns the number of points.
static ErrorCode ComputeLocalDensityApprox(GenericIndexedCloudPersist *cloud, Density densityType, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Computes the local density (approximate)
virtual bool reserve(unsigned newCapacity)
Reserves memory for the point database.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
unsigned size() const override
Definition: PointCloudTpl.h:38
ScalarType getPointScalarValue(unsigned pointIndex) const override
bool enableScalarField() override
Definition: PointCloudTpl.h:77
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
unsigned size() const override
Returns the number of points.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
static void RedrawDisplay(bool only2D=false, bool forceRedraw=true)
Graphical progress indicator (thread-safe)
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
constexpr Rgb red(MAX, 0, 0)
constexpr Rgb yellow(MAX, MAX, 0)
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
Parameters for the scalar-field based modulation of a parameter.