Super4PCS Library  V1.1.2(719f5c0)
kdtree.h
1 // Copyright 2014 Gael Guennebaud, Nicolas Mellado
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // -------------------------------------------------------------------------- //
16 //
17 // Authors: Gael Guennebaud, Nicolas Mellado
18 //
19 // Part of the implementation of the Super 4-points Congruent Sets (Super 4PCS)
20 // algorithm presented in:
21 //
22 // Super 4PCS: Fast Global Pointcloud Registration via Smart Indexing
23 // Nicolas Mellado, Dror Aiger, Niloy J. Mitra
24 // Symposium on Geometry Processing 2014.
25 //
26 // Data acquisition in large-scale scenes regularly involves accumulating
27 // information across multiple scans. A common approach is to locally align scan
28 // pairs using Iterative Closest Point (ICP) algorithm (or its variants), but
29 // requires static scenes and small motion between scan pairs. This prevents
30 // accumulating data across multiple scan sessions and/or different acquisition
31 // modalities (e.g., stereo, depth scans). Alternatively, one can use a global
32 // registration algorithm allowing scans to be in arbitrary initial poses. The
33 // state-of-the-art global registration algorithm, 4PCS, however has a quadratic
34 // time complexity in the number of data points. This vastly limits its
35 // applicability to acquisition of large environments. We present Super 4PCS for
36 // global pointcloud registration that is optimal, i.e., runs in linear time (in
37 // the number of data points) and is also output sensitive in the complexity of
38 // the alignment problem based on the (unknown) overlap across scan pairs.
39 // Technically, we map the algorithm as an ‘instance problem’ and solve it
40 // efficiently using a smart indexing data organization. The algorithm is
41 // simple, memory-efficient, and fast. We demonstrate that Super 4PCS results in
42 // significant speedup over alternative approaches and allows unstructured
43 // efficient acquisition of scenes at scales previously not possible. Complete
44 // source code and datasets are available for research use at
45 // http://geometry.cs.ucl.ac.uk/projects/2014/super4PCS/.
46 
47 #ifndef _SUPER4PCS_ACCELERATORS_KDTREE_H
48 #define _SUPER4PCS_ACCELERATORS_KDTREE_H
49 
50 #include "super4pcs/utils/disablewarnings.h"
51 #include "super4pcs/accelerators/bbox.h"
52 
53 #include <Eigen/Core>
54 
55 #include <limits>
56 #include <iostream>
57 #include <numeric> //iota
58 
59 // max depth of the tree
60 #define KD_MAX_DEPTH 32
61 
62 // number of neighbors
63 #define KD_POINT_PER_CELL 64
64 
65 
66 namespace GlobalRegistration{
67 
140 template<typename _Scalar, typename _Index = int >
141 class KdTree
142 {
143 public:
144  struct KdNode
145  {
146  union {
147  struct {
148  float splitValue;
149  unsigned int firstChildId:24;
150  unsigned int dim:2;
151  unsigned int leaf:1;
152  };
153  struct {
154  unsigned int start;
155  unsigned short size;
156  };
157  };
158  };
159 
160  typedef _Scalar Scalar;
161  typedef _Index Index;
162 
163  static constexpr Index invalidIndex() { return -1; }
164 
165  typedef Eigen::Matrix<Scalar,3,1> VectorType;
167 
168  typedef std::vector<KdNode> NodeList;
169  typedef std::vector<VectorType> PointList;
170  typedef std::vector<Index> IndexList;
171 
173  struct QueryNode
174  {
175  inline QueryNode() {}
176  inline QueryNode(unsigned int id) : nodeId(id) {}
178  unsigned int nodeId;
180  Scalar sq;
181  };
182 
183  template <int _stackSize = 64>
184  struct RangeQuery
185  {
186  VectorType queryPoint;
187  Scalar sqdist;
188  QueryNode nodeStack[_stackSize];
189  };
190 
191  inline const NodeList& _getNodes (void) { return mNodes; }
192  inline const PointList& _getPoints (void) { return mPoints; }
193  inline const PointList& _getIndices (void) { return mIndices; }
194 
195 
196 public:
198  KdTree(const PointList& points,
199  unsigned int nofPointsPerCell = KD_POINT_PER_CELL,
200  unsigned int maxDepth = KD_MAX_DEPTH );
201 
203  KdTree( unsigned int size = 0,
204  unsigned int nofPointsPerCell = KD_POINT_PER_CELL,
205  unsigned int maxDepth = KD_MAX_DEPTH );
206 
208  template <class VectorDerived>
209  inline void add( const VectorDerived &p ){
210  // this is ok since the memory has been reserved at construction time
211  mPoints.push_back(p);
212  mIndices.push_back(mIndices.size());
213  mAABB.extend(p);
214  }
215 
216  inline void add(Scalar *position){
217  add(Eigen::Map< Eigen::Matrix<Scalar, 3, 1> >(position));
218  }
219 
221  inline
222  void finalize( );
223 
224  inline const AxisAlignedBoxType& aabb() const {return mAABB; }
225 
226  ~KdTree();
227 
231  template<int stackSize, typename Container = std::vector<VectorType> >
232  inline void
233  doQueryDist(RangeQuery<stackSize>& query,
234  Container& result) const {
235  _doQueryDistIndicesWithFunctor(query, [&result,this](unsigned int i){
236  result.push_back(mPoints[i]);
237  });
238  }
239 
243  template<int stackSize, typename IndexContainer = std::vector<Index> >
244  inline void
245  doQueryDistIndices(RangeQuery<stackSize>& query,
246  IndexContainer& result) const {
247  _doQueryDistIndicesWithFunctor(query, [&result,this](unsigned int i){
248  result.push_back(mIndices[i]);
249  });
250  }
251 
255  template<int stackSize, typename Functor>
256  inline void
257  doQueryDistProcessIndices(RangeQuery<stackSize> &query,
258  Functor f) const {
259  _doQueryDistIndicesWithFunctor(query, [f,this](unsigned int i){
260  f(mIndices[i]);
261  });
262  }
263 
268  template<int stackSize>
269  inline Index
270  doQueryRestrictedClosestIndex(RangeQuery<stackSize> &query,
271  int currentId = -1) const;
272 
273  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
274 
275 protected:
276 
282  inline
283  unsigned int split(int start, int end, unsigned int dim, Scalar splitValue);
284 
285  void createTree(unsigned int nodeId,
286  unsigned int start,
287  unsigned int end,
288  unsigned int level,
289  unsigned int targetCellsize,
290  unsigned int targetMaxDepth);
291 
292 
296  template<int stackSize, typename Functor >
297  inline void
298  _doQueryDistIndicesWithFunctor(RangeQuery<stackSize>& query,
299  Functor f) const;
300 protected:
301 
302  PointList mPoints;
303  IndexList mIndices;
304  AxisAlignedBoxType mAABB;
305  NodeList mNodes;
306 
307  unsigned int _nofPointsPerCell;
308  unsigned int _maxDepth;
309 };
310 
311 
312 
313 
314 
318 template<typename Scalar, typename Index>
320  unsigned int nofPointsPerCell,
321  unsigned int maxDepth)
322  : mPoints(points),
323  mIndices(points.size()),
324  _nofPointsPerCell(nofPointsPerCell),
325  _maxDepth(maxDepth)
326 {
327  mAABB.extend(points.cbegin(), points.cend());
328  std::iota (mIndices.begin(), mIndices.end(), 0); // Fill with 0, 1, ..., 99.
329  finalize();
330 }
331 
338 template<typename Scalar, typename Index>
340  unsigned int nofPointsPerCell,
341  unsigned int maxDepth)
342  : _nofPointsPerCell(nofPointsPerCell),
343  _maxDepth(maxDepth)
344 {
345  mPoints.reserve(size);
346  mIndices.reserve(size);
347 }
348 
349 template<typename Scalar, typename Index>
350 void
352 {
353  mNodes.clear();
354  mNodes.reserve(4*mPoints.size()/_nofPointsPerCell);
355  mNodes.emplace_back();
356  mNodes.back().leaf = 0;
357 #ifdef DEBUG
358  std::cout << "create tree" << std::endl;
359 #endif
360  createTree(0, 0, mPoints.size(), 1, _nofPointsPerCell, _maxDepth);
361 #ifdef DEBUG
362  std::cout << "create tree ... DONE (" << mPoints.size() << " points)" << std::endl;
363 #endif
364 }
365 
366 template<typename Scalar, typename Index>
368 {
369 }
370 
371 
388 template<typename Scalar, typename Index>
389 template<int stackSize>
390 Index
392  RangeQuery<stackSize>& query,
393  int currentId) const
394 {
395 
396  Index cl_id = invalidIndex();
397  Scalar cl_dist = query.sqdist;
398 
399  query.nodeStack[0].nodeId = 0;
400  query.nodeStack[0].sq = 0.f;
401  unsigned int count = 1;
402 
403  //int nbLoop = 0;
404  while (count)
405  {
406  //nbLoop++;
407  QueryNode& qnode = query.nodeStack[count-1];
408  const KdNode& node = mNodes[qnode.nodeId];
409 
410  if (qnode.sq < cl_dist)
411  {
412  if (node.leaf)
413  {
414  --count; // pop
415  const int end = node.start+node.size;
416  for (int i=node.start ; i<end ; ++i){
417  const Scalar sqdist = (query.queryPoint - mPoints[i]).squaredNorm();
418  if (sqdist <= cl_dist && mIndices[i] != currentId){
419  cl_dist = sqdist;
420  cl_id = mIndices[i];
421  }
422  }
423  }
424  else
425  {
426  // replace the stack top by the farthest and push the closest
427  const Scalar new_off = query.queryPoint[node.dim] - node.splitValue;
428 
429  //std::cout << "new_off = " << new_off << std::endl;
430 
431  if (new_off < 0.)
432  {
433  query.nodeStack[count].nodeId = node.firstChildId; // stack top the farthest
434  qnode.nodeId = node.firstChildId+1; // push the closest
435  }
436  else
437  {
438  query.nodeStack[count].nodeId = node.firstChildId+1;
439  qnode.nodeId = node.firstChildId;
440  }
441  query.nodeStack[count].sq = qnode.sq;
442  qnode.sq = new_off*new_off;
443  ++count;
444  }
445  }
446  else
447  {
448  // pop
449  --count;
450  }
451  }
452  return cl_id;
453 }
454 
462 template<typename Scalar, typename Index>
463 template<int stackSize, typename Functor >
464 void
466  RangeQuery<stackSize>& query,
467  Functor f) const
468 {
469  query.nodeStack[0].nodeId = 0;
470  query.nodeStack[0].sq = 0.f;
471  unsigned int count = 1;
472 
473  while (count)
474  {
475  QueryNode& qnode = query.nodeStack[count-1];
476  const KdNode & node = mNodes[qnode.nodeId];
477 
478  if (qnode.sq < query.sqdist)
479  {
480  if (node.leaf)
481  {
482  --count; // pop
483  unsigned int end = node.start+node.size;
484  for (unsigned int i=node.start ; i<end ; ++i)
485  if ( (query.queryPoint - mPoints[i]).squaredNorm() < query.sqdist){
486  f(i);
487  }
488  }
489  else
490  {
491  // replace the stack top by the farthest and push the closest
492  Scalar new_off = query.queryPoint[node.dim] - node.splitValue;
493  if (new_off < 0.)
494  {
495  query.nodeStack[count].nodeId = node.firstChildId;
496  qnode.nodeId = node.firstChildId+1;
497  }
498  else
499  {
500  query.nodeStack[count].nodeId = node.firstChildId+1;
501  qnode.nodeId = node.firstChildId;
502  }
503  query.nodeStack[count].sq = qnode.sq;
504  qnode.sq = new_off*new_off;
505  ++count;
506  }
507  }
508  else
509  {
510  // pop
511  --count;
512  }
513  }
514 }
515 
516 template<typename Scalar, typename Index>
517 unsigned int KdTree<Scalar, Index>::split(int start, int end, unsigned int dim, Scalar splitValue)
518 {
519  int l(start), r(end-1);
520  for ( ; l<r ; ++l, --r)
521  {
522  while (l < end && mPoints[l][dim] < splitValue)
523  l++;
524  while (r >= start && mPoints[r][dim] >= splitValue)
525  r--;
526  if (l > r)
527  break;
528  std::swap(mPoints[l],mPoints[r]);
529  std::swap(mIndices[l],mIndices[r]);
530  }
531  return (mPoints[l][dim] < splitValue ? l+1 : l);
532 }
533 
554 template<typename Scalar, typename Index>
555 void KdTree<Scalar, Index>::createTree(unsigned int nodeId, unsigned int start, unsigned int end, unsigned int level, unsigned int targetCellSize, unsigned int targetMaxDepth)
556 {
557 
558  KdNode& node = mNodes[nodeId];
559  AxisAlignedBoxType aabb;
560  //aabb.Set(mPoints[start]);
561  for (unsigned int i=start ; i<end ; ++i)
562  aabb.extend(mPoints[i]);
563 
564  VectorType diag = aabb.diagonal();
565  typename VectorType::Index dim;
566 
567 #ifdef DEBUG
568 
569 // std::cout << "createTree("
570 // << nodeId << ", "
571 // << start << ", "
572 // << end << ", "
573 // << level << ")"
574 // << std::endl;
575 
576  if (std::isnan(diag.maxCoeff(&dim))){
577  std::cerr << "NaN values discovered in the tree, abort" << std::endl;
578  return;
579  }
580 #else
581  diag.maxCoeff(&dim);
582 #endif
583 
584 
585 #undef DEBUG
586  node.dim = dim;
587  node.splitValue = aabb.center()(dim);
588 
589  unsigned int midId = split(start, end, dim, node.splitValue);
590 
591  node.firstChildId = mNodes.size();
592 
593  {
594  KdNode n;
595  n.size = 0;
596  mNodes.push_back(n);
597  mNodes.push_back(n);
598  }
599  //mNodes << Node() << Node();
600  //mNodes.resize(mNodes.size()+2);
601 
602  {
603  // left child
604  unsigned int childId = mNodes[nodeId].firstChildId;
605  KdNode& child = mNodes[childId];
606  if (midId-start <= targetCellSize || level>=targetMaxDepth)
607  {
608  child.leaf = 1;
609  child.start = start;
610  child.size = midId-start;
611  }
612  else
613  {
614  child.leaf = 0;
615  createTree(childId, start, midId, level+1, targetCellSize, targetMaxDepth);
616  }
617  }
618 
619  {
620  // right child
621  unsigned int childId = mNodes[nodeId].firstChildId+1;
622  KdNode& child = mNodes[childId];
623  if (end-midId <= targetCellSize || level>=targetMaxDepth)
624  {
625  child.leaf = 1;
626  child.start = midId;
627  child.size = end-midId;
628  }
629  else
630  {
631  child.leaf = 0;
632  createTree(childId, midId, end, level+1, targetCellSize, targetMaxDepth);
633  }
634  }
635 }
636 } //namespace Super4PCS
637 
638 
639 #endif // KDTREE_H
std::vector< KdNode > NodeList
Definition: kdtree.h:168
const PointList & _getIndices(void)
Definition: kdtree.h:193
unsigned int firstChildId
Definition: kdtree.h:149
std::vector< VectorType > PointList
Definition: kdtree.h:169
Index doQueryRestrictedClosestIndex(RangeQuery< stackSize > &query, int currentId=-1) const
Finds the closest element index within the range [0:sqrt(sqdist)].
Definition: kdtree.h:391
Eigen::Matrix< Scalar, 3, 1 > VectorType
Definition: kdtree.h:165
Scalar sq
squared distance to the next node
Definition: kdtree.h:180
void doQueryDistProcessIndices(RangeQuery< stackSize > &query, Functor f) const
Performs distance query and return indices.
Definition: kdtree.h:257
void finalize()
Finalize the creation of the KdTree.
Definition: kdtree.h:351
const PointList & _getPoints(void)
Definition: kdtree.h:192
void extend(InputIt first, InputIt last)
Definition: bbox.h:75
void add(Scalar *position)
Definition: kdtree.h:216
Definition: bbox.h:54
std::vector< Index > IndexList
Definition: kdtree.h:170
unsigned int dim
Definition: kdtree.h:150
unsigned int start
Definition: kdtree.h:154
Scalar sqdist
Definition: kdtree.h:187
Definition: kdtree.h:141
const AxisAlignedBoxType & aabb() const
Definition: kdtree.h:224
const NodeList & _getNodes(void)
Definition: kdtree.h:191
VectorType queryPoint
Definition: kdtree.h:186
element of the stack
Definition: kdtree.h:173
static constexpr Index invalidIndex()
Definition: kdtree.h:163
QueryNode()
Definition: kdtree.h:175
void add(const VectorDerived &p)
Add a new vertex in the KdTree.
Definition: kdtree.h:209
float splitValue
Definition: kdtree.h:148
void doQueryDist(RangeQuery< stackSize > &query, Container &result) const
Performs distance query and return vector coordinates.
Definition: kdtree.h:233
AABB3D< Scalar > AxisAlignedBoxType
Definition: kdtree.h:166
unsigned short size
Definition: kdtree.h:155
Definition: bbox.h:57
unsigned int leaf
Definition: kdtree.h:151
~KdTree()
Definition: kdtree.h:367
_Scalar Scalar
Definition: kdtree.h:160
Definition: kdtree.h:144
void doQueryDistIndices(RangeQuery< stackSize > &query, IndexContainer &result) const
Performs distance query and return indices.
Definition: kdtree.h:245
_Index Index
Definition: kdtree.h:161
KdTree(const PointList &points, unsigned int nofPointsPerCell=KD_POINT_PER_CELL, unsigned int maxDepth=KD_MAX_DEPTH)
Create the Kd-Tree using memory copy.
Definition: kdtree.h:319
unsigned int nodeId
id of the next node
Definition: kdtree.h:178
QueryNode(unsigned int id)
Definition: kdtree.h:176