ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
CloudSamplingTools.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 <CloudSamplingTools.h>
9 
10 // local
11 #include <CVPointCloud.h>
15 #include <Neighbourhood.h>
16 #include <ReferenceCloud.h>
17 #include <ScalarField.h>
18 #include <ScalarFieldTools.h>
19 #include <SimpleMesh.h>
20 
21 // system
22 #include <algorithm>
23 #include <random>
24 
25 using namespace cloudViewer;
26 
28  GenericIndexedCloudPersist* inputCloud,
29  int newNumberOfPoints,
30  RESAMPLING_CELL_METHOD resamplingMethod,
31  GenericProgressCallback* progressCb,
32  DgmOctree* inputOctree) {
33  assert(inputCloud);
34 
35  DgmOctree* octree = inputOctree;
36  if (!octree) {
37  octree = new DgmOctree(inputCloud);
38  if (octree->build(progressCb) < 1) return nullptr;
39  }
40 
41  // look for the Octree level that gives the number of cells (= points)
42  // closest to the desired value
43  unsigned char bestLevel =
44  octree->findBestLevelForAGivenCellNumber(newNumberOfPoints);
45 
47  inputCloud, bestLevel, resamplingMethod, progressCb, octree);
48 
49  if (!inputOctree) delete octree;
50 
51  return sampledCloud;
52 }
53 
55  GenericIndexedCloudPersist* inputCloud,
56  unsigned char octreeLevel,
57  RESAMPLING_CELL_METHOD resamplingMethod,
58  GenericProgressCallback* progressCb /*=0*/,
59  DgmOctree* inputOctree /*=0*/) {
60  assert(inputCloud);
61 
62  DgmOctree* octree = inputOctree;
63  if (!octree) {
64  octree = new DgmOctree(inputCloud);
65  if (octree->build(progressCb) < 1) {
66  delete octree;
67  return nullptr;
68  }
69  }
70 
71  PointCloud* cloud = new PointCloud();
72 
73  unsigned nCells = octree->getCellNumber(octreeLevel);
74  if (!cloud->reserve(nCells)) {
75  if (!inputOctree) delete octree;
76  delete cloud;
77  return nullptr;
78  }
79 
80  // structure contenant les parametres additionnels
81  void* additionalParameters[2] = {
82  reinterpret_cast<void*>(cloud),
83  reinterpret_cast<void*>(&resamplingMethod)};
84 
86  octreeLevel, &resampleCellAtLevel, additionalParameters,
87  false, // the process is so simple that MT is slower!
88  progressCb, "Cloud Resampling") == 0) {
89  // something went wrong
90  delete cloud;
91  cloud = nullptr;
92  }
93 
94  if (!inputOctree) delete octree;
95 
96  return cloud;
97 }
98 
100  GenericIndexedCloudPersist* inputCloud,
101  int newNumberOfPoints,
102  SUBSAMPLING_CELL_METHOD subsamplingMethod,
103  GenericProgressCallback* progressCb /*=0*/,
104  DgmOctree* inputOctree /*=0*/) {
105  assert(inputCloud);
106 
107  DgmOctree* octree = inputOctree;
108  if (!octree) {
109  octree = new DgmOctree(inputCloud);
110  if (octree->build(progressCb) < 1) return nullptr;
111  }
112 
113  // on cherche le niveau qui donne le nombre de points le plus proche de la
114  // consigne
115  unsigned char bestLevel =
116  octree->findBestLevelForAGivenCellNumber(newNumberOfPoints);
117 
119  inputCloud, bestLevel, subsamplingMethod, progressCb, octree);
120 
121  if (!inputOctree) delete octree;
122 
123  return subsampledCloud;
124 }
125 
127  GenericIndexedCloudPersist* inputCloud,
128  unsigned char octreeLevel,
129  SUBSAMPLING_CELL_METHOD subsamplingMethod,
130  GenericProgressCallback* progressCb /*=0*/,
131  DgmOctree* inputOctree /*=0*/) {
132  assert(inputCloud);
133 
134  DgmOctree* octree = inputOctree;
135  if (!octree) {
136  octree = new DgmOctree(inputCloud);
137  if (octree->build(progressCb) < 1) {
138  delete octree;
139  return nullptr;
140  }
141  }
142 
143  ReferenceCloud* cloud = new ReferenceCloud(inputCloud);
144 
145  unsigned nCells = octree->getCellNumber(octreeLevel);
146  if (!cloud->reserve(nCells)) {
147  if (!inputOctree) delete octree;
148  delete cloud;
149  return nullptr;
150  }
151 
152  // structure contenant les parametres additionnels
153  void* additionalParameters[2] = {
154  reinterpret_cast<void*>(cloud),
155  reinterpret_cast<void*>(&subsamplingMethod)};
156 
158  octreeLevel, &subsampleCellAtLevel, additionalParameters,
159  false, // the process is so simple that MT is slower!
160  progressCb, "Cloud Subsampling") == 0) {
161  // something went wrong
162  delete cloud;
163  cloud = nullptr;
164  }
165 
166  if (!inputOctree) delete octree;
167 
168  return cloud;
169 }
170 
172  GenericIndexedCloudPersist* inputCloud, unsigned every_k_points) {
173  assert(inputCloud);
174 
175  if (every_k_points <= 1) {
176  return nullptr;
177  }
178 
179  unsigned pointCount = inputCloud->size();
180 
181  unsigned newNumberOfPoints = pointCount / every_k_points + 2;
182 
183  // we put all input points in a ReferenceCloud
184  ReferenceCloud* newCloud = new ReferenceCloud(inputCloud);
185  if (!newCloud->reserve(newNumberOfPoints)) {
186  // not enough memory
187  delete newCloud;
188  return nullptr;
189  }
190 
191  int num = 0;
192  for (unsigned i = 0; i < pointCount; i += every_k_points) {
193  newCloud->addPointIndex(i);
194  num += 1;
195  }
196 
197  newCloud->resize(num); // always smaller, so it should be ok!
198 
199  return newCloud;
200 }
201 
203  GenericIndexedCloudPersist* inputCloud,
204  unsigned newNumberOfPoints,
205  GenericProgressCallback* progressCb /*=0*/) {
206  assert(inputCloud);
207 
208  unsigned theCloudSize = inputCloud->size();
209 
210  // we put all input points in a ReferenceCloud
211  ReferenceCloud* newCloud = new ReferenceCloud(inputCloud);
212  if (!newCloud->addPointIndex(0, theCloudSize)) {
213  delete newCloud;
214  return nullptr;
215  }
216 
217  // we have less points than requested?!
218  if (theCloudSize <= newNumberOfPoints) {
219  return newCloud;
220  }
221 
222  unsigned pointsToRemove = theCloudSize - newNumberOfPoints;
223  std::random_device rd; // non-deterministic generator
224  std::mt19937 gen(rd()); // to seed mersenne twister.
225 
226  NormalizedProgress normProgress(progressCb, pointsToRemove);
227  if (progressCb) {
228  if (progressCb->textCanBeEdited()) {
229  progressCb->setInfo("Random subsampling");
230  }
231  progressCb->update(0);
232  progressCb->start();
233  }
234 
235  // we randomly remove "inputCloud.size() - newNumberOfPoints" points (much
236  // simpler)
237  unsigned lastPointIndex = theCloudSize - 1;
238  for (unsigned i = 0; i < pointsToRemove; ++i) {
239  std::uniform_int_distribution<unsigned> dist(0, lastPointIndex);
240  unsigned index = dist(gen);
241  newCloud->swap(index, lastPointIndex);
242  --lastPointIndex;
243 
244  if (progressCb && !normProgress.oneStep()) {
245  // cancel process
246  delete newCloud;
247  return nullptr;
248  }
249  }
250 
251  newCloud->resize(newNumberOfPoints); // always smaller, so it should be ok!
252 
253  return newCloud;
254 }
255 
257  GenericIndexedCloudPersist* inputCloud,
258  PointCoordinateType minDistance,
259  const SFModulationParams& modParams,
260  DgmOctree* inputOctree /*=0*/,
261  GenericProgressCallback* progressCb /*=0*/) {
262  assert(inputCloud);
263  unsigned cloudSize = inputCloud->size();
264 
265  DgmOctree* octree = inputOctree;
266  if (!octree) {
267  octree = new DgmOctree(inputCloud);
268  if (octree->build() < static_cast<int>(cloudSize)) {
269  delete octree;
270  return nullptr;
271  }
272  }
273  assert(octree && octree->associatedCloud() == inputCloud);
274 
275  // output cloud
276  ReferenceCloud* sampledCloud = new ReferenceCloud(inputCloud);
277  const unsigned c_reserveStep = 65536;
278  if (!sampledCloud->reserve(std::min(cloudSize, c_reserveStep))) {
279  if (!inputOctree) delete octree;
280  return nullptr;
281  }
282 
283  std::vector<char>
284  markers; // DGM: upgraded from vector, as this can be quite huge!
285  try {
286  markers.resize(cloudSize, 1); // true by default
287  } catch (const std::bad_alloc&) {
288  if (!inputOctree) delete octree;
289  delete sampledCloud;
290  return nullptr;
291  }
292 
293  // best octree level (there may be several of them if we use parameter
294  // modulation)
295  std::vector<unsigned char> bestOctreeLevel;
296  bool modParamsEnabled = modParams.enabled;
297  ScalarType sfMin = 0, sfMax = 0;
298  try {
299  if (modParams.enabled) {
300  // compute min and max sf values
302  sfMax);
303 
304  if (!ScalarField::ValidValue(sfMin)) {
305  // all SF values are NAN?!
306  modParamsEnabled = false;
307  } else {
308  // compute min and max 'best' levels
309  PointCoordinateType dist0 = static_cast<PointCoordinateType>(
310  sfMin * modParams.a + modParams.b);
311  PointCoordinateType dist1 = static_cast<PointCoordinateType>(
312  sfMax * modParams.a + modParams.b);
313  unsigned char level0 =
315  dist0);
316  unsigned char level1 =
318  dist1);
319 
320  bestOctreeLevel.push_back(level0);
321  if (level1 != level0) {
322  // add intermediate levels if necessary
323  std::size_t levelCount =
324  (level1 < level0 ? level0 - level1
325  : level1 - level0) +
326  1;
327  assert(levelCount != 0);
328 
329  for (std::size_t i = 1; i < levelCount - 1;
330  ++i) // we already know level0 and level1!
331  {
332  ScalarType sfVal =
333  sfMin + i * ((sfMax - sfMin) / levelCount);
335  static_cast<PointCoordinateType>(
336  sfVal * modParams.a + modParams.b);
337  unsigned char level =
339  dist);
340  bestOctreeLevel.push_back(level);
341  }
342  }
343  bestOctreeLevel.push_back(level1);
344  }
345  } else {
346  unsigned char defaultLevel =
348  minDistance);
349  bestOctreeLevel.push_back(defaultLevel);
350  }
351  } catch (const std::bad_alloc&) {
352  // not enough memory
353  if (!inputOctree) {
354  delete octree;
355  }
356  delete sampledCloud;
357  return nullptr;
358  }
359 
360  // progress notification
361  NormalizedProgress normProgress(progressCb, cloudSize);
362  if (progressCb) {
363  if (progressCb->textCanBeEdited()) {
364  progressCb->setMethodTitle("Spatial resampling");
365  char buffer[256];
366  sprintf(buffer, "Points: %u\nMin dist.: %f", cloudSize,
367  minDistance);
368  progressCb->setInfo(buffer);
369  }
370  progressCb->update(0);
371  progressCb->start();
372  }
373 
374  // for each point in the cloud that is still 'marked', we look
375  // for its neighbors and remove their own marks
376  bool error = false;
377  // default octree level
378  assert(!bestOctreeLevel.empty());
379  unsigned char octreeLevel = bestOctreeLevel.front();
380  // default distance between points
381  PointCoordinateType minDistBetweenPoints = minDistance;
382  for (unsigned i = 0; i < cloudSize; i++) {
383  // no mark? we skip this point
384  if (markers[i] != 0) {
385  // init neighbor search structure
386  const CCVector3* P = inputCloud->getPoint(i);
387 
388  // parameters modulation
389  if (modParamsEnabled) {
390  ScalarType sfVal = inputCloud->getPointScalarValue(i);
391  if (ScalarField::ValidValue(sfVal)) {
392  // modulate minDistance
393  minDistBetweenPoints = static_cast<PointCoordinateType>(
394  sfVal * modParams.a + modParams.b);
395  // get (approximate) best level
396  std::size_t levelIndex = static_cast<std::size_t>(
397  bestOctreeLevel.size() *
398  ((sfVal - sfMin) / (sfMax - sfMin)));
399  if (levelIndex == bestOctreeLevel.size()) --levelIndex;
400  octreeLevel = bestOctreeLevel[levelIndex];
401  } else {
402  minDistBetweenPoints = minDistance;
403  octreeLevel = bestOctreeLevel.front();
404  }
405  }
406 
407  // look for neighbors and 'de-mark' them
408  {
409  DgmOctree::NeighboursSet neighbours;
411  *P, minDistBetweenPoints, neighbours, octreeLevel);
412  for (DgmOctree::NeighboursSet::iterator it = neighbours.begin();
413  it != neighbours.end(); ++it)
414  if (it->pointIndex != i) markers[it->pointIndex] = 0;
415  }
416 
417  // At this stage, the ith point is the only one marked in a radius
418  // of <minDistance>. Therefore it will necessarily be in the final
419  // cloud!
420  if (sampledCloud->size() == sampledCloud->capacity() &&
421  !sampledCloud->reserve(sampledCloud->capacity() +
422  c_reserveStep)) {
423  // not enough memory
424  error = true;
425  break;
426  }
427  if (!sampledCloud->addPointIndex(i)) {
428  // not enough memory
429  error = true;
430  break;
431  }
432  }
433 
434  // progress indicator
435  if (progressCb && !normProgress.oneStep()) {
436  // cancel process
437  error = true;
438  break;
439  }
440  }
441 
442  // remove unnecessarily allocated memory
443  if (!error) {
444  if (sampledCloud->capacity() > sampledCloud->size())
445  sampledCloud->resize(sampledCloud->size());
446  } else {
447  delete sampledCloud;
448  sampledCloud = nullptr;
449  }
450 
451  if (progressCb) {
452  progressCb->stop();
453  }
454 
455  if (!inputOctree) {
456  // locally computed octree
457  delete octree;
458  octree = nullptr;
459  }
460 
461  return sampledCloud;
462 }
463 
465  GenericIndexedCloudPersist* inputCloud,
466  int knn /*=6*/,
467  double nSigma /*=1.0*/,
468  DgmOctree* inputOctree /*=0*/,
469  GenericProgressCallback* progressCb /*=0*/) {
470  if (!inputCloud || knn <= 0 ||
471  inputCloud->size() <= static_cast<unsigned>(knn)) {
472  // invalid input
473  assert(false);
474  return nullptr;
475  }
476 
477  DgmOctree* octree = inputOctree;
478  if (!octree) {
479  // compute the octree if necessary
480  octree = new DgmOctree(inputCloud);
481  if (octree->build(progressCb) < 1) {
482  delete octree;
483  return nullptr;
484  }
485  }
486 
487  // output
488  ReferenceCloud* filteredCloud = nullptr;
489  for (unsigned step = 0; step < 1; ++step) // fake loop for easy break
490  {
491  unsigned pointCount = inputCloud->size();
492 
493  std::vector<PointCoordinateType> meanDistances;
494  try {
495  meanDistances.resize(pointCount, 0);
496  } catch (const std::bad_alloc&) {
497  // not enough memory
498  break;
499  }
500  double avgDist = 0, stdDev = 0;
501 
502  // 1st step: compute the average distance to the neighbors
503  {
504  // additional parameters
505  void* additionalParameters[] = {
506  reinterpret_cast<void*>(&knn),
507  reinterpret_cast<void*>(&meanDistances)};
508 
509  unsigned char octreeLevel =
511 
514  additionalParameters, true, progressCb,
515  "SOR filter") == 0) {
516  // something went wrong
517  break;
518  }
519 
520  // deduce the average distance and std. dev.
521  double sumDist = 0;
522  double sumSquareDist = 0;
523  for (unsigned i = 0; i < pointCount; ++i) {
524  sumDist += meanDistances[i];
525  sumSquareDist += meanDistances[i] * meanDistances[i];
526  }
527  avgDist = sumDist / pointCount;
528  stdDev = sqrt(
529  std::abs(sumSquareDist / pointCount - avgDist * avgDist));
530  }
531 
532  // 2nd step: remove the farthest points
533  {
534  // deduce the max distance
535  double maxDist = avgDist + nSigma * stdDev;
536 
537  filteredCloud = new ReferenceCloud(inputCloud);
538  if (!filteredCloud->reserve(pointCount)) {
539  // not enough memory
540  delete filteredCloud;
541  filteredCloud = nullptr;
542  break;
543  }
544 
545  for (unsigned i = 0; i < pointCount; ++i) {
546  if (meanDistances[i] <= maxDist) {
547  filteredCloud->addPointIndex(i);
548  }
549  }
550 
551  filteredCloud->resize(filteredCloud->size());
552  }
553  }
554 
555  if (!inputOctree) {
556  delete octree;
557  octree = 0;
558  }
559 
560  return filteredCloud;
561 }
562 
564  GenericIndexedCloudPersist* inputCloud,
565  PointCoordinateType kernelRadius,
566  double nSigma,
567  bool removeIsolatedPoints /*=false*/,
568  bool useKnn /*=false*/,
569  int knn /*=6*/,
570  bool useAbsoluteError /*=true*/,
571  double absoluteError /*=0.0*/,
572  DgmOctree* inputOctree /*=0*/,
573  GenericProgressCallback* progressCb /*=0*/) {
574  if (!inputCloud || inputCloud->size() < 2 || (useKnn && knn <= 0) ||
575  (!useKnn && kernelRadius <= 0)) {
576  // invalid input
577  assert(false);
578  return nullptr;
579  }
580 
581  DgmOctree* octree = inputOctree;
582  if (!octree) {
583  octree = new DgmOctree(inputCloud);
584  if (octree->build(progressCb) < 1) {
585  delete octree;
586  return nullptr;
587  }
588  }
589 
590  ReferenceCloud* filteredCloud = new ReferenceCloud(inputCloud);
591 
592  unsigned pointCount = inputCloud->size();
593  if (!filteredCloud->reserve(pointCount)) {
594  // not enough memory
595  if (!inputOctree) delete octree;
596  delete filteredCloud;
597  return nullptr;
598  }
599 
600  // additional parameters
601  void* additionalParameters[] = {
602  reinterpret_cast<void*>(filteredCloud),
603  reinterpret_cast<void*>(&kernelRadius),
604  reinterpret_cast<void*>(&nSigma),
605  reinterpret_cast<void*>(&removeIsolatedPoints),
606  reinterpret_cast<void*>(&useKnn),
607  reinterpret_cast<void*>(&knn),
608  reinterpret_cast<void*>(&useAbsoluteError),
609  reinterpret_cast<void*>(&absoluteError)};
610 
611  unsigned char octreeLevel = 0;
612  if (useKnn)
614  kernelRadius);
615  else
617 
619  octreeLevel, &applyNoiseFilterAtLevel, additionalParameters,
620  true, progressCb, "Noise filter") == 0) {
621  // something went wrong
622  delete filteredCloud;
623  filteredCloud = nullptr;
624  }
625 
626  if (!inputOctree) {
627  delete octree;
628  octree = nullptr;
629  }
630 
631  if (filteredCloud) {
632  filteredCloud->resize(filteredCloud->size());
633  }
634 
635  return filteredCloud;
636 }
637 
639  const DgmOctree::octreeCell& cell,
640  void** additionalParameters,
641  NormalizedProgress* nProgress /*=0*/) {
642  PointCloud* cloud = static_cast<PointCloud*>(additionalParameters[0]);
643  RESAMPLING_CELL_METHOD resamplingMethod =
644  *static_cast<RESAMPLING_CELL_METHOD*>(additionalParameters[1]);
645 
646  if (resamplingMethod == CELL_GRAVITY_CENTER) {
647  const CCVector3* P = Neighbourhood(cell.points).getGravityCenter();
648  if (!P) return false;
649  cloud->addPoint(*P);
650  } else // if (resamplingMethod == CELL_CENTER)
651  {
652  CCVector3 center;
654  center, true);
655  cloud->addPoint(center);
656  }
657 
658  if (nProgress && !nProgress->steps(cell.points->size())) {
659  return false;
660  }
661 
662  return true;
663 }
664 
666  const DgmOctree::octreeCell& cell,
667  void** additionalParameters,
668  NormalizedProgress* nProgress /*=0*/) {
669  ReferenceCloud* cloud =
670  static_cast<ReferenceCloud*>(additionalParameters[0]);
671  SUBSAMPLING_CELL_METHOD subsamplingMethod =
672  *static_cast<SUBSAMPLING_CELL_METHOD*>(additionalParameters[1]);
673 
674  unsigned selectedPointIndex = 0;
675  unsigned pointsCount = cell.points->size();
676 
677  if (subsamplingMethod == RANDOM_POINT) {
678  selectedPointIndex = (static_cast<unsigned>(rand()) % pointsCount);
679 
680  if (nProgress && !nProgress->steps(pointsCount)) {
681  return false;
682  }
683  } else // if (subsamplingMethod == NEAREST_POINT_TO_CELL_CENTER)
684  {
685  CCVector3 center;
687  center, true);
688 
689  PointCoordinateType minSquareDist =
690  (*cell.points->getPoint(0) - center).norm2();
691 
692  for (unsigned i = 1; i < pointsCount; ++i) {
693  PointCoordinateType squareDist =
694  (*cell.points->getPoint(i) - center).norm2();
695  if (squareDist < minSquareDist) {
696  selectedPointIndex = i;
697  minSquareDist = squareDist;
698  }
699 
700  if (nProgress && !nProgress->oneStep()) {
701  return false;
702  }
703  }
704  }
705 
706  return cloud->addPointIndex(
707  cell.points->getPointGlobalIndex(selectedPointIndex));
708 }
709 
711  const DgmOctree::octreeCell& cell,
712  void** additionalParameters,
713  NormalizedProgress* nProgress /*=0*/) {
714  ReferenceCloud* cloud =
715  static_cast<ReferenceCloud*>(additionalParameters[0]);
716  PointCoordinateType kernelRadius =
717  *static_cast<PointCoordinateType*>(additionalParameters[1]);
718  double nSigma = *static_cast<double*>(additionalParameters[2]);
719  bool removeIsolatedPoints = *static_cast<bool*>(additionalParameters[3]);
720  bool useKnn = *static_cast<bool*>(additionalParameters[4]);
721  int knn = *static_cast<int*>(additionalParameters[5]);
722  bool useAbsoluteError = *static_cast<bool*>(additionalParameters[6]);
723  double absoluteError = *static_cast<double*>(additionalParameters[7]);
724 
725  // structure for nearest neighbors search
727  nNSS.level = cell.level;
728  nNSS.prepare(kernelRadius, cell.parentOctree->getCellSize(nNSS.level));
729  if (useKnn) {
730  nNSS.minNumberOfNeighbors = knn;
731  }
732  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
733  true);
734  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
735  nNSS.cellCenter);
736 
737  unsigned n = cell.points->size(); // number of points in the current cell
738 
739  // for each point in the cell
740  for (unsigned i = 0; i < n; ++i) {
741  cell.points->getPoint(i, nNSS.queryPoint);
742 
743  // look for neighbors (either inside a sphere or the k nearest ones)
744  // warning: there may be more points at the end of
745  // nNSS.pointsInNeighbourhood than the actual nearest neighbors
746  // (neighborCount)!
747  unsigned neighborCount = 0;
748 
749  if (useKnn)
750  neighborCount =
752  nNSS);
753  else
754  neighborCount =
756  nNSS, kernelRadius, false);
757 
758  if (neighborCount >
759  3) // we want 3 points or more (other than the point itself!)
760  {
761  // find the query point in the nearest neighbors set and place it at
762  // the end
763  const unsigned globalIndex = cell.points->getPointGlobalIndex(i);
764  unsigned localIndex = 0;
765  while (localIndex < neighborCount &&
766  nNSS.pointsInNeighbourhood[localIndex].pointIndex !=
767  globalIndex)
768  ++localIndex;
769  // the query point should be in the nearest neighbors set!
770  assert(localIndex < neighborCount);
771  if (localIndex + 1 <
772  neighborCount) // no need to swap with another point if it's
773  // already at the end!
774  {
775  std::swap(nNSS.pointsInNeighbourhood[localIndex],
776  nNSS.pointsInNeighbourhood[neighborCount - 1]);
777  }
778 
779  unsigned realNeighborCount = neighborCount - 1;
780  DgmOctreeReferenceCloud neighboursCloud(
781  &nNSS.pointsInNeighbourhood,
782  realNeighborCount); // we don't take the query point into
783  // account!
784  Neighbourhood Z(&neighboursCloud);
785 
786  const PointCoordinateType* lsPlane = Z.getLSPlane();
787  if (lsPlane) {
788  double maxD = absoluteError;
789  if (!useAbsoluteError) {
790  // compute the std. dev. to this plane
791  double sum_d = 0;
792  double sum_d2 = 0;
793  for (unsigned j = 0; j < realNeighborCount; ++j) {
794  const CCVector3* P = neighboursCloud.getPoint(j);
796  computePoint2PlaneDistance(P, lsPlane);
797  sum_d += d;
798  sum_d2 += d * d;
799  }
800 
801  double stddev = sqrt(std::abs(sum_d2 * realNeighborCount -
802  sum_d * sum_d)) /
803  realNeighborCount;
804  maxD = stddev * nSigma;
805  }
806 
807  // distance from the query point to the plane
809  computePoint2PlaneDistance(
810  &nNSS.queryPoint, lsPlane));
811 
812  if (d <= maxD) {
813  cloud->addPointIndex(globalIndex);
814  }
815  } else {
816  // TODO: ???
817  }
818  } else {
819  // not enough points to fit a plane AND compute distances to it
820  if (!removeIsolatedPoints) {
821  // we keep the point
822  unsigned globalIndex = cell.points->getPointGlobalIndex(i);
823  cloud->addPointIndex(globalIndex);
824  }
825  }
826 
827  if (nProgress && !nProgress->oneStep()) {
828  return false;
829  }
830  }
831 
832  return true;
833 }
834 
836  const DgmOctree::octreeCell& cell,
837  void** additionalParameters,
838  NormalizedProgress* nProgress /*=0*/) {
839  int knn = *static_cast<int*>(additionalParameters[0]);
840  std::vector<PointCoordinateType>& meanDistances =
841  *static_cast<std::vector<PointCoordinateType>*>(
842  additionalParameters[1]);
843 
844  // structure for nearest neighbors search
846  nNSS.level = cell.level;
847  nNSS.minNumberOfNeighbors =
848  knn; // DGM: I woud have put knn+1 (as the point itself will be
849  // ignored) but in this case we won't get the same result as
850  // PCL!
851  cell.parentOctree->getCellPos(cell.truncatedCode, cell.level, nNSS.cellPos,
852  true);
853  cell.parentOctree->computeCellCenter(nNSS.cellPos, cell.level,
854  nNSS.cellCenter);
855 
856  unsigned n = cell.points->size(); // number of points in the current cell
857 
858  // for each point in the cell
859  for (unsigned i = 0; i < n; ++i) {
860  cell.points->getPoint(i, nNSS.queryPoint);
861  const unsigned globalIndex = cell.points->getPointGlobalIndex(i);
862 
863  // look for the k nearest neighbors
865  double sumDist = 0;
866  unsigned count = 0;
867  for (int j = 0; j < knn; ++j) {
868  if (nNSS.pointsInNeighbourhood[j].pointIndex != globalIndex) {
869  sumDist += sqrt(nNSS.pointsInNeighbourhood[j].squareDistd);
870  ++count;
871  }
872  }
873 
874  if (count) {
875  meanDistances[globalIndex] =
876  static_cast<PointCoordinateType>(sumDist / count);
877  } else {
878  // shouldn't happen
879  assert(false);
880  }
881 
882  if (nProgress && !nProgress->oneStep()) {
883  return false;
884  }
885  }
886 
887  return true;
888 }
float PointCoordinateType
Type of the coordinates of a (N-D) point.
Definition: CVTypes.h:16
int count
static bool subsampleCellAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
"Cellular" function to select a unique point inside an octree cell
static GenericIndexedCloud * resampleCloudWithOctree(GenericIndexedCloudPersist *cloud, int newNumberOfPoints, RESAMPLING_CELL_METHOD resamplingMethod, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Resamples a point cloud (process based on the octree)
static PointCloud * resampleCloudWithOctreeAtLevel(GenericIndexedCloudPersist *cloud, unsigned char octreeLevel, RESAMPLING_CELL_METHOD resamplingMethod, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Resamples a point cloud (process based on the octree)
static bool applyNoiseFilterAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
"Cellular" function to apply the noise filter inside an octree cell
static ReferenceCloud * UniformDownSample(GenericIndexedCloudPersist *cloud, unsigned every_k_points)
static ReferenceCloud * subsampleCloudWithOctree(GenericIndexedCloudPersist *cloud, int newNumberOfPoints, SUBSAMPLING_CELL_METHOD subsamplingMethod, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Subsamples a point cloud (process based on the octree)
static ReferenceCloud * noiseFilter(GenericIndexedCloudPersist *cloud, PointCoordinateType kernelRadius, double nSigma, bool removeIsolatedPoints=false, bool useKnn=false, int knn=6, bool useAbsoluteError=true, double absoluteError=0.0, DgmOctree *octree=nullptr, GenericProgressCallback *progressCb=nullptr)
Noise filter based on the distance to the approximate local surface.
static ReferenceCloud * subsampleCloudRandomly(GenericIndexedCloudPersist *cloud, unsigned newNumberOfPoints, GenericProgressCallback *progressCb=nullptr)
Subsamples a point cloud (process based on random selections)
static ReferenceCloud * subsampleCloudWithOctreeAtLevel(GenericIndexedCloudPersist *cloud, unsigned char octreeLevel, SUBSAMPLING_CELL_METHOD subsamplingMethod, GenericProgressCallback *progressCb=nullptr, DgmOctree *inputOctree=nullptr)
Subsamples a point cloud (process based on the octree)
static bool applySORFilterAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
"Cellular" function to apply the SOR filter inside an octree cell
static bool resampleCellAtLevel(const DgmOctree::octreeCell &cell, void **additionalParameters, NormalizedProgress *nProgress=nullptr)
static ReferenceCloud * sorFilter(GenericIndexedCloudPersist *cloud, int knn=6, double nSigma=1.0, DgmOctree *octree=nullptr, GenericProgressCallback *progressCb=nullptr)
Statistical Outliers Removal (SOR) filter.
static ReferenceCloud * resampleCloudSpatially(GenericIndexedCloudPersist *cloud, PointCoordinateType minDistance, const SFModulationParams &modParams, DgmOctree *octree=nullptr, GenericProgressCallback *progressCb=nullptr)
Resamples a point cloud (process based on inter point distance)
A kind of ReferenceCloud based on the DgmOctree::NeighboursSet structure.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
The octree structure used throughout the library.
Definition: DgmOctree.h:39
GenericIndexedCloudPersist * associatedCloud() const
Returns the associated cloud.
Definition: DgmOctree.h:1191
unsigned char findBestLevelForAGivenCellNumber(unsigned indicativeNumberOfCells) const
Definition: DgmOctree.cpp:2766
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
int getPointsInSphericalNeighbourhood(const CCVector3 &sphereCenter, PointCoordinateType radius, NeighboursSet &neighbours, unsigned char level) const
Returns the points falling inside a sphere.
Definition: DgmOctree.cpp:1846
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
const unsigned & getCellNumber(unsigned char level) const
Returns the number of cells for a given level of subdivision.
Definition: DgmOctree.h:1038
std::vector< PointDescriptor > NeighboursSet
A set of neighbours.
Definition: DgmOctree.h:133
unsigned char findBestLevelForAGivenPopulationPerCell(unsigned indicativeNumberOfPointsPerCell) const
Definition: DgmOctree.cpp:2737
static ScalarType computePoint2PlaneDistance(const CCVector3 *P, const PointCoordinateType *planeEquation)
Computes the (signed) distance between a point and a plane.
virtual unsigned size() const =0
Returns the number of points.
virtual ScalarType getPointScalarValue(unsigned pointIndex) const =0
Returns the ith point associated scalar value.
A generic 3D point cloud with index-based and presistent access to points.
A generic 3D point cloud with index-based point access.
virtual const CCVector3 * getPoint(unsigned index) const =0
Returns the ith point.
virtual void stop()=0
Notifies the fact that the process has ended.
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.
const PointCoordinateType * getLSPlane()
Returns best interpolating plane equation (Least-square)
const CCVector3 * getGravityCenter()
Returns gravity center.
bool steps(unsigned n)
Increments total progress value of more than a single unit.
bool oneStep()
Increments total progress value of a single unit.
virtual bool reserve(unsigned newCapacity)
Reserves memory for the point database.
void addPoint(const CCVector3 &P)
Adds a 3D point to the database.
A very simple point cloud (no point duplication)
virtual void swap(unsigned i, unsigned j)
Swaps two point references.
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
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.
virtual unsigned capacity() const
Returns max capacity.
virtual bool reserve(unsigned n)
Reserves some memory for hosting the point references.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
static void computeScalarFieldExtremas(const GenericCloud *theCloud, ScalarType &minV, ScalarType &maxV)
Compute the extreme values of a scalar field.
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
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
::ccPointCloud PointCloud
Definition: PointCloud.h:19
Generic file read and write utility for python interface.
void swap(cloudViewer::core::SmallVectorImpl< T > &LHS, cloudViewer::core::SmallVectorImpl< T > &RHS)
Implement std::swap in terms of SmallVector swap.
Definition: SmallVector.h:1370
double stddev(std::vector< double > const &func)
Definition: qAutoSeg.cpp:86
cloudViewer::NormalizedProgress * nProgress
cloudViewer::DgmOctree * octree
unsigned char octreeLevel
Parameters for the scalar-field based modulation of a parameter.
bool enabled
Whether the modulation is enabled or not.
double b
Modulation scheme: y = a.sf + b.
double a
Modulation scheme: y = a.sf + b.
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