ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
GeometricalAnalysisTools.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 
9 
10 // local
11 #include <CVMath.h>
15 #include <ReferenceCloud.h>
16 #include <ScalarField.h>
17 #include <ScalarFieldTools.h>
18 
19 // system
20 #include <algorithm>
21 #include <random>
22 
23 using namespace cloudViewer;
24 
25 // volume of a unit sphere
26 static double s_UnitSphereVolume = 4.0 * M_PI / 3.0;
27 
31  int subOption,
33  PointCoordinateType kernelRadius,
34  const CCVector3* roughnessUpDir /*=nullptr*/,
35  GenericProgressCallback* progressCb /*=nullptr*/,
36  DgmOctree* inputOctree /*=nullptr*/) {
37  if (!cloud) {
38  // invalid input cloud
39  return InvalidInput;
40  }
41 
42  unsigned numberOfPoints = cloud->size();
43 
44  std::string label;
45  switch (c) {
46  case Feature:
47  if (subOption == 0) return InvalidInput;
48  if (numberOfPoints < 4) return NotEnoughPoints;
49  label = "Feature computation";
50  break;
51  case Curvature:
52  if (subOption == 0) return InvalidInput;
53  if (numberOfPoints < 5) return NotEnoughPoints;
54  label = "Curvature computation";
55  break;
56  case LocalDensity:
57  if (subOption == 0) return InvalidInput;
58  if (numberOfPoints < 3) return NotEnoughPoints;
59  label = "Density computation";
60  break;
61  case ApproxLocalDensity:
62  if (subOption == 0) return InvalidInput;
63  // special case (can't be handled in the same way as the other
64  // characteristics)
65  return ComputeLocalDensityApprox(cloud,
66  static_cast<Density>(subOption),
67  progressCb, inputOctree);
68  case Roughness:
69  if (numberOfPoints < 4) return NotEnoughPoints;
70  label = "Roughness computation";
71  break;
72  case MomentOrder1:
73  if (numberOfPoints < 4) return NotEnoughPoints;
74  label = "1st order moment computation";
75  break;
76  default:
77  assert(false);
79  }
80 
81  DgmOctree* octree = inputOctree;
82  if (!octree) {
83  // try to build the octree if none was provided
84  octree = new DgmOctree(cloud);
85  if (octree->build(progressCb) < 1) {
86  delete octree;
88  }
89  }
90 
91  // enable a scalar field for storing the characteristic values
92  cloud->enableScalarField();
93 
94  // find the best octree leve to perform the computation
95  unsigned char level =
97  kernelRadius);
98 
99  // parameters
100  void* additionalParameters[] = {
101  static_cast<void*>(&c), static_cast<void*>(&subOption),
102  static_cast<void*>(&kernelRadius),
103  static_cast<void*>(const_cast<CCVector3*>(roughnessUpDir))};
104 
106 
108  level, &ComputeGeomCharacteristicAtLevel, additionalParameters,
109  true, progressCb, label.c_str()) == 0) {
110  // something went wrong
112  }
113 
114  if (octree && !inputOctree) {
115  delete octree;
116  octree = nullptr;
117  }
118 
119  // we need to finish the work for the Density computation
120  if (result == NoError && c == LocalDensity &&
121  subOption != DENSITY_KNN // no need to do anything for the 'KNN' mode)
122  ) {
123  // compute the right dimensional coef based on the expected output
124  ScalarType dimensionalCoef = 1.0f;
125  switch (static_cast<Density>(subOption)) {
126  case DENSITY_KNN:
127  assert(false);
128  dimensionalCoef = 1.0f;
129  break;
130  case DENSITY_2D:
131  dimensionalCoef =
132  static_cast<ScalarType>(M_PI * pow(kernelRadius, 2.0));
133  break;
134  case DENSITY_3D:
135  dimensionalCoef = static_cast<ScalarType>(
136  s_UnitSphereVolume * pow(kernelRadius, 3.0));
137  break;
138  default:
139  assert(false);
141  break;
142  }
143 
144  for (unsigned i = 0; i < numberOfPoints; ++i) {
145  ScalarType s = cloud->getPointScalarValue(i);
146  s /= dimensionalCoef;
147  cloud->setPointScalarValue(i, s);
148  }
149  }
150 
151  return result;
152 }
153 
155  const DgmOctree::octreeCell& cell,
156  void** additionalParameters,
157  NormalizedProgress* nProgress /*=0*/) {
158  // parameters
160  *static_cast<GeomCharacteristic*>(additionalParameters[0]);
161  int subOption = *static_cast<Neighbourhood::CurvatureType*>(
162  additionalParameters[1]);
163  PointCoordinateType radius =
164  *static_cast<PointCoordinateType*>(additionalParameters[2]);
165  const CCVector3* roughnessUpDir =
166  static_cast<const CCVector3*>(additionalParameters[3]);
167 
168  // structure for nearest neighbors search
170  nNSS.level = cell.level;
171  nNSS.prepare(radius, cell.parentOctree->getCellSize(nNSS.level));
172  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
173  true);
174  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
175  nNSS.cellCenter);
176 
177  unsigned n = cell.points->size(); // number of points in the current cell
178 
179  // we already know some of the neighbours: the points in the current cell!
180  {
181  try {
182  nNSS.pointsInNeighbourhood.resize(n);
183  } catch (const std::bad_alloc&) {
184  // out of memory
185  return false;
186  }
187 
188  DgmOctree::NeighboursSet::iterator it =
189  nNSS.pointsInNeighbourhood.begin();
190  for (unsigned i = 0; i < n; ++i, ++it) {
191  it->point = cell.points->getPointPersistentPtr(i);
192  it->pointIndex = cell.points->getPointGlobalIndex(i);
193  }
194  }
196 
197  // for each point in the cell
198  for (unsigned i = 0; i < n; ++i) {
199  cell.points->getPoint(i, nNSS.queryPoint);
200 
201  // look for neighbors in a sphere
202  // warning: there may be more points at the end of
203  // nNSS.pointsInNeighbourhood than the actual nearest neighbors
204  // (neighborCount)!
205  unsigned neighborCount =
207  nNSS, radius, false);
208 
209  ScalarType value = NAN_VALUE;
210 
211  switch (c) {
212  case Feature:
213  if (neighborCount > 3) {
214  DgmOctreeReferenceCloud neighboursCloud(
215  &nNSS.pointsInNeighbourhood, neighborCount);
216  Neighbourhood Z(&neighboursCloud);
217  value = static_cast<ScalarType>(Z.computeFeature(
218  static_cast<Neighbourhood::GeomFeature>(
219  subOption)));
220  }
221  break;
222 
223  case Curvature:
224  if (neighborCount > 5) {
225  DgmOctreeReferenceCloud neighboursCloud(
226  &nNSS.pointsInNeighbourhood, neighborCount);
227  Neighbourhood Z(&neighboursCloud);
228  value = Z.computeCurvature(
229  nNSS.queryPoint,
230  static_cast<Neighbourhood::CurvatureType>(
231  subOption));
232  }
233  break;
234 
235  case LocalDensity: {
236  value = static_cast<ScalarType>(neighborCount);
237  } break;
238 
239  case Roughness:
240  if (neighborCount > 3) {
241  // find the query point in the nearest neighbors set and
242  // place it at the end
243  const unsigned globalIndex =
244  cell.points->getPointGlobalIndex(i);
245  unsigned localIndex = 0;
246  while (localIndex < neighborCount &&
247  nNSS.pointsInNeighbourhood[localIndex].pointIndex !=
248  globalIndex) {
249  ++localIndex;
250  }
251  // the query point should be in the nearest neighbors set!
252  assert(localIndex < neighborCount);
253  if (localIndex + 1 <
254  neighborCount) // no need to swap with another point if
255  // it's already at the end!
256  {
257  std::swap(
258  nNSS.pointsInNeighbourhood[localIndex],
259  nNSS.pointsInNeighbourhood[neighborCount - 1]);
260  }
261 
262  DgmOctreeReferenceCloud neighboursCloud(
263  &nNSS.pointsInNeighbourhood,
264  neighborCount - 1); // we don't take the query
265  // point into account!
266  Neighbourhood Z(&neighboursCloud);
267  value = Z.computeRoughness(nNSS.queryPoint, roughnessUpDir);
268 
269  // swap the points back to their original position (DGM: not
270  // necessary in this case) if (localIndex+1 < neighborCount)
271  //{
272  // std::swap(nNSS.pointsInNeighbourhood[localIndex],nNSS.pointsInNeighbourhood[neighborCount-1]);
273  // }
274  }
275  break;
276 
277  case MomentOrder1: {
278  DgmOctreeReferenceCloud neighboursCloud(
279  &nNSS.pointsInNeighbourhood, neighborCount);
280  Neighbourhood Z(&neighboursCloud);
281  value = Z.computeMomentOrder1(nNSS.queryPoint);
282  } break;
283 
284  default:
285  assert(false);
286  return false;
287  }
288 
289  cell.points->setPointScalarValue(i, value);
290 
291  if (nProgress && !nProgress->oneStep()) {
292  return false;
293  }
294  }
295 
296  return true;
297 }
298 
302  double minDistanceBetweenPoints /*=1.0e-12*/,
303  GenericProgressCallback* progressCb /*=0*/,
304  DgmOctree* inputOctree /*=0*/) {
305  if (!cloud) return InvalidInput;
306 
307  unsigned numberOfPoints = cloud->size();
308  if (numberOfPoints <= 1) return NotEnoughPoints;
309 
310  DgmOctree* theOctree = inputOctree;
311  if (!theOctree) {
312  theOctree = new DgmOctree(cloud);
313  if (theOctree->build(progressCb) < 1) {
314  delete theOctree;
316  }
317  }
318 
319  cloud->enableScalarField();
320  // set all flags to 0 by default
322 
323  unsigned char level =
325  static_cast<PointCoordinateType>(minDistanceBetweenPoints));
326 
327  // parameters
328  void* additionalParameters[1] = {
329  static_cast<void*>(&minDistanceBetweenPoints)};
330 
332 
333  if (theOctree->executeFunctionForAllCellsAtLevel(
334  level, &FlagDuplicatePointsInACellAtLevel, additionalParameters,
335  false, // doesn't work in parallel!
336  progressCb, "Flag duplicate points") == 0) {
337  // something went wrong
339  }
340 
341  if (!inputOctree) {
342  delete theOctree;
343  theOctree = nullptr;
344  }
345 
346  return result;
347 }
348 
349 //"PER-CELL" METHOD: FLAG DUPLICATE POINTS
350 // ADDITIONAL PARAMETERS (1):
351 // [0] -> (double*) maxSquareDistBetweenPoints: max square distance between
352 // points
354  const DgmOctree::octreeCell& cell,
355  void** additionalParameters,
356  NormalizedProgress* nProgress /*=0*/) {
357  // parameter(s)
358  double minDistBetweenPoints =
359  *static_cast<double*>(additionalParameters[0]);
360 
361  // structure for nearest neighbors search
363  nNSS.level = cell.level;
364  nNSS.prepare(static_cast<PointCoordinateType>(minDistBetweenPoints),
365  cell.parentOctree->getCellSize(nNSS.level));
366  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
367  true);
368  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
369  nNSS.cellCenter);
370 
371  unsigned n = cell.points->size(); // number of points in the current cell
372 
373  // for each point in the cell
374  for (unsigned i = 0; i < n; ++i) {
375  // don't process points already flagged as 'duplicate'
376  if (cell.points->getPointScalarValue(i) == 0) {
377  cell.points->getPoint(i, nNSS.queryPoint);
378 
379  // look for neighbors in a sphere
380  // warning: there may be more points at the end of
381  // nNSS.pointsInNeighbourhood than the actual nearest neighbors
382  // (neighborCount)!
383  unsigned neighborCount =
385  nNSS, minDistBetweenPoints, false);
386  if (neighborCount > 1) // the point itself lies in the neighborhood
387  {
388  unsigned iIndex = cell.points->getPointGlobalIndex(i);
389  for (unsigned j = 0; j < neighborCount; ++j) {
390  if (nNSS.pointsInNeighbourhood[j].pointIndex != iIndex) {
391  // flag this point as 'duplicate'
393  nNSS.pointsInNeighbourhood[j].pointIndex,
394  static_cast<ScalarType>(1));
395  }
396  }
397  }
398  }
399 
400  if (nProgress && !nProgress->oneStep()) {
401  return false;
402  }
403  }
404 
405  return true;
406 }
407 
411  Density densityType,
412  GenericProgressCallback* progressCb /*=0*/,
413  DgmOctree* inputOctree /*=0*/) {
414  if (!cloud) return InvalidInput;
415 
416  unsigned numberOfPoints = cloud->size();
417  if (numberOfPoints < 3) return NotEnoughPoints;
418 
419  DgmOctree* theOctree = inputOctree;
420  if (!theOctree) {
421  theOctree = new DgmOctree(cloud);
422  if (theOctree->build(progressCb) < 1) {
423  delete theOctree;
425  }
426  }
427 
428  cloud->enableScalarField();
429 
430  // determine best octree level to perform the computation
431  unsigned char level = theOctree->findBestLevelForAGivenPopulationPerCell(3);
432 
433  // parameters
434  void* additionalParameters[] = {static_cast<void*>(&densityType)};
435 
437 
438  if (theOctree->executeFunctionForAllCellsAtLevel(
440  additionalParameters, true, progressCb,
441  "Approximate Local Density Computation") == 0) {
442  // something went wrong
444  }
445 
446  if (!inputOctree) {
447  delete theOctree;
448  theOctree = nullptr;
449  }
450 
451  return result;
452 }
453 
454 //"PER-CELL" METHOD: APPROXIMATE LOCAL DENSITY
455 // ADDITIONAL PARAMETERS (0): NONE
457  const DgmOctree::octreeCell& cell,
458  void** additionalParameters,
459  NormalizedProgress* nProgress /*=0*/) {
460  // extract additional parameter(s)
461  Density densityType = *static_cast<Density*>(additionalParameters[0]);
462 
464  nNSS.level = cell.level;
466  nNSS.minNumberOfNeighbors = 2;
467  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
468  true);
469  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
470  nNSS.cellCenter);
471 
472  unsigned n = cell.points->size();
473  for (unsigned i = 0; i < n; ++i) {
474  cell.points->getPoint(i, nNSS.queryPoint);
475 
476  // the first point is always the point itself!
478  double R2 = nNSS.pointsInNeighbourhood[1].squareDistd;
479 
480  ScalarType density = NAN_VALUE;
482  switch (densityType) {
483  case DENSITY_KNN: {
484  // we return in fact the (inverse) distance to the
485  // nearest neighbor
486  density = static_cast<ScalarType>(1.0 / sqrt(R2));
487  } break;
488  case DENSITY_2D: {
489  // circle area (2D approximation)
490  double circleArea = M_PI * R2;
491  density = static_cast<ScalarType>(1.0 / circleArea);
492  } break;
493  case DENSITY_3D: {
494  double sphereVolume =
495  s_UnitSphereVolume * R2 * sqrt(R2);
496  density = static_cast<ScalarType>(1.0 / sphereVolume);
497  } break;
498  default:
499  assert(false);
500  break;
501  }
502  }
503  cell.points->setPointScalarValue(i, density);
504  } else {
505  // shouldn't happen! Apart if the cloud has only one point...
507  }
508 
509  if (nProgress && !nProgress->oneStep()) {
510  return false;
511  }
512  }
513 
514  return true;
515 }
516 
518  const GenericCloud* cloud) {
519  assert(cloud);
520 
521  unsigned count = cloud->size();
522  if (count == 0) return CCVector3();
523 
524  CCVector3d sum(0, 0, 0);
525 
526  GenericCloud* points = const_cast<GenericCloud*>(cloud);
527  points->placeIteratorAtBeginning();
528  const CCVector3* P = nullptr;
529  while ((P = points->getNextPoint())) {
530  sum += CCVector3d::fromArray(P->u);
531  }
532 
533  sum /= static_cast<double>(count);
534  return CCVector3::fromArray(sum.u);
535 }
536 
538  GenericCloud* cloud, ScalarField* weights) {
539  assert(cloud && weights);
540 
541  unsigned count = cloud->size();
542  if (count == 0 || !weights || weights->currentSize() < count)
543  return CCVector3();
544 
545  CCVector3d sum(0, 0, 0);
546 
547  cloud->placeIteratorAtBeginning();
548  double wSum = 0;
549  for (unsigned i = 0; i < count; ++i) {
550  const CCVector3* P = cloud->getNextPoint();
551  ScalarType w = weights->getValue(i);
552  if (!ScalarField::ValidValue(w)) continue;
553  sum += CCVector3d::fromArray(P->u) * std::abs(w);
554  wSum += w;
555  }
556 
557  if (wSum != 0) sum /= wSum;
558 
559  return CCVector3::fromArray(sum.u);
560 }
561 
563  const GenericCloud* cloud, const PointCoordinateType* _gravityCenter) {
564  assert(cloud);
565  unsigned n = (cloud ? cloud->size() : 0);
566  if (n == 0) return cloudViewer::SquareMatrixd();
567 
568  cloudViewer::SquareMatrixd covMat(3);
569  covMat.clear();
570 
571  // gravity center
572  CCVector3 G = (_gravityCenter ? CCVector3(_gravityCenter)
573  : ComputeGravityCenter(cloud));
574 
575  // cross sums (we use doubles to avoid overflow)
576  double mXX = 0;
577  double mYY = 0;
578  double mZZ = 0;
579  double mXY = 0;
580  double mXZ = 0;
581  double mYZ = 0;
582 
583  GenericCloud* points = const_cast<GenericCloud*>(cloud);
584  points->placeIteratorAtBeginning();
585  for (unsigned i = 0; i < n; ++i) {
586  const CCVector3* Q = points->getNextPoint();
587 
588  CCVector3 P = *Q - G;
589  mXX += static_cast<double>(P.x * P.x);
590  mYY += static_cast<double>(P.y * P.y);
591  mZZ += static_cast<double>(P.z * P.z);
592  mXY += static_cast<double>(P.x * P.y);
593  mXZ += static_cast<double>(P.x * P.z);
594  mYZ += static_cast<double>(P.y * P.z);
595  }
596 
597  covMat.m_values[0][0] = mXX / static_cast<double>(n);
598  covMat.m_values[1][1] = mYY / static_cast<double>(n);
599  covMat.m_values[2][2] = mZZ / static_cast<double>(n);
600  covMat.m_values[1][0] = covMat.m_values[0][1] =
601  mXY / static_cast<double>(n);
602  covMat.m_values[2][0] = covMat.m_values[0][2] =
603  mXZ / static_cast<double>(n);
604  covMat.m_values[2][1] = covMat.m_values[1][2] =
605  mYZ / static_cast<double>(n);
606 
607  return covMat;
608 }
609 
612  GenericCloud* Q,
613  const CCVector3& Gp,
614  const CCVector3& Gq) {
615  assert(P && Q);
616  assert(Q->size() == P->size());
617 
618  // shortcuts to output matrix lines
619  cloudViewer::SquareMatrixd covMat(3);
620  double* l1 = covMat.row(0);
621  double* l2 = covMat.row(1);
622  double* l3 = covMat.row(2);
623 
626 
627  // sums
628  unsigned count = P->size();
629  for (unsigned i = 0; i < count; i++) {
630  CCVector3 Pt = *P->getNextPoint() - Gp;
631  CCVector3 Qt = *Q->getNextPoint() - Gq;
632 
633  l1[0] += Pt.x * Qt.x;
634  l1[1] += Pt.x * Qt.y;
635  l1[2] += Pt.x * Qt.z;
636  l2[0] += Pt.y * Qt.x;
637  l2[1] += Pt.y * Qt.y;
638  l2[2] += Pt.y * Qt.z;
639  l3[0] += Pt.z * Qt.x;
640  l3[1] += Pt.z * Qt.y;
641  l3[2] += Pt.z * Qt.z;
642  }
643 
644  covMat.scale(1.0 / static_cast<double>(count));
645 
646  return covMat;
647 }
648 
651  GenericCloud* P, // data
652  GenericCloud* Q, // model
653  const CCVector3& Gp,
654  const CCVector3& Gq,
655  ScalarField* coupleWeights /*=0*/) {
656  assert(P && Q);
657  assert(Q->size() == P->size());
658  assert(coupleWeights);
659  assert(coupleWeights->currentSize() == P->size());
660 
661  // shortcuts to output matrix lines
662  cloudViewer::SquareMatrixd covMat(3);
663  double* r1 = covMat.row(0);
664  double* r2 = covMat.row(1);
665  double* r3 = covMat.row(2);
666 
669 
670  // sums
671  unsigned count = P->size();
672  double wSum = 0.0; // we will normalize by the sum
673  for (unsigned i = 0; i < count; i++) {
674  CCVector3d Pt = CCVector3d::fromArray((*P->getNextPoint() - Gp).u);
675  CCVector3 Qt = *Q->getNextPoint() - Gq;
676 
677  // Weighting scheme for cross-covariance is inspired from
678  // https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Weighted_sample_covariance
679  double wi = 1.0;
680  if (coupleWeights) {
681  ScalarType w = coupleWeights->getValue(i);
682  if (!ScalarField::ValidValue(w)) continue;
683  wi = std::abs(w);
684  }
685 
686  // DGM: we virtually make the P (data) point nearer if it has a lower
687  // weight
688  Pt *= wi;
689  wSum += wi;
690 
691  // 1st row
692  r1[0] += Pt.x * Qt.x;
693  r1[1] += Pt.x * Qt.y;
694  r1[2] += Pt.x * Qt.z;
695  // 2nd row
696  r2[0] += Pt.y * Qt.x;
697  r2[1] += Pt.y * Qt.y;
698  r2[2] += Pt.y * Qt.z;
699  // 3rd row
700  r3[0] += Pt.z * Qt.x;
701  r3[1] += Pt.z * Qt.y;
702  r3[2] += Pt.z * Qt.z;
703  }
704 
705  if (wSum != 0.0) covMat.scale(1.0 / wSum);
706 
707  return covMat;
708 }
709 
712  CCVector3& center,
713  PointCoordinateType& radius,
714  double minRelativeCenterShift /*=1.0e-3*/) {
715  if (!cloud || cloud->size() < 5) {
716  // invalid input
717  return false;
718  }
719 
720  CCVector3d c = CCVector3d::fromArray(center.u);
721 
722  unsigned count = cloud->size();
723 
724  // compute barycenter
725  CCVector3d G(0, 0, 0);
726  {
727  for (unsigned i = 0; i < count; ++i) {
728  const CCVector3* P = cloud->getPoint(i);
729  G += CCVector3d::fromArray(P->u);
730  }
731  G /= count;
732  }
733 
734  static const unsigned MAX_ITERATIONS = 100;
735  for (unsigned it = 0; it < MAX_ITERATIONS; ++it) {
736  // Compute average L, dL/da, dL/db, dL/dc.
737  double meanNorm = 0.0;
738  CCVector3d derivatives(0, 0, 0);
739  unsigned realCount = 0;
740  for (unsigned i = 0; i < count; ++i) {
741  const CCVector3* Pi = cloud->getPoint(i);
742  CCVector3d Di = CCVector3d::fromArray(Pi->u) - c;
743  double norm = Di.norm();
744  if (norm < ZERO_TOLERANCE_D) continue;
745 
746  meanNorm += norm;
747  derivatives += Di / norm;
748  ++realCount;
749  }
750 
751  meanNorm /= count;
752  derivatives /= count;
753 
754  // backup previous center
755  CCVector3d c0 = c;
756  // deduce new center
757  c = G - derivatives * meanNorm;
758  radius = static_cast<PointCoordinateType>(meanNorm);
759 
760  double shift = (c - c0).norm();
761  double relativeShift = shift / radius;
762  if (relativeShift < minRelativeCenterShift) break;
763  }
764 
765  return true;
766 }
767 
771  double outliersRatio,
772  CCVector3& center,
773  PointCoordinateType& radius,
774  double& rms,
775  GenericProgressCallback* progressCb /*=0*/,
776  double confidence /*=0.99*/,
777  unsigned seed /*=0*/) {
778  if (!cloud) return InvalidInput;
779 
780  unsigned n = cloud->size();
781  if (n < 4) return NotEnoughPoints;
782 
783  assert(confidence < 1.0);
784  confidence = std::min(confidence, 1.0 - FLT_EPSILON);
785 
786  const unsigned p = 4;
787 
788  // we'll need an array (sorted) to compute the medians
789  std::vector<PointCoordinateType> values;
790  try {
791  values.resize(n);
792  } catch (const std::bad_alloc&) {
793  // not enough memory
794  return NotEnoughMemory;
795  }
796 
797  // number of samples
798  unsigned m = 1;
799  if (n > p) {
800  m = static_cast<unsigned>(
801  log(1.0 - confidence) /
802  log(1.0 - pow(1.0 - outliersRatio, static_cast<double>(p))));
803  }
804 
805  // for progress notification
806  NormalizedProgress nProgress(progressCb, m);
807  if (progressCb) {
808  if (progressCb->textCanBeEdited()) {
809  char buffer[64];
810  sprintf(buffer, "Least Median of Squares samples: %u", m);
811  progressCb->setInfo(buffer);
812  progressCb->setMethodTitle("Detect sphere");
813  }
814  progressCb->update(0);
815  progressCb->start();
816  }
817 
818  // now we are going to randomly extract a subset of 4 points and test the
819  // resulting sphere each time
820  if (seed == 0) {
821  std::random_device randomGenerator; // non-deterministic generator
822  seed = randomGenerator();
823  }
824  std::mt19937 gen(seed); // to seed mersenne twister.
825  std::uniform_int_distribution<unsigned> dist(0, n - 1);
826  unsigned sampleCount = 0;
827  unsigned attempts = 0;
828  double minError = -1.0;
829  while (sampleCount < m && attempts < 2 * m) {
830  // get 4 random (different) indexes
831  unsigned indexes[4] = {0, 0, 0, 0};
832  for (unsigned j = 0; j < 4; ++j) {
833  bool isOK = false;
834  while (!isOK) {
835  indexes[j] = dist(gen);
836  isOK = true;
837  for (unsigned k = 0; k < j && isOK; ++k)
838  if (indexes[j] == indexes[k]) isOK = false;
839  }
840  }
841 
842  const CCVector3* A = cloud->getPoint(indexes[0]);
843  const CCVector3* B = cloud->getPoint(indexes[1]);
844  const CCVector3* C = cloud->getPoint(indexes[2]);
845  const CCVector3* D = cloud->getPoint(indexes[3]);
846 
847  ++attempts;
848  CCVector3 thisCenter;
849  PointCoordinateType thisRadius;
850  if (ComputeSphereFrom4(*A, *B, *C, *D, thisCenter, thisRadius) !=
851  NoError)
852  continue;
853 
854  // compute residuals
855  for (unsigned i = 0; i < n; ++i) {
857  (*cloud->getPoint(i) - thisCenter).norm() - thisRadius;
858  values[i] = error * error;
859  }
860 
861  const unsigned int medianIndex = n / 2;
862 
863  std::nth_element(values.begin(), values.begin() + medianIndex,
864  values.end());
865 
866  // the error is the median of the squared residuals
867  double error = static_cast<double>(values[medianIndex]);
868 
869  // we keep track of the solution with the least error
870  if (error < minError || minError < 0.0) {
871  minError = error;
872  center = thisCenter;
873  radius = thisRadius;
874  }
875 
876  ++sampleCount;
877 
878  if (progressCb && !nProgress.oneStep()) {
879  // progress canceled by the user
880  return ProcessCancelledByUser;
881  }
882  }
883 
884  // too many failures?!
885  if (sampleCount < m) {
886  return ProcessFailed;
887  }
888 
889  // last step: robust estimation
890  ReferenceCloud candidates(cloud);
891  if (n > p) {
892  // e robust standard deviation estimate (see Zhang's report)
893  double sigma = 1.4826 * (1.0 + 5.0 / (n - p)) * sqrt(minError);
894 
895  // compute the least-squares best-fitting sphere with the points
896  // having residuals below 2.5 sigma
897  double maxResidual = 2.5 * sigma;
898  if (candidates.reserve(n)) {
899  // compute residuals and select the points
900  for (unsigned i = 0; i < n; ++i) {
902  (*cloud->getPoint(i) - center).norm() - radius;
903  if (error < maxResidual) candidates.addPointIndex(i);
904  }
905  candidates.resize(candidates.size());
906 
907  // eventually estimate the robust sphere parameters with least
908  // squares (iterative)
909  if (RefineSphereLS(&candidates, center, radius)) {
910  // replace input cloud by this subset!
911  cloud = &candidates;
912  n = cloud->size();
913  }
914  } else {
915  // not enough memory!
916  // we'll keep the rough estimate...
917  }
918  }
919 
920  // update residuals
921  {
922  double residuals = 0;
923  for (unsigned i = 0; i < n; ++i) {
924  const CCVector3* P = cloud->getPoint(i);
925  double e = (*P - center).norm() - radius;
926  residuals += e * e;
927  }
928  rms = sqrt(residuals / n);
929  }
930 
931  return NoError;
932 }
933 
934 static bool Landau_Smith(const std::vector<CCVector2d>& xy,
935  CCVector2d& center,
936  PointCoordinateType& radius) {
937  size_t N = xy.size();
938  if (N < 3) {
939  assert(false);
940  return false;
941  }
942 
943  double p1 = 0.0, p2 = 0.0, p3 = 0.0, p4 = 0.0, p5 = 0.0, p6 = 0.0, p7 = 0.0,
944  p8 = 0.0, p9 = 0.0;
945 
946  for (size_t i = 0; i < N; ++i) {
947  p1 += xy[i].x;
948  p2 += xy[i].x * xy[i].x;
949  p3 += xy[i].x * xy[i].y;
950  p4 += xy[i].y;
951  p5 += xy[i].y * xy[i].y;
952  p6 += xy[i].x * xy[i].x * xy[i].x;
953  p7 += xy[i].x * xy[i].y * xy[i].y;
954  p8 += xy[i].y * xy[i].y * xy[i].y;
955  p9 += xy[i].x * xy[i].x * xy[i].y;
956  }
957 
958  double a1 = 2 * (p1 * p1 - N * p2);
959  double b1 = 2 * (p1 * p4 - N * p3);
960  double a2 = b1;
961  double b2 = 2 * (p4 * p4 - N * p5);
962  double c1 = p2 * p1 - N * p6 + p1 * p5 - N * p7;
963  double c2 = p2 * p4 - N * p8 + p4 * p5 - N * p9;
964 
965  center.x = (c1 * b2 - c2 * b1) / (a1 * b2 - a2 * b1); // center along x
966  center.y = (a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1); // center along y
967  radius = static_cast<PointCoordinateType>(
968  sqrt(((p2 + p5) - (2 * p1 * center.x) + (N * center.x * center.x) -
969  (2 * p4 * center.y) + (N * center.y * center.y)) /
970  N)); // circle radius
971 
972  return true;
973 }
974 
977  CCVector3& center,
978  CCVector3& normal,
979  PointCoordinateType& radius,
980  double& rms,
981  GenericProgressCallback* progressCb /*=nullptr*/) {
982  center = CCVector3(0, 0, 0);
983  normal = CCVector3(0, 0, PC_ONE);
984  radius = std::numeric_limits<PointCoordinateType>::quiet_NaN();
985  rms = std::numeric_limits<double>::quiet_NaN();
986 
987  if (!cloud) {
988  assert(false);
989  return InvalidInput;
990  }
991 
992  unsigned n = cloud->size();
993  if (n < 4) {
994  return NotEnoughPoints;
995  }
996 
997  // for progress notification
998  NormalizedProgress nProgress(progressCb, 7);
999  if (progressCb) {
1000  if (progressCb->textCanBeEdited()) {
1001  progressCb->setMethodTitle(
1002  "Detect circle using Landau Smith algorithm");
1003  }
1004  progressCb->update(0);
1005  progressCb->start();
1006  }
1007 
1008  // step 1: fit a plane on the cloud to retrieve the eigenvalues and vectors
1009  // of the covariance matrix
1010  Neighbourhood Yk(cloud);
1011  if (!Yk.getLSPlane()) {
1012  return ProcessFailed;
1013  }
1014 
1015  if (progressCb && !nProgress.oneStep()) {
1016  // progress canceled by the user
1017  return ProcessCancelledByUser;
1018  }
1019  // step 2: try to fit a circle with each eigenvector and keep the best
1020  // result
1021  const CCVector3* eigenvectors[2]{
1022  Yk.getLSPlaneX(), // eigenvector associated to the max eigenvalue
1023  // (= most elongated dimension)
1024  Yk.getLSPlaneNormal() // eigenvector associated to the min
1025  // eigenvalue (= most flat dimension)
1026  };
1027 
1028  std::vector<CCVector2d> pointsOnPlane;
1029  try {
1030  pointsOnPlane.resize(cloud->size());
1031  } catch (const std::bad_alloc&) {
1032  return NotEnoughMemory;
1033  }
1034 
1035  // compute the cloud (gravity) center
1036  const CCVector3* G = Yk.getGravityCenter();
1037  assert(G);
1038 
1039 #ifdef DEBUG_TRACE
1040  FILE* fp = fopen("C:\\Temp\\circle_fit.txt", "wt");
1041 #endif
1042 
1043  for (unsigned dim = 0; dim < 2; ++dim) {
1044  const CCVector3* eigenVector = eigenvectors[dim];
1045  assert(eigenVector);
1046 
1047  // compute a local coordinate system
1048  CCVector3 x = eigenVector->orthogonal();
1049  CCVector3 y = eigenVector->cross(x);
1050 #ifdef DEBUG_TRACE
1051  {
1052  fprintf(fp, "Dim %i\n", dim);
1053  fprintf(fp, "X = %f %f %f\n", x.x, x.y, x.z);
1054  fprintf(fp, "Y = %f %f %f\n", y.x, y.y, y.z);
1055  fprintf(fp, "Z = %f %f %f\n", eigenVector->x, eigenVector->y,
1056  eigenVector->z);
1057  }
1058 #endif
1059 
1060  // step 3: project the point cloud onto a 2D plane
1061  for (unsigned i = 0; i < n; ++i) {
1062  CCVector3 Plocal = *cloud->getPoint(i) - *G;
1063  pointsOnPlane[i] = {Plocal.dot(x), Plocal.dot(y)};
1064  }
1065 
1066  if (progressCb && !nProgress.oneStep()) {
1067  // progress canceled by the user
1068  return ProcessCancelledByUser;
1069  }
1070 
1071 #ifdef DEBUG_TRACE
1072  {
1073  FILE* fpc = nullptr;
1074  switch (dim) {
1075  case 0:
1076  fpc = fopen("C:\\Temp\\circle_dim0.asc", "wt");
1077  break;
1078  case 1:
1079  fpc = fopen("C:\\Temp\\circle_dim1.asc", "wt");
1080  break;
1081  case 2:
1082  fpc = fopen("C:\\Temp\\circle_dim2.asc", "wt");
1083  break;
1084  }
1085 
1086  for (const CCVector2d& P2D : pointsOnPlane) {
1087  fprintf(fpc, "%f %f 0\n", P2D.x, P2D.y);
1088  }
1089  fclose(fpc);
1090  }
1091 #endif
1092 
1093  // step 4: calculate the circle center and radius on the 2D plane using
1094  // the Landau Smith algorithm
1095  CCVector2d thisCenter2D;
1096  PointCoordinateType thisRadius = 0;
1097  if (!Landau_Smith(pointsOnPlane, thisCenter2D, thisRadius)) {
1098  assert(false);
1099  return ProcessFailed;
1100  }
1101 
1102 #ifdef DEBUG_TRACE
1103  fprintf(fp, "Center (%f, %f) - radius = %f\n", thisCenter2D.x,
1104  thisCenter2D.y, thisRadius);
1105 #endif
1106 
1107  // estimate the RMS
1108  {
1109  double thisRMS = 0.0;
1110  for (const CCVector2d& P2D : pointsOnPlane) {
1111  double r = (P2D - thisCenter2D).norm();
1112  double error = thisRadius - r;
1113  thisRMS += error * error;
1114  }
1115 
1116  thisRMS = sqrt(thisRMS / n);
1117 
1118 #ifdef DEBUG_TRACE
1119  fprintf(fp, "RMS = %f\n", thisRMS);
1120  fprintf(fp, "=================\n");
1121 #endif
1122 
1123  if (dim == 0 || thisRMS < rms) {
1124  // reposition the circle center in 3D
1125  center = *G +
1126  static_cast<PointCoordinateType>(thisCenter2D.x) * x +
1127  static_cast<PointCoordinateType>(thisCenter2D.y) * y;
1128  normal = CCVector3::fromArray(eigenVector->u);
1129  radius = thisRadius;
1130  rms = thisRMS;
1131  }
1132  }
1133 
1134  if (progressCb && !nProgress.oneStep()) {
1135  // progress canceled by the user
1136  return ProcessCancelledByUser;
1137  }
1138  }
1139 
1140 #ifdef DEBUG_TRACE
1141  fclose(fp);
1142 #endif
1143 
1144  return NoError;
1145 }
1146 
1147 //******************************************************************************
1148 //
1149 // Purpose:
1150 //
1151 // DMAT_SOLVE uses Gauss-Jordan elimination to solve an N by N linear system.
1152 //
1153 // Discussion:
1154 //
1155 // The doubly dimensioned array A is treated as a one dimensional vector,
1156 // stored by COLUMNS. Entry A(I,J) is stored as A[I+J*N]
1157 //
1158 // Modified:
1159 //
1160 // 29 August 2003
1161 //
1162 // Author:
1163 //
1164 // John Burkardt
1165 //
1166 // Parameters:
1167 //
1168 // Input, int N, the order of the matrix.
1169 //
1170 // Input, int RHS_NUM, the number of right hand sides. RHS_NUM
1171 // must be at least 0.
1172 //
1173 // Input/output, double A[N*(N+RHS_NUM)], contains in rows and columns 1
1174 // to N the coefficient matrix, and in columns N+1 through
1175 // N+RHS_NUM, the right hand sides. On output, the coefficient matrix
1176 // area has been destroyed, while the right hand sides have
1177 // been overwritten with the corresponding solutions.
1178 //
1179 // Output, int DMAT_SOLVE, singularity flag.
1180 // 0, the matrix was not singular, the solutions were computed;
1181 // J, factorization failed on step J, and the solutions could not
1182 // be computed.
1183 //
1184 int dmat_solve(int n, int rhs_num, double a[]) {
1185  for (int j = 0; j < n; j++) {
1186  // Choose a pivot row.
1187  int ipivot = j;
1188  double apivot = a[j + j * n];
1189 
1190  for (int i = j; i < n; i++) {
1191  if (std::abs(apivot) < std::abs(a[i + j * n])) {
1192  apivot = a[i + j * n];
1193  ipivot = i;
1194  }
1195  }
1196 
1197  if (apivot == 0.0) {
1198  return j;
1199  }
1200 
1201  // Interchange.
1202  for (int i = 0; i < n + rhs_num; i++) {
1203  std::swap(a[ipivot + i * n], a[j + i * n]);
1204  }
1205 
1206  // A(J,J) becomes 1.
1207  a[j + j * n] = 1.0;
1208  for (int k = j; k < n + rhs_num; k++) {
1209  a[j + k * n] = a[j + k * n] / apivot;
1210  }
1211 
1212  // A(I,J) becomes 0.
1213  for (int i = 0; i < n; i++) {
1214  if (i != j) {
1215  double factor = a[i + j * n];
1216  a[i + j * n] = 0.0;
1217  for (int k = j; k < n + rhs_num; k++) {
1218  a[i + k * n] = a[i + k * n] - factor * a[j + k * n];
1219  }
1220  }
1221  }
1222  }
1223 
1224  return 0;
1225 }
1226 
1229  const CCVector3& B,
1230  const CCVector3& C,
1231  const CCVector3& D,
1232  CCVector3& center,
1233  PointCoordinateType& radius) {
1234  // inspired from 'tetrahedron_circumsphere_3d' by Adrian Bowyer and John
1235  // Woodwark
1236 
1237  // Set up the linear system.
1238  double a[12];
1239  {
1240  CCVector3 AB = B - A;
1241  a[0] = AB.x;
1242  a[3] = AB.y;
1243  a[6] = AB.z;
1244  a[9] = AB.norm2d();
1245  }
1246  {
1247  CCVector3 AC = C - A;
1248  a[1] = AC.x;
1249  a[4] = AC.y;
1250  a[7] = AC.z;
1251  a[10] = AC.norm2d();
1252  }
1253  {
1254  CCVector3 AD = D - A;
1255  a[2] = AD.x;
1256  a[5] = AD.y;
1257  a[8] = AD.z;
1258  a[11] = AD.norm2d();
1259  }
1260 
1261  // Solve the linear system (with Gauss-Jordan elimination)
1262  if (dmat_solve(3, 1, a) != 0) {
1263  // system is singular?
1264  return ProcessFailed;
1265  }
1266 
1267  // Compute the radius and center.
1268  CCVector3 u = CCVector3(static_cast<PointCoordinateType>(a[0 + 3 * 3]),
1269  static_cast<PointCoordinateType>(a[1 + 3 * 3]),
1270  static_cast<PointCoordinateType>(a[2 + 3 * 3])) /
1271  2;
1272  radius = u.norm();
1273  center = A + u;
1274 
1275  return NoError;
1276 }
constexpr PointCoordinateType PC_ONE
'1' as a PointCoordinateType value
Definition: CVConst.h:67
constexpr double M_PI
Pi.
Definition: CVConst.h:19
constexpr ScalarType NAN_VALUE
NaN as a ScalarType value.
Definition: CVConst.h:76
constexpr double ZERO_TOLERANCE_D
Definition: CVConst.h:49
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
double normal[3]
int count
int points
core::Tensor result
Definition: VtkUtils.cpp:76
Type y
Definition: CVGeom.h:137
Type u[3]
Definition: CVGeom.h:139
Type x
Definition: CVGeom.h:137
Type z
Definition: CVGeom.h:137
Type x
Definition: CVGeom.h:36
Type y
Definition: CVGeom.h:36
Type dot(const Vector3Tpl &v) const
Dot product.
Definition: CVGeom.h:408
double norm2d() const
Returns vector square norm (forces double precision output)
Definition: CVGeom.h:419
Vector3Tpl orthogonal() const
Returns a normalized vector which is orthogonal to this one.
Definition: CVGeom.h:433
Type norm() const
Returns vector norm.
Definition: CVGeom.h:424
Vector3Tpl cross(const Vector3Tpl &v) const
Cross product.
Definition: CVGeom.h:412
static Vector3Tpl fromArray(const int a[3])
Constructor from an int array.
Definition: CVGeom.h:268
A kind of ReferenceCloud based on the DgmOctree::NeighboursSet structure.
The octree structure used throughout the library.
Definition: DgmOctree.h:39
void getCellPos(CellCode code, unsigned char level, Tuple3i &cellPos, bool isCodeTruncated) const
Definition: DgmOctree.cpp:498
unsigned findNearestNeighborsStartingFromCell(NearestNeighboursSearchStruct &nNSS, bool getOnlyPointsWithValidScalar=false) const
Definition: DgmOctree.cpp:1655
unsigned char findBestLevelForAGivenNeighbourhoodSizeExtraction(PointCoordinateType radius) const
Definition: DgmOctree.cpp:2664
int findNeighborsInASphereStartingFromCell(NearestNeighboursSearchStruct &nNSS, double radius, bool sortValues=true) const
Advanced form of the nearest neighbours search algorithm (in a sphere)
Definition: DgmOctree.cpp:2479
const PointCoordinateType & getCellSize(unsigned char level) const
Returns the octree cells length for a given level of subdivision.
Definition: DgmOctree.h:494
void computeCellCenter(CellCode code, unsigned char level, CCVector3 &center, bool isCodeTruncated=false) const
Definition: DgmOctree.h:862
unsigned executeFunctionForAllCellsAtLevel(unsigned char level, octreeCellFunc func, void **additionalParameters, bool multiThread=false, GenericProgressCallback *progressCb=nullptr, const char *functionTitle=nullptr, int maxThreadCount=0)
Definition: DgmOctree.cpp:3573
int build(GenericProgressCallback *progressCb=nullptr)
Builds the structure.
Definition: DgmOctree.cpp:196
unsigned char findBestLevelForAGivenPopulationPerCell(unsigned indicativeNumberOfPointsPerCell) const
Definition: DgmOctree.cpp:2737
virtual bool enableScalarField()=0
Enables the scalar field associated to the cloud.
virtual void forEach(genericPointAction action)=0
Fast iteration mechanism.
virtual void placeIteratorAtBeginning()=0
Sets the cloud iterator at the beginning.
virtual const CCVector3 * getNextPoint()=0
Returns the next point (relatively to the global iterator position)
virtual unsigned size() const =0
Returns the number of points.
virtual ScalarType getPointScalarValue(unsigned pointIndex) const =0
Returns the ith point associated scalar value.
virtual void setPointScalarValue(unsigned pointIndex, ScalarType value)=0
Sets the ith point associated scalar value.
A generic 3D point cloud with index-based and presistent access to points.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
virtual void setInfo(const char *infoStr)=0
Notifies some information about the ongoing process.
virtual void setMethodTitle(const char *methodTitle)=0
Notifies the algorithm title.
virtual bool textCanBeEdited() const
Returns whether the dialog title and info can be updated or not.
virtual void update(float percent)=0
Notifies the algorithm progress.
static ErrorCode DetectSphereRobust(GenericIndexedCloudPersist *cloud, double outliersRatio, CCVector3 &center, PointCoordinateType &radius, double &rms, GenericProgressCallback *progressCb=nullptr, double confidence=0.99, unsigned seed=0)
Tries to detect a sphere in a point cloud.
static ErrorCode DetectCircle(GenericIndexedCloudPersist *cloud, CCVector3 &center, CCVector3 &normal, PointCoordinateType &radius, double &rms, GenericProgressCallback *progressCb=nullptr)
Detects a circle from a point cloud.
static SquareMatrixd ComputeWeightedCrossCovarianceMatrix(GenericCloud *P, GenericCloud *Q, const CCVector3 &pGravityCenter, const CCVector3 &qGravityCenter, ScalarField *coupleWeights=nullptr)
static cloudViewer::SquareMatrixd ComputeCovarianceMatrix(const GenericCloud *theCloud, const PointCoordinateType *_gravityCenter=nullptr)
Computes the covariance matrix of a clouds.
static ErrorCode ComputeSphereFrom4(const CCVector3 &A, const CCVector3 &B, const CCVector3 &C, const CCVector3 &D, CCVector3 &center, PointCoordinateType &radius)
Computes the center and radius of a sphere passing through 4 points.
static ErrorCode FlagDuplicatePoints(GenericIndexedCloudPersist *theCloud, double minDistanceBetweenPoints=std::numeric_limits< double >::epsilon(), GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Flag duplicate points.
static bool FlagDuplicatePointsInACellAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
Flags duplicate points inside a cell.
static bool ComputeApproxPointsDensityInACellAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
Computes approximate point density inside a cell.
static ErrorCode ComputeCharactersitic(GeomCharacteristic c, int subOption, GenericIndexedCloudPersist *cloud, PointCoordinateType kernelRadius, const CCVector3 *roughnessUpDir=nullptr, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Unified way to compute a geometric characteristic.
static ErrorCode ComputeLocalDensityApprox(GenericIndexedCloudPersist *cloud, Density densityType, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Computes the local density (approximate)
static bool ComputeGeomCharacteristicAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
Computes geom characteristic inside a cell.
static bool RefineSphereLS(GenericIndexedCloudPersist *cloud, CCVector3 &center, PointCoordinateType &radius, double minReltaiveCenterShift=1.0e-3)
Refines the estimation of a sphere by (iterative) least-squares.
static CCVector3 ComputeGravityCenter(const GenericCloud *theCloud)
Computes the gravity center of a point cloud.
static CCVector3 ComputeWeightedGravityCenter(GenericCloud *theCloud, ScalarField *weights)
Computes the weighted gravity center of a point cloud.
static SquareMatrixd ComputeCrossCovarianceMatrix(GenericCloud *P, GenericCloud *Q, const CCVector3 &pGravityCenter, const CCVector3 &qGravityCenter)
Computes the cross covariance matrix between two clouds (same size)
ScalarType computeCurvature(const CCVector3 &P, CurvatureType cType)
Computes the curvature of a set of point (by fitting a 2.5D quadric)
const PointCoordinateType * getLSPlane()
Returns best interpolating plane equation (Least-square)
ScalarType computeRoughness(const CCVector3 &P, const CCVector3 *roughnessUpDir=nullptr)
const CCVector3 * getGravityCenter()
Returns gravity center.
GeomFeature
Geometric feature computed from eigen values/vectors.
const CCVector3 * getLSPlaneX()
Returns best interpolating plane (Least-square) 'X' base vector.
CurvatureType
Curvature type.
Definition: Neighbourhood.h:42
const CCVector3 * getLSPlaneNormal()
Returns best interpolating plane (Least-square) normal vector.
double computeFeature(GeomFeature feature)
Computes the given feature on a set of point.
ScalarType computeMomentOrder1(const CCVector3 &P)
bool oneStep()
Increments total progress value of a single unit.
A very simple point cloud (no point duplication)
void setPointScalarValue(unsigned pointIndex, ScalarType value) override
Sets the ith point associated scalar value.
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
virtual GenericIndexedCloudPersist * getAssociatedCloud()
Returns the associated (source) cloud.
unsigned size() const override
Returns the number of points.
virtual unsigned getPointGlobalIndex(unsigned localIndex) const
virtual bool resize(unsigned n)
Presets the size of the vector used to store point references.
const CCVector3 * getPointPersistentPtr(unsigned index) override
Returns the ith point as a persistent pointer.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
ScalarType getPointScalarValue(unsigned pointIndex) const override
Returns the ith point associated scalar value.
static void SetScalarValueToZero(const CCVector3 &P, ScalarType &scalarValue)
Sets the distance value associated to a point to zero.
A simple scalar field (to be associated to a point cloud)
Definition: ScalarField.h:25
ScalarType & getValue(std::size_t index)
Definition: ScalarField.h:92
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
unsigned currentSize() const
Definition: ScalarField.h:100
Scalar ** m_values
The matrix rows.
Definition: SquareMatrix.h:157
void clear()
Sets all elements to 0.
Definition: SquareMatrix.h:319
void scale(Scalar coef)
Scales matrix (all elements are multiplied by the same coef.)
Definition: SquareMatrix.h:459
Scalar * row(unsigned index)
Returns pointer to matrix row.
Definition: SquareMatrix.h:160
int dmat_solve(int n, int rhs_num, double a[])
static double s_UnitSphereVolume
static bool Landau_Smith(const std::vector< CCVector2d > &xy, CCVector2d &center, PointCoordinateType &radius)
int min(int a, int b)
Definition: cutil_math.h:53
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
static void error(char *msg)
Definition: lsd.c:159
Generic file read and write utility for python interface.
SquareMatrixTpl< double > SquareMatrixd
Double square matrix type.
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
cloudViewer::NormalizedProgress * nProgress
cloudViewer::DgmOctree * octree
Container of in/out parameters for nearest neighbour(s) search.
Definition: DgmOctree.h:161
unsigned char level
Level of subdivision of the octree at which to start the search.
Definition: DgmOctree.h:171
Tuple3i cellPos
Position in the octree of the cell including the query point.
Definition: DgmOctree.h:184
CCVector3 cellCenter
Coordinates of the center of the cell including the query point.
Definition: DgmOctree.h:189
unsigned minNumberOfNeighbors
Minimal number of neighbours to find.
Definition: DgmOctree.h:177
void prepare(PointCoordinateType radius, PointCoordinateType cellSize)
Updates maxD2 and minD2 with search radius and cellSize.
Definition: DgmOctree.h:270
Octree cell descriptor.
Definition: DgmOctree.h:354
ReferenceCloud * points
Set of points lying inside this cell.
Definition: DgmOctree.h:365
const DgmOctree * parentOctree
Octree to which the cell belongs.
Definition: DgmOctree.h:359
unsigned char level
Cell level of subdivision.
Definition: DgmOctree.h:367
CellCode truncatedCode
Truncated cell code.
Definition: DgmOctree.h:361