ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvPoissonReconDlg.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 "ecvPoissonReconDlg.h"
9 
10 #include "ecvEntityAction.h"
11 
12 // CV_DB_LIB
13 #include <ecvHObjectCaster.h>
14 #include <ecvMesh.h>
15 #include <ecvPointCloud.h>
16 
17 // QT
18 #include <QDialog>
19 #include <QInputDialog>
20 #include <QMainWindow>
21 #include <QMessageBox>
22 #include <QProgressDialog>
23 #include <QtConcurrentRun>
24 #include <QtCore>
25 #include <QtGui>
26 
27 // System
28 #if defined(CV_WINDOWS)
29 #include "Windows.h"
30 #else
31 #include <time.h>
32 #include <unistd.h>
33 #endif
34 
35 static double s_defaultResolution = 0.0;
36 static bool s_depthMode = true;
37 static ccPointCloud* s_cloud = nullptr;
38 static ccMesh* s_mesh = nullptr;
39 
40 static std::vector<double> s_density;
41 
43 struct Parameters {
45  Parameters() = default;
46 
49 
52 
55 
59  int depth = 8;
60 
63  float finestCellWidth = 0.0f;
64 
67 
70  float scale = 1.1f;
71 
74 
80  float samplesPerNode = 1.5f;
81 
84 
87  float pointWeight = 2.0f;
88 
90 
93  int iters = 8;
94 
97  bool density = false;
98 
101  bool withColors = true;
102 
104 
107  float colorPullFactor = 32.0f;
108 
110 
113  float normalConfidence = 0.0;
114 
116 
120  float normalConfidenceBias = 0.0;
121 
124  bool linearFit = false;
125 
128  int threads = 1;
129 
134 
137  int fullDepth = 5;
138 
140  int baseDepth = 0;
141 
143  int baseVCycles = 1;
144 
146  float cgAccuracy = 1.0e-3f;
147 };
149 
151  // invalid parameters
152  if (!s_cloud) {
153  return false;
154  }
155 
156  std::tuple<std::shared_ptr<ccMesh>, std::vector<double>> result =
158  *s_cloud, (size_t)s_params.depth,
162 
163  s_mesh = new ccMesh();
164  s_mesh->CreateInternalCloud();
165  *s_mesh = *std::get<0>(result);
166 
167  if (s_params.density) {
168  s_density = std::get<1>(result);
169  }
170 
171  return !s_mesh->IsEmpty();
172 }
173 
175  : QDialog(parent, Qt::Tool),
176  Ui::PoissonReconParamDialog(),
177  m_app(parent),
178  m_applyAllClouds(false) {
179  setupUi(this);
180  m_clouds.clear();
181  m_normalsMask.clear();
182  m_result.clear();
183 }
184 
186  s_defaultResolution = cloud->getOwnBB().getDiagNormd() / 200.0;
187  bool cloudHasColors = cloud->hasColors();
188  importColorsCheckBox->setVisible(cloudHasColors);
189  if (s_depthMode)
190  depthRadioButton->setChecked(true);
191  else
192  resolutionRadioButton->setChecked(true);
193 
194  // init dialog with semi-persistent settings
195  depthSpinBox->setValue(s_params.depth);
196  resolutionDoubleSpinBox->setValue(s_defaultResolution);
197  samplesPerNodeSpinBox->setValue(s_params.samplesPerNode);
198  importColorsCheckBox->setChecked(s_params.withColors);
199  densityCheckBox->setChecked(s_params.density);
200  weightDoubleSpinBox->setValue(s_params.pointWeight);
201  linearFitCheckBox->setChecked(s_params.linearFit);
202  switch (s_params.boundary) {
203  case Parameters::FREE:
204  boundaryComboBox->setCurrentIndex(0);
205  break;
207  boundaryComboBox->setCurrentIndex(1);
208  break;
209  case Parameters::NEUMANN:
210  boundaryComboBox->setCurrentIndex(2);
211  break;
212  case Parameters::COUNT:
213  boundaryComboBox->setCurrentIndex(3);
214  break;
215  default:
216  assert(false);
217  break;
218  }
219 }
220 
222  bool result = false;
223  {
224  // progress dialog (Qtconcurrent::run can't be canceled!)
225  QProgressDialog pDlg(tr("Initialization"), QString(), 0, 0, m_app);
226  pDlg.setWindowTitle(tr("Poisson Reconstruction"));
227  pDlg.show();
228  // QApplication::processEvents();
229  if (!s_cloud) return result;
230 
231  QString progressLabel(tr("Reconstruction for [%1] in progress\n")
232  .arg(s_cloud->getName()));
233  if (s_depthMode)
234  progressLabel += tr("level: %1").arg(s_params.depth);
235  else
236  progressLabel += tr("resolution: %1").arg(s_params.finestCellWidth);
237  progressLabel += tr(" [%1 thread(s)]").arg(s_params.threads);
238 
239  pDlg.setLabelText(progressLabel);
240  QApplication::processEvents();
241 
242  // run in a separate thread
243 
244  QFuture<bool> future = QtConcurrent::run(doReconstruct);
245 
246  // wait until process is finished!
247  while (!future.isFinished()) {
248 #if defined(CV_WINDOWS)
249  ::Sleep(500);
250 #else
251  usleep(500 * 1000);
252 #endif
253 
254  pDlg.setValue(pDlg.value() + 1);
255  QApplication::processEvents();
256  }
257 
258  result = future.result();
259 
260  s_cloud = nullptr;
261 
262  pDlg.hide();
263  QApplication::processEvents();
264  }
265 
266  return result;
267 }
268 
270  if (!ent->isKindOf(CV_TYPES::POINT_CLOUD)) return false;
271 
272  m_clouds.push_back(ent);
273  if (ent->isA(CV_TYPES::POINT_CLOUD)) {
274  m_normalsMask.push_back(static_cast<ccPointCloud*>(ent)->hasNormals());
275  } else {
276  m_normalsMask.push_back(false);
277  }
278  return false;
279 }
280 
282  return m_result;
283 }
284 
286  int nCount = std::count(m_normalsMask.begin(), m_normalsMask.end(), false);
287  bool updateNormals = false;
288  if (nCount > 0) {
289  updateNormals =
290  (QMessageBox::question(
291  m_app, tr("Some clouds have no normals?"),
292  tr("Clouds must have normals before poisson "
293  "reconstruction. Do you want to compute normals?"),
294  QMessageBox::Yes,
295  QMessageBox::No) == QMessageBox::Yes);
296  if (!updateNormals) {
297  CVLog::Error(
298  tr("m_clouds must have normals before poisson "
299  "reconstruction!"));
300  return false;
301  } else {
302  assert(m_normalsMask.size() == m_clouds.size());
303  ccHObject::Container toBeEstimateNormals;
304  for (size_t i = 0; i < m_normalsMask.size(); ++i) {
305  if (!m_normalsMask[i])
306  toBeEstimateNormals.push_back(m_clouds[i]);
307  }
308  if (!ccEntityAction::computeNormals(toBeEstimateNormals, m_app)) {
309  CVLog::Error(tr("Computer normals failed!"));
310  return false;
311  }
312  }
313  }
314 
315  m_applyAllClouds = false;
316  for (size_t i = 0; i < m_clouds.size(); ++i) {
317  ccHObject* ent = m_clouds[i];
318  assert(ent->isKindOf(CV_TYPES::POINT_CLOUD));
319 
320  // poisson reconstruction parameters
322  adjustParams(cloud);
323 
324  if (!m_applyAllClouds) {
325  if (!showDialog()) {
326  continue;
327  }
328  }
329  updateParams();
330 
331  // clear history
332  s_cloud = cloud;
333  s_mesh = nullptr;
334  s_density.clear();
335 
336  // run in a separate thread
337  // compute mesh
338  if (!doComputation()) {
339  CVLog::Warning("");
340  continue;
341  }
342 
343  if (s_mesh) {
344  cloud->setEnabled(false); // can't disable the cloud as the
345  // resulting mesh will be its child!
346  if (cloud->getParent()) {
347  cloud->getParent()->addChild(s_mesh);
348  }
349 
350  ccPointCloud* newPC =
352 
353  CVLog::Print(tr("[PoissonRecon] reconstruction from [%1] (%2 "
354  "triangles, %3 vertices)")
355  .arg(cloud->getName())
356  .arg(s_mesh->size())
357  .arg(newPC->size()));
358 
359  s_mesh->setName(tr("Mesh[%1] (level %2)")
360  .arg(cloud->getName())
361  .arg(s_params.depth));
362  newPC->setEnabled(false);
363  s_mesh->setVisible(true);
364  s_mesh->computeNormals(true);
365  if (!s_params.withColors) {
366  newPC->unallocateColors();
367  newPC->showColors(false);
368  }
369  s_mesh->showColors(newPC->hasColors());
370 
371  // copy Global Shift & Scale information
372  newPC->setGlobalShift(cloud->getGlobalShift());
373  newPC->setGlobalScale(cloud->getGlobalScale());
374 
375  if (s_params.density) {
376  if (s_density.size() != newPC->size()) {
378  "[doActionPoissonReconstruction] density dimension "
379  "does not match!");
380  s_mesh->showColors(newPC->colorsShown());
381  s_mesh->showSF(false);
382  m_result.push_back(s_mesh);
383  continue;
384  }
385 
386  std::vector<std::vector<ScalarType>> scalarsVector;
387  std::vector<std::vector<double>> tempScalarsvector;
388  std::vector<ccHObject*> tempClouds;
389  tempClouds.push_back(s_mesh);
390  tempScalarsvector.push_back(s_density);
391  ccEntityAction::ConvertToScalarType<double>(tempScalarsvector,
392  scalarsVector);
393  if (!ccEntityAction::importToSF(tempClouds, scalarsVector,
394  "Density")) {
395  CVLog::Error(
396  "[doActionPoissonReconstruction] import sf "
397  "failed!");
398  s_mesh->showSF(false);
399  } else {
400  s_mesh->showSF(true);
401  }
402  newPC->showSF(true);
403  s_mesh->showColors(newPC->colorsShown());
404  s_mesh->showSF(true);
405  }
406 
407  m_result.push_back(s_mesh);
408  } else {
409  CVLog::Warning(tr("[ecvPoissonReconDlg] Poisson reconstruction "
410  "from [%1] failed!")
411  .arg(ent->getName()));
412  }
413  }
414 
415  return true;
416 }
417 
419  if (!this->exec()) {
420  return false;
421  }
422 
423  // set parameters with dialog settings
424  updateParams();
425 
426  return true;
427 }
428 
430  // Set parameters with dialog settings
431  s_depthMode = depthRadioButton->isChecked();
432  s_defaultResolution = resolutionDoubleSpinBox->value();
433  s_params.depth = (s_depthMode ? depthSpinBox->value() : 0);
435  static_cast<float>(s_depthMode ? 0.0 : s_defaultResolution);
437  static_cast<float>(samplesPerNodeSpinBox->value());
438  s_params.withColors = importColorsCheckBox->isChecked();
439  s_params.density = densityCheckBox->isChecked();
440  s_params.pointWeight = static_cast<float>(weightDoubleSpinBox->value());
441  s_params.linearFit = linearFitCheckBox->isChecked();
442  switch (boundaryComboBox->currentIndex()) {
443  case 0:
445  break;
446  case 1:
448  break;
449  case 2:
451  break;
452  case 3:
454  break;
455  default:
456  assert(false);
457  break;
458  }
459 
460  m_applyAllClouds = applyParamsAllCloudsCheckBox->isChecked();
461 }
int count
core::Tensor result
Definition: VtkUtils.cpp:76
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
virtual bool colorsShown() const
Returns whether colors are shown or not.
virtual void setVisible(bool state)
Sets entity visibility.
virtual void showColors(bool state)
Sets colors visibility.
virtual void showSF(bool state)
Sets active scalarfield visibility.
ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
ccHObject * getParent() const
Returns parent object.
Definition: ecvHObject.h:245
std::vector< ccHObject * > Container
Standard instances container (for children, etc.)
Definition: ecvHObject.h:337
Triangular mesh.
Definition: ecvMesh.h:35
static std::tuple< std::shared_ptr< ccMesh >, std::vector< double > > CreateFromPointCloudPoisson(const ccPointCloud &pcd, size_t depth=8, size_t width=0, float scale=1.1f, bool linear_fit=false, float point_weight=2.f, float samples_per_node=1.5f, int boundary_type=2, int n_threads=-1)
Function that computes a triangle mesh from a oriented PointCloud pcd. This implements the Screened P...
virtual bool IsEmpty() const override
Definition: ecvMesh.h:735
ccGenericPointCloud * getAssociatedCloud() const override
Returns the vertices cloud.
Definition: ecvMesh.h:143
virtual unsigned size() const override
Returns the number of triangles.
bool computeNormals(bool perVertex)
Computes normals.
virtual QString getName() const
Returns object name.
Definition: ecvObject.h:72
bool isA(CV_CLASS_ENUM type) const
Definition: ecvObject.h:131
virtual void setName(const QString &name)
Sets object name.
Definition: ecvObject.h:75
virtual void setEnabled(bool state)
Sets the "enabled" property.
Definition: ecvObject.h:102
bool isKindOf(CV_CLASS_ENUM type) const
Definition: ecvObject.h:128
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void unallocateColors()
Erases the cloud colors.
bool hasColors() const override
Returns whether colors are enabled or not.
virtual void setGlobalScale(double scale)
virtual void setGlobalShift(double x, double y, double z)
Sets shift applied to original coordinates (information storage only)
virtual const CCVector3d & getGlobalShift() const
Returns the shift applied to original coordinates.
virtual double getGlobalScale() const
Returns the scale applied to original coordinates.
double getDiagNormd() const
Returns diagonal length (double precision)
Definition: BoundingBox.h:175
unsigned size() const override
Definition: PointCloudTpl.h:38
void adjustParams(ccPointCloud *cloud)
ecvPoissonReconDlg(QWidget *parent=0)
ccHObject::Container & getReconstructions()
bool addEntity(ccHObject *ent)
static std::vector< double > s_density
static bool s_depthMode
static Parameters s_params
static ccPointCloud * s_cloud
static double s_defaultResolution
bool doReconstruct()
static ccMesh * s_mesh
@ POINT_CLOUD
Definition: CVTypes.h:104
bool importToSF(const ccHObject::Container &selectedEntities, const std::vector< std::vector< ScalarType >> &scalarsVector, const std::string &name)
bool computeNormals(const ccHObject::Container &selectedEntities, QWidget *parent)
void Sleep(int milliseconds)
Definition: Helper.cpp:278
Algorithm parameters.
int baseVCycles
Coarse MG solver v-cycles.
float colorPullFactor
Data pull factor.
float cgAccuracy
This flag specifies the accuracy cut-off to be used for CG.
BoundaryType
Boundary types.
Parameters()=default
Default initializer.
float normalConfidence
Normal confidence exponent.
BoundaryType boundary
Boundary type for the finite elements.
int fullDepth
The depth beyond which the octree will be adapted.
float normalConfidenceBias
Normal confidence bias exponent.
int iters
The number of solver iterations.
int baseDepth
Coarse MG solver depth.