ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
TrueKdTree.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 "TrueKdTree.h"
9 
10 // local
12 #include "Neighbourhood.h"
13 #include "ParallelSort.h"
14 
15 using namespace cloudViewer;
16 
18  : m_root(nullptr),
19  m_associatedCloud(cloud),
20  m_maxError(0.0),
21  m_errorMeasure(DistanceComputationTools::RMS),
22  m_minPointCountPerCell(3),
23  m_maxPointCountPerCell(0) {
24  assert(m_associatedCloud);
25 }
26 
28 
30  if (m_root) {
31  delete m_root;
32  m_root = nullptr;
33  }
34 }
35 
36 // shared structure used to sort the points along a single dimension (see
37 // TrueKdTree::split)
38 static std::vector<PointCoordinateType> s_sortedCoordsForSplit;
40 static unsigned s_lastProgressCount = 0;
41 static unsigned s_totalProgressCount = 0;
42 static unsigned s_lastProgress = 0;
43 
44 static void InitProgress(GenericProgressCallback* progressCb,
45  unsigned totalCount) {
46  s_progressCb = totalCount ? progressCb : nullptr;
47  s_totalProgressCount = totalCount;
49  s_lastProgress = 0;
50 
51  if (s_progressCb) {
52  if (progressCb->textCanBeEdited()) {
53  s_progressCb->setMethodTitle("Kd-tree computation");
54  char info[256];
55  sprintf(info, "Points: %u", totalCount);
56  s_progressCb->setInfo(info);
57  }
59  }
60 }
61 
62 static inline void UpdateProgress(unsigned increment) {
63  if (s_progressCb) {
64  assert(s_totalProgressCount != 0);
65  s_lastProgressCount += increment;
66  float fPercent = static_cast<float>(s_lastProgressCount) /
67  s_totalProgressCount * 100.0f;
68  unsigned uiPercent = static_cast<unsigned>(fPercent);
69  if (uiPercent > s_lastProgress) {
70  s_progressCb->update(fPercent);
71  s_lastProgress = uiPercent;
72  }
73  }
74 }
75 
77  assert(subset); // subset will always be taken care of by this method
78 
79  unsigned count = subset->size();
80 
81  const PointCoordinateType* planeEquation =
82  Neighbourhood(subset).getLSPlane();
83  if (!planeEquation) {
84  // an error occurred during LS plane computation?! (maybe the (3) points
85  // are aligned) we return an invalid Leaf (so as the above level
86  // understands that it's not a memory issue)
87  delete subset;
88  PointCoordinateType fakePlaneEquation[4] = {0, 0, 0, 0};
89  return new Leaf(nullptr, fakePlaneEquation,
90  static_cast<ScalarType>(-1));
91  }
92 
93  // we always split sets larger than a given size
94  ScalarType error = -1;
96  assert(std::abs(CCVector3(planeEquation).norm2() - 1.0) < 1.0e-6);
97  error = (count > 3
99  subset, planeEquation, m_errorMeasure)
100  : 0);
101 
102  // we can't split cells with less than twice the minimum number of
103  // points per cell! (and min >= 3 so as to fit a plane)
104  bool isLeaf =
106  if (isLeaf) {
108  // the Leaf class takes ownership of the subset!
109  return new Leaf(subset, planeEquation, error);
110  }
111  }
112 
113  /*** proceed with a 'standard' binary partition ***/
114 
115  // cell limits (dimensions)
116  CCVector3 dims;
117  {
118  CCVector3 bbMin, bbMax;
119  subset->getBoundingBox(bbMin, bbMax);
120  dims = bbMax - bbMin;
121  }
122 
123  // find the largest dimension
124  uint8_t splitDim = X_DIM;
125  if (dims.y > dims.x) splitDim = Y_DIM;
126  if (dims.z > dims.u[splitDim]) splitDim = Z_DIM;
127 
128  // find the median by sorting the points coordinates
129  assert(s_sortedCoordsForSplit.size() >= static_cast<std::size_t>(count));
130  for (unsigned i = 0; i < count; ++i) {
131  const CCVector3* P = subset->getPoint(i);
132  s_sortedCoordsForSplit[i] = P->u[splitDim];
133  }
134 
136  s_sortedCoordsForSplit.begin() + count);
137 
138  unsigned splitCount = count / 2;
139  assert(splitCount >= 3); // count >= 6 (see above)
140 
141  // we must check that the split value is the 'first one'
142  if (s_sortedCoordsForSplit[splitCount - 1] ==
143  s_sortedCoordsForSplit[splitCount]) {
144  if (s_sortedCoordsForSplit[2] !=
145  s_sortedCoordsForSplit[splitCount]) // can we go backward?
146  {
147  while (/*splitCount>0 &&*/ s_sortedCoordsForSplit[splitCount - 1] ==
148  s_sortedCoordsForSplit[splitCount]) {
149  assert(splitCount > 3);
150  --splitCount;
151  }
152  } else if (s_sortedCoordsForSplit[count - 3] !=
153  s_sortedCoordsForSplit[splitCount]) // can we go forward?
154  {
155  do {
156  ++splitCount;
157  assert(splitCount < count - 3);
158  } while (/*splitCount+1<count &&*/ s_sortedCoordsForSplit
159  [splitCount] ==
160  s_sortedCoordsForSplit[splitCount - 1]);
161  } else // in fact we can't split this cell!
162  {
164  if (error < 0)
167  subset, planeEquation,
169  : 0);
170  // the Leaf class takes ownership of the subset!
171  return new Leaf(subset, planeEquation, error);
172  }
173  }
174 
175  PointCoordinateType splitCoord =
176  s_sortedCoordsForSplit[splitCount]; // count > 3 --> splitCount >=
177  // 2
178 
179  ReferenceCloud* leftSubset =
180  new ReferenceCloud(subset->getAssociatedCloud());
181  ReferenceCloud* rightSubset =
182  new ReferenceCloud(subset->getAssociatedCloud());
183  if (!leftSubset->reserve(splitCount) ||
184  !rightSubset->reserve(count - splitCount)) {
185  // not enough memory!
186  delete leftSubset;
187  delete rightSubset;
188  delete subset;
189  return nullptr;
190  }
191 
192  // fill subsets
193  for (unsigned i = 0; i < count; ++i) {
194  const CCVector3* P = subset->getPoint(i);
195  if (P->u[splitDim] < splitCoord) {
196  leftSubset->addPointIndex(subset->getPointGlobalIndex(i));
197  } else {
198  rightSubset->addPointIndex(subset->getPointGlobalIndex(i));
199  }
200  }
201 
202  // process subsets (if any)
203  BaseNode* leftChild = split(leftSubset);
204  if (!leftChild) {
205  delete subset;
206  delete rightSubset;
207  return nullptr;
208  }
209 
210  BaseNode* rightChild = split(rightSubset);
211  if (!rightChild) {
212  delete subset;
213  delete leftChild;
214  return nullptr;
215  }
216 
217  if ((leftChild->isLeaf() &&
218  static_cast<Leaf*>(leftChild)->points == nullptr) ||
219  (rightChild->isLeaf() &&
220  static_cast<Leaf*>(rightChild)->points == nullptr)) {
221  // at least one of the subsets couldn't be fitted with a plane!
222  delete leftChild;
223  delete rightChild;
224 
225  // this node will become a leaf!
227  // the Leaf class takes ownership of the subset!
228  return new Leaf(subset, planeEquation, error);
229  }
230 
231  // we can now delete the subset
232  delete subset;
233  subset = nullptr;
234 
235  Node* node = new Node;
236  {
237  node->leftChild = leftChild;
238  leftChild->parent = node;
239  node->rightChild = rightChild;
240  rightChild->parent = node;
241  node->splitDim = splitDim;
242  node->splitValue = splitCoord;
243  }
244  return node;
245 }
246 
247 bool TrueKdTree::build(double maxError,
249  errorMeasure /*=DistanceComputationTools::RMS*/,
250  unsigned minPointCountPerCell /*=3*/,
251  unsigned maxPointCountPerCell /*=0*/,
252  GenericProgressCallback* progressCb /*=0*/) {
253  if (!m_associatedCloud) return false;
254 
255  // tree already computed! (call clear before)
256  if (m_root) return false;
257 
258  unsigned count = m_associatedCloud->size();
259  if (count == 0) // no point, no node!
260  {
261  return false;
262  }
263 
264  // structures used to sort the points along the 3 dimensions
265  try {
267  } catch (const std::bad_alloc&) {
268  // not enough memory!
269  return false;
270  }
271 
272  // initial 'subset' to start recursion
274  if (!subset->addPointIndex(0, count)) {
275  // not enough memory
276  delete subset;
277  return false;
278  }
279 
280  InitProgress(progressCb, count);
281 
282  // launch recursive process
283  m_maxError = maxError;
284  m_minPointCountPerCell = std::max<unsigned>(3, minPointCountPerCell);
285  m_maxPointCountPerCell = std::max<unsigned>(
286  2 * minPointCountPerCell,
287  maxPointCountPerCell); // the max number of point per cell can't be
288  // < 2*min
289  m_errorMeasure = errorMeasure;
290  m_root = split(subset);
291 
292  // clear static structure
293  s_sortedCoordsForSplit.clear();
294 
295  return (m_root != nullptr);
296 }
297 
300 public:
302  : m_leaves(&leaves) {}
303 
305  if (!node) return;
306 
307  if (node->isNode()) {
308  visit(static_cast<TrueKdTree::Node*>(node)->leftChild);
309  visit(static_cast<TrueKdTree::Node*>(node)->rightChild);
310  } else // if (node->isLeaf())
311  {
312  assert(m_leaves);
313  m_leaves->push_back(static_cast<TrueKdTree::Leaf*>(node));
314  }
315  }
316 
317 protected:
319 };
320 
321 bool TrueKdTree::getLeaves(LeafVector& leaves) const {
322  if (!m_root) return false;
323 
324  try {
325  GetLeavesVisitor(leaves).visit(m_root);
326  } catch (const std::bad_alloc&) {
327  return false;
328  }
329 
330  return true;
331 }
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
int count
#define ParallelSort
Definition: ParallelSort.h:33
Recursive visitor for TrueKdTree::getLeaves.
Definition: TrueKdTree.cpp:299
void visit(TrueKdTree::BaseNode *node)
Definition: TrueKdTree.cpp:304
GetLeavesVisitor(TrueKdTree::LeafVector &leaves)
Definition: TrueKdTree.cpp:301
TrueKdTree::LeafVector * m_leaves
Definition: TrueKdTree.cpp:318
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
static ScalarType ComputeCloud2PlaneDistance(cloudViewer::GenericCloud *cloud, const PointCoordinateType *planeEquation, ERROR_MEASURES measureType)
virtual unsigned size() const =0
Returns the number of points.
A generic 3D point cloud with index-based and presistent access to points.
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)
A very simple point cloud (no point duplication)
virtual bool addPointIndex(unsigned globalIndex)
Point global index insertion mechanism.
void getBoundingBox(CCVector3 &bbMin, CCVector3 &bbMax) override
Returns the cloud bounding box.
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 reserve(unsigned n)
Reserves some memory for hosting the point references.
const CCVector3 * getPoint(unsigned index) const override
Returns the ith point.
ReferenceCloud * points
Definition: TrueKdTree.h:83
PointCoordinateType splitValue
Definition: TrueKdTree.h:57
double m_maxError
Max error for planarity-based split strategy (see m_errorMeasure)
Definition: TrueKdTree.h:159
unsigned m_maxPointCountPerCell
Max number of points per cell (speed-up)
Definition: TrueKdTree.h:172
BaseNode * m_root
Root node.
Definition: TrueKdTree.h:153
bool getLeaves(LeafVector &leaves) const
Returns all leaf nodes.
Definition: TrueKdTree.cpp:321
DistanceComputationTools::ERROR_MEASURES m_errorMeasure
Error measurement.
Definition: TrueKdTree.h:162
TrueKdTree(GenericIndexedCloudPersist *cloud)
Default constructor.
Definition: TrueKdTree.cpp:17
bool build(double maxError, DistanceComputationTools::ERROR_MEASURES errorMeasure=DistanceComputationTools::RMS, unsigned minPointCountPerCell=3, unsigned maxPointCountPerCell=0, GenericProgressCallback *progressCb=nullptr)
Builds KD-tree.
Definition: TrueKdTree.cpp:247
unsigned m_minPointCountPerCell
Min number of points per cell (speed-up)
Definition: TrueKdTree.h:167
BaseNode * split(ReferenceCloud *subset)
Recursive split process.
Definition: TrueKdTree.cpp:76
static const uint8_t X_DIM
Definition: TrueKdTree.h:26
std::vector< Leaf * > LeafVector
A vector of leaves.
Definition: TrueKdTree.h:105
static const uint8_t Z_DIM
Definition: TrueKdTree.h:28
GenericIndexedCloudPersist * m_associatedCloud
Associated cloud.
Definition: TrueKdTree.h:156
void clear()
Clears structure.
Definition: TrueKdTree.cpp:29
static const uint8_t Y_DIM
Definition: TrueKdTree.h:27
~TrueKdTree()
Destructor.
Definition: TrueKdTree.cpp:27
static std::vector< PointCoordinateType > s_sortedCoordsForSplit
Definition: TrueKdTree.cpp:38
static void UpdateProgress(unsigned increment)
Definition: TrueKdTree.cpp:62
static unsigned s_lastProgressCount
Definition: TrueKdTree.cpp:40
static GenericProgressCallback * s_progressCb
Definition: TrueKdTree.cpp:39
static void InitProgress(GenericProgressCallback *progressCb, unsigned totalCount)
Definition: TrueKdTree.cpp:44
static unsigned s_totalProgressCount
Definition: TrueKdTree.cpp:41
static unsigned s_lastProgress
Definition: TrueKdTree.cpp:42
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
static void error(char *msg)
Definition: lsd.c:159
Generic file read and write utility for python interface.