ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
SurfaceReconstructionPoisson.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 <Logging.h>
9 
10 #include <Eigen/Dense>
11 #include <cfloat>
12 #include <cmath>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <iostream>
16 #include <list>
17 
18 #include "ecvHObjectCaster.h"
19 #include "ecvMesh.h"
20 #include "ecvPointCloud.h"
21 
22 // clang-format off
23 #ifdef _MSC_VER
24 #pragma warning(push)
25 #pragma warning(disable: 4701 4703 4245 4189)
26 // 4701: potentially uninitialized local variable
27 // 4703: potentially uninitialized local pointer variable
28 // 4245: signed/unsigned mismatch
29 // 4189: local variable is initialized but not referenced
30 #endif
31 #include "PoissonRecon/Src/PreProcessor.h"
32 #include "PoissonRecon/Src/MyMiscellany.h"
33 #include "PoissonRecon/Src/CmdLineParser.h"
34 #include "PoissonRecon/Src/FEMTree.h"
35 #include "PoissonRecon/Src/PPolynomial.h"
36 #include "PoissonRecon/Src/PointStreamData.h"
37 #ifdef _MSC_VER
38 #pragma warning(pop)
39 #endif
40 // clang-format on
41 
42 using namespace cloudViewer;
43 
44 namespace poisson {
45 
46 // The order of the B-Spline used to splat in data for color interpolation
47 static const int DATA_DEGREE = 0;
48 // The order of the B-Spline used to splat in the weights for density estimation
49 static const int WEIGHT_DEGREE = 2;
50 // The order of the B-Spline used to splat in the normals for constructing the
51 // Laplacian constraints
52 static const int NORMAL_DEGREE = 2;
53 // The default finite-element degree
54 static const int DEFAULT_FEM_DEGREE = 1;
55 // The default finite-element boundary type
56 static const BoundaryType DEFAULT_FEM_BOUNDARY = BOUNDARY_NEUMANN;
57 // The dimension of the system
58 static const int DIMENSION = 3;
59 
61 public:
62  CloudViewerData() : normal_(0, 0, 0), color_(0, 0, 0) {}
63  CloudViewerData(const Eigen::Vector3d& normal, const Eigen::Vector3d& color)
64  : normal_(normal), color_(color) {}
65 
66  CloudViewerData operator*(double s) const {
67  return CloudViewerData(s * normal_, s * color_);
68  }
69  CloudViewerData operator/(double s) const {
70  return CloudViewerData(normal_ / s, (1 / s) * color_);
71  }
73  normal_ += d.normal_;
74  color_ += d.color_;
75  return *this;
76  }
78  normal_ *= s;
79  color_ *= s;
80  return *this;
81  }
82 
83 public:
84  Eigen::Vector3d normal_;
85  Eigen::Vector3d color_;
86 };
87 
88 template <typename Real>
90  : public InputPointStreamWithData<Real, DIMENSION, CloudViewerData> {
91 public:
93  : pcd_(pcd), xform_(nullptr), current_(0) {}
94  void reset(void) { current_ = 0; }
95  bool nextPoint(Point<Real, 3>& p, CloudViewerData& d) {
96  if (current_ >= pcd_->size()) {
97  return false;
98  }
99  p.coords[0] = static_cast<Real>(pcd_->getEigenPoint(current_)(0));
100  p.coords[1] = static_cast<Real>(pcd_->getEigenPoint(current_)(1));
101  p.coords[2] = static_cast<Real>(pcd_->getEigenPoint(current_)(2));
102 
103  if (xform_ != nullptr) {
104  p = (*xform_) * p;
105  }
106 
107  if (pcd_->hasNormals()) {
108  d.normal_ = pcd_->getEigenNormal(current_);
109  } else {
110  d.normal_ = Eigen::Vector3d(0, 0, 0);
111  }
112 
113  if (pcd_->hasColors()) {
114  d.color_ = pcd_->getEigenColor(current_);
115  } else {
116  d.color_ = Eigen::Vector3d(0, 0, 0);
117  }
118 
119  current_++;
120  return true;
121  }
122 
123 public:
125  XForm<Real, 4>* xform_;
126  size_t current_;
127 };
128 
129 template <typename _Real>
131 public:
132  typedef _Real Real;
133 
134  CloudViewerVertex() : CloudViewerVertex(Point<Real, 3>(0, 0, 0)) {}
135  CloudViewerVertex(Point<Real, 3> point)
136  : point(point), normal_(0, 0, 0), color_(0, 0, 0), w_(0) {}
137 
139  point *= s;
140  normal_ *= s;
141  color_ *= s;
142  w_ *= s;
143  return *this;
144  }
145 
147  point += p.point;
148  normal_ += p.normal_;
149  color_ += p.color_;
150  w_ += p.w_;
151  return *this;
152  }
153 
155  point /= s;
156  normal_ /= s;
157  color_ /= s;
158  w_ /= s;
159  return *this;
160  }
161 
162 public:
163  // point can not have trailing _, because template methods assume that it is
164  // named this way
165  Point<Real, 3> point;
166  Eigen::Vector3d normal_;
167  Eigen::Vector3d color_;
168  double w_;
169 };
170 
171 template <unsigned int Dim, class Real>
173  FEMTree<Dim, Real>& tree;
174  double t;
175 
176  FEMTreeProfiler(FEMTree<Dim, Real>& t) : tree(t) {}
177  void start(void) {
178  t = Time(), FEMTree<Dim, Real>::ResetLocalMemoryUsage();
179  }
180  void dumpOutput(const char* header) const {
181  FEMTree<Dim, Real>::MemoryUsage();
182  if (header) {
183  utility::LogDebug("{} {} (s), {} (MB) / {} (MB) / {} (MB)", header,
184  Time() - t,
185  FEMTree<Dim, Real>::LocalMemoryUsage(),
186  FEMTree<Dim, Real>::MaxMemoryUsage(),
187  MemoryInfo::PeakMemoryUsageMB());
188  } else {
189  utility::LogDebug("{} (s), {} (MB) / {} (MB) / {} (MB)", Time() - t,
190  FEMTree<Dim, Real>::LocalMemoryUsage(),
191  FEMTree<Dim, Real>::MaxMemoryUsage(),
192  MemoryInfo::PeakMemoryUsageMB());
193  }
194  }
195 };
196 
197 template <class Real, unsigned int Dim>
198 XForm<Real, Dim + 1> GetBoundingBoxXForm(Point<Real, Dim> min,
199  Point<Real, Dim> max,
200  Real scaleFactor) {
201  Point<Real, Dim> center = (max + min) / 2;
202  Real scale = max[0] - min[0];
203  for (unsigned int d = 1; d < Dim; d++) {
204  scale = std::max<Real>(scale, max[d] - min[d]);
205  }
206  scale *= scaleFactor;
207  for (unsigned int i = 0; i < Dim; i++) {
208  center[i] -= scale / 2;
209  }
210  XForm<Real, Dim + 1> tXForm = XForm<Real, Dim + 1>::Identity(),
211  sXForm = XForm<Real, Dim + 1>::Identity();
212  for (unsigned int i = 0; i < Dim; i++) {
213  sXForm(i, i) = (Real)(1. / scale), tXForm(Dim, i) = -center[i];
214  }
215  return sXForm * tXForm;
216 }
217 
218 template <class Real, unsigned int Dim>
219 XForm<Real, Dim + 1> GetBoundingBoxXForm(Point<Real, Dim> min,
220  Point<Real, Dim> max,
221  Real width,
222  Real scaleFactor,
223  int& depth) {
224  // Get the target resolution (along the largest dimension)
225  Real resolution = (max[0] - min[0]) / width;
226  for (unsigned int d = 1; d < Dim; d++) {
227  resolution = std::max<Real>(resolution, (max[d] - min[d]) / width);
228  }
229  resolution *= scaleFactor;
230  depth = 0;
231  while ((1 << depth) < resolution) {
232  depth++;
233  }
234 
235  Point<Real, Dim> center = (max + min) / 2;
236  Real scale = (1 << depth) * width;
237 
238  for (unsigned int i = 0; i < Dim; i++) {
239  center[i] -= scale / 2;
240  }
241  XForm<Real, Dim + 1> tXForm = XForm<Real, Dim + 1>::Identity(),
242  sXForm = XForm<Real, Dim + 1>::Identity();
243  for (unsigned int i = 0; i < Dim; i++) {
244  sXForm(i, i) = (Real)(1. / scale), tXForm(Dim, i) = -center[i];
245  }
246  return sXForm * tXForm;
247 }
248 
249 template <class Real, unsigned int Dim>
250 XForm<Real, Dim + 1> GetPointXForm(InputPointStream<Real, Dim>& stream,
251  Real width,
252  Real scaleFactor,
253  int& depth) {
254  Point<Real, Dim> min, max;
255  stream.boundingBox(min, max);
256  return GetBoundingBoxXForm(min, max, width, scaleFactor, depth);
257 }
258 
259 template <class Real, unsigned int Dim>
260 XForm<Real, Dim + 1> GetPointXForm(InputPointStream<Real, Dim>& stream,
261  Real scaleFactor) {
262  Point<Real, Dim> min, max;
263  stream.boundingBox(min, max);
264  return GetBoundingBoxXForm(min, max, scaleFactor);
265 }
266 
267 template <unsigned int Dim, typename Real>
269  Real target, weight;
270  ConstraintDual(Real t, Real w) : target(t), weight(w) {}
271  CumulativeDerivativeValues<Real, Dim, 0> operator()(
272  const Point<Real, Dim>& p) const {
273  return CumulativeDerivativeValues<Real, Dim, 0>(target * weight);
274  };
275 };
276 
277 template <unsigned int Dim, typename Real>
278 struct SystemDual {
279  Real weight;
280  SystemDual(Real w) : weight(w) {}
281  CumulativeDerivativeValues<Real, Dim, 0> operator()(
282  const Point<Real, Dim>& p,
283  const CumulativeDerivativeValues<Real, Dim, 0>& dValues) const {
284  return dValues * weight;
285  };
286  CumulativeDerivativeValues<double, Dim, 0> operator()(
287  const Point<Real, Dim>& p,
288  const CumulativeDerivativeValues<double, Dim, 0>& dValues) const {
289  return dValues * weight;
290  };
291 };
292 
293 template <unsigned int Dim>
294 struct SystemDual<Dim, double> {
295  typedef double Real;
297  SystemDual(Real w) : weight(w) {}
298  CumulativeDerivativeValues<Real, Dim, 0> operator()(
299  const Point<Real, Dim>& p,
300  const CumulativeDerivativeValues<Real, Dim, 0>& dValues) const {
301  return dValues * weight;
302  };
303 };
304 
305 template <typename Vertex,
306  typename Real,
307  typename SetVertexFunction,
308  unsigned int... FEMSigs,
309  typename... SampleData>
311  float datax,
312  bool linear_fit,
313  UIntPack<FEMSigs...>,
314  std::tuple<SampleData...>,
315  FEMTree<sizeof...(FEMSigs), Real>& tree,
316  const DenseNodeData<Real, UIntPack<FEMSigs...>>& solution,
317  Real isoValue,
318  const std::vector<typename FEMTree<sizeof...(FEMSigs),
319  Real>::PointSample>* samples,
320  std::vector<CloudViewerData>* sampleData,
321  const typename FEMTree<sizeof...(FEMSigs),
322  Real>::template DensityEstimator<WEIGHT_DEGREE>*
323  density,
324  const SetVertexFunction& SetVertex,
325  XForm<Real, sizeof...(FEMSigs) + 1> iXForm,
326  std::shared_ptr<ccMesh>& out_mesh,
327  std::vector<double>& out_densities) {
328  static const int Dim = sizeof...(FEMSigs);
329  typedef UIntPack<FEMSigs...> Sigs;
330  static const unsigned int DataSig =
331  FEMDegreeAndBType<DATA_DEGREE, BOUNDARY_FREE>::Signature;
332  typedef typename FEMTree<Dim,
333  Real>::template DensityEstimator<WEIGHT_DEGREE>
334  DensityEstimator;
335 
336  FEMTreeProfiler<Dim, Real> profiler(tree);
337 
338  CoredMeshData<Vertex, node_index_type>* mesh;
339  mesh = new CoredVectorMeshData<Vertex, node_index_type>();
340 
341  bool non_manifold = true;
342  bool polygon_mesh = false;
343 
344  profiler.start();
345  typename IsoSurfaceExtractor<Dim, Real, Vertex>::IsoStats isoStats;
346  if (sampleData) {
347  SparseNodeData<ProjectiveData<CloudViewerData, Real>,
348  IsotropicUIntPack<Dim, DataSig>>
349  _sampleData =
350  tree.template setMultiDepthDataField<DataSig, false>(
351  *samples, *sampleData, (DensityEstimator*)NULL);
352  for (const RegularTreeNode<Dim, FEMTreeNodeData, depth_and_offset_type>*
353  n = tree.tree().nextNode();
354  n; n = tree.tree().nextNode(n)) {
355  ProjectiveData<CloudViewerData, Real>* clr = _sampleData(n);
356  if (clr) (*clr) *= (Real)pow(datax, tree.depth(n));
357  }
358  isoStats = IsoSurfaceExtractor<Dim, Real, Vertex>::template Extract<
360  Sigs(), UIntPack<WEIGHT_DEGREE>(), UIntPack<DataSig>(), tree,
361  density, &_sampleData, solution, isoValue, *mesh, SetVertex,
362  !linear_fit, !non_manifold, polygon_mesh, false);
363  } else {
364  isoStats = IsoSurfaceExtractor<Dim, Real, Vertex>::template Extract<
366  Sigs(), UIntPack<WEIGHT_DEGREE>(), UIntPack<DataSig>(), tree,
367  density, NULL, solution, isoValue, *mesh, SetVertex,
368  !linear_fit, !non_manifold, polygon_mesh, false);
369  }
370 
371  mesh->resetIterator();
372  out_densities.clear();
373  ccPointCloud* cloud =
374  ccHObjectCaster::ToPointCloud(out_mesh->getAssociatedCloud());
375  if (!cloud) {
377  "[poisson::ExtractMesh] ccMesh should set associated cloud "
378  "before using!");
379  }
380 
381  if (!cloud->reserveThePointsTable(
382  static_cast<unsigned>(mesh->outOfCorePointCount()))) {
384  "[poisson::ExtractMesh] reserve points failed, not enough "
385  "memory!");
386  return;
387  }
388 
389  if (!cloud->reserveTheNormsTable()) {
391  "[poisson::ExtractMesh] reserve normals failed, not enough "
392  "memory!");
393  return;
394  }
395 
396  if (!cloud->reserveTheRGBTable()) {
398  "[poisson::ExtractMesh] reserve rgbs failed, not enough "
399  "memory!");
400  return;
401  }
402 
403  for (size_t vidx = 0; vidx < mesh->outOfCorePointCount(); ++vidx) {
404  Vertex v;
405  mesh->nextOutOfCorePoint(v);
406  v.point = iXForm * v.point;
407  cloud->addEigenPoint(
408  Eigen::Vector3d(v.point[0], v.point[1], v.point[2]));
409 
410  cloud->addEigenNorm(v.normal_);
411  cloud->addEigenColor(v.color_);
412  out_densities.push_back(v.w_);
413  }
414 
415  if (!out_mesh->reserve(mesh->polygonCount())) {
417  "[poisson::ExtractMesh] reserve triangles failed, not enough "
418  "memory!");
419  return;
420  }
421 
422  for (size_t tidx = 0; tidx < mesh->polygonCount(); ++tidx) {
423  std::vector<CoredVertexIndex<node_index_type>> triangle;
424  mesh->nextPolygon(triangle);
425  if (triangle.size() != 3) {
426  utility::LogError("got polygon");
427  } else {
428  out_mesh->addTriangle(Eigen::Vector3i(
429  triangle[0].idx, triangle[1].idx, triangle[2].idx));
430  }
431  }
432 
433  delete mesh;
434 }
435 
436 template <class Real, typename... SampleData, unsigned int... FEMSigs>
437 void Execute(const ccPointCloud& pcd,
438  std::shared_ptr<ccMesh>& out_mesh,
439  std::vector<double>& out_densities,
440  int depth,
441  size_t width,
442  float scale,
443  bool linear_fit,
444  float point_weight,
445  float samples_per_node,
446  UIntPack<FEMSigs...>) {
447  static const int Dim = sizeof...(FEMSigs);
448  typedef UIntPack<FEMSigs...> Sigs;
449  typedef UIntPack<FEMSignature<FEMSigs>::Degree...> Degrees;
450  typedef UIntPack<FEMDegreeAndBType<
451  NORMAL_DEGREE, DerivativeBoundary<FEMSignature<FEMSigs>::BType,
452  1>::BType>::Signature...>
453  NormalSigs;
454  typedef typename FEMTree<Dim,
455  Real>::template DensityEstimator<WEIGHT_DEGREE>
456  DensityEstimator;
457  typedef typename FEMTree<Dim, Real>::template InterpolationInfo<Real, 0>
458  InterpolationInfo;
459 
460  XForm<Real, Dim + 1> xForm, iXForm;
461  xForm = XForm<Real, Dim + 1>::Identity();
462 
463  float datax = 32.f;
464  int base_depth = 0;
465  int base_v_cycles = 1;
466  float confidence = 0.f;
467  // float point_weight = 2.f * DEFAULT_FEM_DEGREE;
468  // float samples_per_node = 1.5f;
469  float confidence_bias = 0.f;
470  float cg_solver_accuracy = 1e-3f;
471  int full_depth = 5;
472  int iters = 8;
473  bool exact_interpolation = false;
474 
475  double startTime = Time();
476  Real isoValue = 0;
477 
478  FEMTree<Dim, Real> tree(MEMORY_ALLOCATOR_BLOCK_SIZE);
479  FEMTreeProfiler<Dim, Real> profiler(tree);
480 
481  size_t pointCount;
482 
483  Real pointWeightSum;
484  std::vector<typename FEMTree<Dim, Real>::PointSample> samples;
485  std::vector<CloudViewerData> sampleData;
486  DensityEstimator* density = NULL;
487  SparseNodeData<Point<Real, Dim>, NormalSigs>* normalInfo = NULL;
488  Real targetValue = (Real)0.5;
489 
490  // Read in the samples (and color data)
491  {
492  CloudViewerPointStream<Real> pointStream(&pcd);
493 
494  if (width > 0) {
495  xForm = GetPointXForm<Real, Dim>(pointStream, (Real)width,
496  (Real)(scale > 0 ? scale : 1.),
497  depth) *
498  xForm;
499  } else {
500  xForm = scale > 0 ? GetPointXForm<Real, Dim>(pointStream,
501  (Real)scale) *
502  xForm
503  : xForm;
504  }
505 
506  pointStream.xform_ = &xForm;
507 
508  {
509  auto ProcessDataWithConfidence = [&](const Point<Real, Dim>& p,
510  CloudViewerData& d) {
511  Real l = (Real)d.normal_.norm();
512  if (!l || l != l) return (Real)-1.;
513  return (Real)pow(l, confidence);
514  };
515  auto ProcessData = [](const Point<Real, Dim>& p,
516  CloudViewerData& d) {
517  Real l = (Real)d.normal_.norm();
518  if (!l || l != l) return (Real)-1.;
519  d.normal_ /= l;
520  return (Real)1.;
521  };
522  if (confidence > 0) {
523  pointCount = FEMTreeInitializer<Dim, Real>::template Initialize<
525  tree.spaceRoot(), pointStream, depth, samples,
526  sampleData, true, tree.nodeAllocators[0],
527  tree.initializer(), ProcessDataWithConfidence);
528  } else {
529  pointCount = FEMTreeInitializer<Dim, Real>::template Initialize<
530  CloudViewerData>(tree.spaceRoot(), pointStream, depth,
531  samples, sampleData, true,
532  tree.nodeAllocators[0],
533  tree.initializer(), ProcessData);
534  }
535  }
536  iXForm = xForm.inverse();
537 
538  utility::LogDebug("Input Points / Samples: {} / {}", pointCount,
539  samples.size());
540  }
541 
542  int kernelDepth = depth - 2;
543  if (kernelDepth < 0) {
545  "[CreateFromPointCloudPoisson] depth (={}) has to be >= 2",
546  depth);
547  }
548 
549  DenseNodeData<Real, Sigs> solution;
550  {
551  DenseNodeData<Real, Sigs> constraints;
552  InterpolationInfo* iInfo = NULL;
553  int solveDepth = depth;
554 
555  tree.resetNodeIndices();
556 
557  // Get the kernel density estimator
558  {
559  profiler.start();
560  density = tree.template setDensityEstimator<WEIGHT_DEGREE>(
561  samples, kernelDepth, samples_per_node, 1);
562  profiler.dumpOutput("# Got kernel density:");
563  }
564 
565  // Transform the Hermite samples into a vector field
566  {
567  profiler.start();
568  normalInfo = new SparseNodeData<Point<Real, Dim>, NormalSigs>();
569  std::function<bool(CloudViewerData, Point<Real, Dim>&)>
570  ConversionFunction =
571  [](CloudViewerData in, Point<Real, Dim>& out) {
572  // Point<Real, Dim> n = in.template data<0>();
573  Point<Real, Dim> n(in.normal_(0), in.normal_(1),
574  in.normal_(2));
575  Real l = (Real)Length(n);
576  // It is possible that the samples have non-zero
577  // normals but there are two co-located samples
578  // with negative normals...
579  if (!l) return false;
580  out = n / l;
581  return true;
582  };
583  std::function<bool(CloudViewerData, Point<Real, Dim>&, Real&)>
584  ConversionAndBiasFunction = [&](CloudViewerData in,
585  Point<Real, Dim>& out,
586  Real& bias) {
587  // Point<Real, Dim> n = in.template data<0>();
588  Point<Real, Dim> n(in.normal_(0), in.normal_(1),
589  in.normal_(2));
590  Real l = (Real)Length(n);
591  // It is possible that the samples have non-zero normals
592  // but there are two co-located samples with negative
593  // normals...
594  if (!l) return false;
595  out = n / l;
596  bias = (Real)(log(l) * confidence_bias /
597  log(1 << (Dim - 1)));
598  return true;
599  };
600  if (confidence_bias > 0) {
601  *normalInfo = tree.setDataField(
602  NormalSigs(), samples, sampleData, density,
603  pointWeightSum, ConversionAndBiasFunction);
604  } else {
605  *normalInfo = tree.setDataField(
606  NormalSigs(), samples, sampleData, density,
607  pointWeightSum, ConversionFunction);
608  }
609  ThreadPool::Parallel_for(0, normalInfo->size(),
610  [&](unsigned int, size_t i) {
611  (*normalInfo)[i] *= (Real)-1.;
612  });
613  profiler.dumpOutput("# Got normal field:");
614  utility::LogDebug("Point weight / Estimated Area: {:e} / {:e}",
615  pointWeightSum, pointCount * pointWeightSum);
616  }
617 
618  // Trim the tree and prepare for multigrid
619  {
620  profiler.start();
621  constexpr int MAX_DEGREE = NORMAL_DEGREE > Degrees::Max()
622  ? NORMAL_DEGREE
623  : Degrees::Max();
624  tree.template finalizeForMultigrid<MAX_DEGREE>(
625  full_depth,
626  typename FEMTree<Dim, Real>::template HasNormalDataFunctor<
627  NormalSigs>(*normalInfo),
628  normalInfo, density);
629  profiler.dumpOutput("# Finalized tree:");
630  }
631 
632  // Add the FEM constraints
633  {
634  profiler.start();
635  constraints = tree.initDenseNodeData(Sigs());
636  typename FEMIntegrator::template Constraint<
637  Sigs, IsotropicUIntPack<Dim, 1>, NormalSigs,
638  IsotropicUIntPack<Dim, 0>, Dim>
639  F;
640  unsigned int derivatives2[Dim];
641  for (unsigned int d = 0; d < Dim; d++) derivatives2[d] = 0;
642  typedef IsotropicUIntPack<Dim, 1> Derivatives1;
643  typedef IsotropicUIntPack<Dim, 0> Derivatives2;
644  for (unsigned int d = 0; d < Dim; d++) {
645  unsigned int derivatives1[Dim];
646  for (unsigned int dd = 0; dd < Dim; dd++)
647  derivatives1[dd] = dd == d ? 1 : 0;
648  F.weights[d]
651  derivatives2)] = 1;
652  }
653  tree.addFEMConstraints(F, *normalInfo, constraints, solveDepth);
654  profiler.dumpOutput("# Set FEM constraints:");
655  }
656 
657  // Free up the normal info
658  delete normalInfo, normalInfo = NULL;
659 
660  // Add the interpolation constraints
661  if (point_weight > 0) {
662  profiler.start();
663  if (exact_interpolation) {
664  iInfo = FEMTree<Dim, Real>::
665  template InitializeExactPointInterpolationInfo<Real, 0>(
666  tree, samples,
668  targetValue,
669  (Real)point_weight * pointWeightSum),
670  SystemDual<Dim, Real>((Real)point_weight *
671  pointWeightSum),
672  true, false);
673  } else {
674  iInfo = FEMTree<Dim, Real>::
675  template InitializeApproximatePointInterpolationInfo<
676  Real, 0>(
677  tree, samples,
679  targetValue,
680  (Real)point_weight * pointWeightSum),
681  SystemDual<Dim, Real>((Real)point_weight *
682  pointWeightSum),
683  true, 1);
684  }
685  tree.addInterpolationConstraints(constraints, solveDepth, *iInfo);
686  profiler.dumpOutput("#Set point constraints:");
687  }
688 
690  "Leaf Nodes / Active Nodes / Ghost Nodes: {} / {} / {}",
691  tree.leaves(), tree.nodes(), tree.ghostNodes());
692  utility::LogDebug("Memory Usage: {:.3f} MB",
693  float(MemoryInfo::Usage()) / (1 << 20));
694 
695  // Solve the linear system
696  {
697  profiler.start();
698  typename FEMTree<Dim, Real>::SolverInfo sInfo;
699  sInfo.cgDepth = 0, sInfo.cascadic = true, sInfo.vCycles = 1,
700  sInfo.iters = iters, sInfo.cgAccuracy = cg_solver_accuracy,
701  sInfo.verbose = utility::GetVerbosityLevel() ==
703  sInfo.showResidual = utility::GetVerbosityLevel() ==
705  sInfo.showGlobalResidual = SHOW_GLOBAL_RESIDUAL_NONE,
706  sInfo.sliceBlockSize = 1;
707  sInfo.baseDepth = base_depth, sInfo.baseVCycles = base_v_cycles;
708  typename FEMIntegrator::template System<Sigs,
709  IsotropicUIntPack<Dim, 1>>
710  F({0., 1.});
711  solution = tree.solveSystem(Sigs(), F, constraints, solveDepth,
712  sInfo, iInfo);
713  profiler.dumpOutput("# Linear system solved:");
714  if (iInfo) {
715  delete iInfo;
716  iInfo = nullptr;
717  }
718  }
719  }
720 
721  {
722  profiler.start();
723  double valueSum = 0, weightSum = 0;
724  typename FEMTree<Dim, Real>::template MultiThreadedEvaluator<Sigs, 0>
725  evaluator(&tree, solution);
726  std::vector<double> valueSums(ThreadPool::NumThreads(), 0),
727  weightSums(ThreadPool::NumThreads(), 0);
728  ThreadPool::Parallel_for(
729  0, samples.size(), [&](unsigned int thread, size_t j) {
730  ProjectiveData<Point<Real, Dim>, Real>& sample =
731  samples[j].sample;
732  Real w = sample.weight;
733  if (w > 0)
734  weightSums[thread] += w,
735  valueSums[thread] +=
736  evaluator.values(sample.data / sample.weight,
737  thread, samples[j].node)[0] *
738  w;
739  });
740  for (size_t t = 0; t < valueSums.size(); t++)
741  valueSum += valueSums[t], weightSum += weightSums[t];
742  isoValue = (Real)(valueSum / weightSum);
743  profiler.dumpOutput("Got average:");
744  utility::LogDebug("Iso-Value: {:e} = {:e} / {:e}", isoValue, valueSum,
745  weightSum);
746  }
747 
748  auto SetVertex = [](CloudViewerVertex<Real>& v, Point<Real, Dim> p, Real w,
749  CloudViewerData d) {
750  v.point = p;
751  v.normal_ = d.normal_;
752  v.color_ = d.color_;
753  v.w_ = w;
754  };
755  ExtractMesh<CloudViewerVertex<Real>, Real>(
756  datax, linear_fit, UIntPack<FEMSigs...>(),
757  std::tuple<SampleData...>(), tree, solution, isoValue, &samples,
758  &sampleData, density, SetVertex, iXForm, out_mesh, out_densities);
759 
760  if (density) delete density, density = NULL;
761  utility::LogDebug("# Total Solve: {:9.1f} (s), {:9.1f} (MB)",
762  Time() - startTime, FEMTree<Dim, Real>::MaxMemoryUsage());
763 }
764 
765 } // namespace poisson
766 
767 std::tuple<std::shared_ptr<ccMesh>, std::vector<double>>
769  size_t depth,
770  size_t width,
771  float scale,
772  bool linear_fit,
773  float point_weight,
774  float samples_per_node,
775  int boundary_type,
776  int n_threads) {
777  if (!pcd.hasNormals()) {
778  utility::LogError("[CreateFromPointCloudPoisson] pcd has no normals");
779  }
780 
781  if (n_threads <= 0) {
782  n_threads = (int)std::thread::hardware_concurrency();
783  }
784 
785 #ifdef _OPENMP
786  ThreadPool::Init((ThreadPool::ParallelType)(int)ThreadPool::OPEN_MP,
787  n_threads);
788 #else
789  ThreadPool::Init((ThreadPool::ParallelType)(int)ThreadPool::THREAD_POOL,
790  n_threads);
791 #endif
792 
793  auto mesh = std::make_shared<ccMesh>();
794  if (!mesh->CreateInternalCloud()) {
796  "[CreateFromPointCloudPoisson] creating internal cloud "
797  "failed!");
798  }
799 
800  std::vector<double> densities;
801  const BoundaryType BType = (BoundaryType)boundary_type;
802  switch (BType) {
803  case BoundaryType::BOUNDARY_FREE:
804  typedef IsotropicUIntPack<
806  FEMDegreeAndBType<poisson::DEFAULT_FEM_DEGREE,
807  BoundaryType::BOUNDARY_FREE>::Signature>
808  FEMSigsFree;
809  poisson::Execute<float>(
810  pcd, mesh, densities, static_cast<int>(depth), width, scale,
811  linear_fit, point_weight, samples_per_node, FEMSigsFree());
812  break;
813  case BoundaryType::BOUNDARY_DIRICHLET:
814  typedef IsotropicUIntPack<
816  FEMDegreeAndBType<
818  BoundaryType::BOUNDARY_DIRICHLET>::Signature>
819  FEMSigsDirichlet;
820  poisson::Execute<float>(pcd, mesh, densities,
821  static_cast<int>(depth), width, scale,
822  linear_fit, point_weight, samples_per_node,
823  FEMSigsDirichlet());
824  break;
825  case BoundaryType::BOUNDARY_NEUMANN:
826  typedef IsotropicUIntPack<
828  FEMDegreeAndBType<
830  BoundaryType::BOUNDARY_NEUMANN>::Signature>
831  FEMSigsNeumann;
832  poisson::Execute<float>(pcd, mesh, densities,
833  static_cast<int>(depth), width, scale,
834  linear_fit, point_weight, samples_per_node,
835  FEMSigsNeumann());
836  break;
837  case BoundaryType::BOUNDARY_COUNT:
838  typedef IsotropicUIntPack<
840  FEMDegreeAndBType<poisson::DEFAULT_FEM_DEGREE,
841  BoundaryType::BOUNDARY_COUNT>::Signature>
842  FEMSigsCount;
843  poisson::Execute<float>(
844  pcd, mesh, densities, static_cast<int>(depth), width, scale,
845  linear_fit, point_weight, samples_per_node, FEMSigsCount());
846  break;
847  default:
848  assert(false);
849  break;
850  }
851 
852  ThreadPool::Terminate();
853 
854  return std::make_tuple(mesh, densities);
855 }
double normal[3]
int width
math::float4 color
static bool Execute(PointStream< Real > &pointStream, PoissonReconLib::IMesh< Real > &out_mesh, const PoissonReconLib::Parameters &params, UIntPack< FEMSigs... >)
void ExtractMesh(const PoissonReconLib::Parameters &params, UIntPack< FEMSigs... >, std::tuple< SampleData... >, FEMTree< sizeof...(FEMSigs), Real > &tree, const DenseNodeData< Real, UIntPack< FEMSigs... >> &solution, Real isoValue, const std::vector< typename FEMTree< sizeof...(FEMSigs), Real >::PointSample > *samples, std::vector< PointData< Real >> *sampleData, const typename FEMTree< sizeof...(FEMSigs), Real >::template DensityEstimator< WEIGHT_DEGREE > *density, const SetVertexFunction &SetVertex, XForm< Real, sizeof...(FEMSigs)+1 > iXForm, PoissonReconLib::IMesh< Real > &out_mesh)
#define NULL
Point< Real, 3 > point
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
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...
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addEigenNorm(const Eigen::Vector3d &N)
void addEigenColor(const Eigen::Vector3d &color)
bool hasNormals() const override
Returns whether normals are enabled or not.
bool reserveTheNormsTable()
Reserves memory to store the compressed normals.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
void addEigenPoint(const Eigen::Vector3d &point)
size_t NumThreads() const
Definition: threading.h:296
CloudViewerData operator*(double s) const
CloudViewerData(const Eigen::Vector3d &normal, const Eigen::Vector3d &color)
CloudViewerData operator/(double s) const
CloudViewerData & operator+=(const CloudViewerData &d)
CloudViewerData & operator*=(double s)
bool nextPoint(Point< Real, 3 > &p, CloudViewerData &d)
CloudViewerVertex & operator*=(Real s)
CloudViewerVertex & operator/=(Real s)
CloudViewerVertex & operator+=(const CloudViewerVertex &p)
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
void Extract(const std::string &file_path, const std::string &extract_dir)
Function to extract compressed files.
Definition: Extract.cpp:33
VerbosityLevel GetVerbosityLevel()
Get global verbosity level of CloudViewer.
Definition: Logging.cpp:93
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
Eigen::MatrixXd::Index Index
Definition: knncpp.h:26
static const int DEFAULT_FEM_DEGREE
XForm< Real, Dim+1 > GetBoundingBoxXForm(Point< Real, Dim > min, Point< Real, Dim > max, Real width, Real scaleFactor, int &depth)
static const int WEIGHT_DEGREE
static const int DATA_DEGREE
static const int DIMENSION
static const int NORMAL_DEGREE
static const BoundaryType DEFAULT_FEM_BOUNDARY
XForm< Real, Dim+1 > GetPointXForm(InputPointStream< Real, Dim > &stream, Real scaleFactor)
CumulativeDerivativeValues< Real, Dim, 0 > operator()(const Point< Real, Dim > &p) const
void dumpOutput(const char *header) const
FEMTreeProfiler(FEMTree< Dim, Real > &t)
CumulativeDerivativeValues< Real, Dim, 0 > operator()(const Point< Real, Dim > &p, const CumulativeDerivativeValues< Real, Dim, 0 > &dValues) const
CumulativeDerivativeValues< double, Dim, 0 > operator()(const Point< Real, Dim > &p, const CumulativeDerivativeValues< double, Dim, 0 > &dValues) const
CumulativeDerivativeValues< Real, Dim, 0 > operator()(const Point< Real, Dim > &p, const CumulativeDerivativeValues< Real, Dim, 0 > &dValues) const