PCL
KDTree.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.7.0
6 // ----------------------------------------------------------------------------
7 // pcl/KDTree.h - Released 2024-06-18T15:48:54Z
8 // ----------------------------------------------------------------------------
9 // This file is part of the PixInsight Class Library (PCL).
10 // PCL is a multiplatform C++ framework for development of PixInsight modules.
11 //
12 // Copyright (c) 2003-2024 Pleiades Astrophoto S.L. All Rights Reserved.
13 //
14 // Redistribution and use in both source and binary forms, with or without
15 // modification, is permitted provided that the following conditions are met:
16 //
17 // 1. All redistributions of source code must retain the above copyright
18 // notice, this list of conditions and the following disclaimer.
19 //
20 // 2. All redistributions in binary form must reproduce the above copyright
21 // notice, this list of conditions and the following disclaimer in the
22 // documentation and/or other materials provided with the distribution.
23 //
24 // 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
25 // of their contributors, may be used to endorse or promote products derived
26 // from this software without specific prior written permission. For written
27 // permission, please contact info@pixinsight.com.
28 //
29 // 4. All products derived from this software, in any form whatsoever, must
30 // reproduce the following acknowledgment in the end-user documentation
31 // and/or other materials provided with the product:
32 //
33 // "This product is based on software from the PixInsight project, developed
34 // by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
35 //
36 // Alternatively, if that is where third-party acknowledgments normally
37 // appear, this acknowledgment must be reproduced in the product itself.
38 //
39 // THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
40 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
43 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
44 // EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
45 // INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
46 // DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49 // POSSIBILITY OF SUCH DAMAGE.
50 // ----------------------------------------------------------------------------
51 
52 #ifndef __PCL_KDTree_h
53 #define __PCL_KDTree_h
54 
56 
57 #include <pcl/Defs.h>
58 
59 #include <pcl/Array.h>
60 #include <pcl/Vector.h>
61 
62 namespace pcl
63 {
64 
65 // ----------------------------------------------------------------------------
66 
118 template <class T>
119 class PCL_CLASS KDTree
120 {
121 public:
122 
126  using point = T;
127 
131  using component = typename point::component;
132 
138 
143 
147  KDTree() = default;
148 
167  KDTree( const point_list& points, int bucketCapacity = 16 )
168  {
169  Build( points, bucketCapacity );
170  }
171 
191  KDTree( const point_list& points, int dimension, int bucketCapacity )
192  {
193  Build( points, dimension, bucketCapacity );
194  }
195 
201  KDTree( const KDTree& ) = delete;
202 
208  KDTree& operator =( const KDTree& ) = delete;
209 
213  KDTree( KDTree&& x )
214  : m_root( x.m_root )
215  , m_dimension( x.m_dimension )
216  , m_bucketCapacity( x.m_bucketCapacity )
217  , m_length( x.m_length )
218  {
219  x.m_root = nullptr;
220  x.m_length = 0;
221  }
222 
226  KDTree& operator =( KDTree&& x )
227  {
228  if ( &x != this )
229  {
230  DestroyTree( m_root );
231  m_root = x.m_root;
232  m_dimension = x.m_dimension;
233  m_bucketCapacity = x.m_bucketCapacity;
234  m_length = x.m_length;
235  x.m_root = nullptr;
236  x.m_length = 0;
237  }
238  return *this;
239  }
240 
245  {
246  Clear();
247  }
248 
252  void Clear()
253  {
254  DestroyTree( m_root );
255  m_root = nullptr;
256  m_length = 0;
257  }
258 
280  void Build( const point_list& points, int bucketCapacity = 16 )
281  {
282  Clear();
283  m_bucketCapacity = Max( 1, bucketCapacity );
284  if ( !points.IsEmpty() )
285  {
286  m_dimension = points[0].Length();
287  if ( m_dimension < 1 )
288  throw Error( "KDTree::Build(): Invalid point space dimension." );
289  m_root = BuildTree( points, 0 );
290  }
291  }
292 
315  void Build( const point_list& points, int dimension, int bucketCapacity )
316  {
317  Clear();
318  m_bucketCapacity = Max( 1, bucketCapacity );
319  if ( (m_dimension = dimension) < 1 )
320  throw Error( "KDTree::Build(): Invalid point space dimension." );
321  m_root = BuildTree( points, 0 );
322  }
323 
345  point_list Search( const point& pt, component epsilon ) const
346  {
347  component_vector p0( m_dimension );
348  component_vector p1( m_dimension );
349  for ( int i = 0; i < m_dimension; ++i )
350  {
351  p0[i] = pt[i] - epsilon;
352  p1[i] = pt[i] + epsilon;
353  }
354  point_list found;
355  SearchTree( found, p0, p1, m_root, 0 );
356  return found;
357  }
358 
389  template <class F>
390  void Search( const point& pt, component epsilon, F callback, void* data ) const
391  {
392  component_vector p0( m_dimension );
393  component_vector p1( m_dimension );
394  for ( int i = 0; i < m_dimension; ++i )
395  {
396  p0[i] = pt[i] - epsilon;
397  p1[i] = pt[i] + epsilon;
398  }
399  SearchTree( p0, p1, callback, data, m_root, 0 );
400  }
401 
406  int Dimension() const
407  {
408  return m_dimension;
409  }
410 
415  {
416  return m_length;
417  }
418 
422  bool IsEmpty()
423  {
424  return m_root == nullptr;
425  }
426 
430  friend void Swap( KDTree& x1, KDTree& x2 )
431  {
432  pcl::Swap( x1.m_root, x2.m_root );
433  pcl::Swap( x1.m_dimension, x2.m_dimension );
434  pcl::Swap( x1.m_bucketCapacity, x2.m_bucketCapacity );
435  pcl::Swap( x1.m_length, x2.m_length );
436  }
437 
438 private:
439 
440  struct Node
441  {
442  double split = 0; // position of this node's splitting hyperplane
443  Node* left = nullptr; // child points at coordinates <= split
444  Node* right = nullptr; // child points at coordinates > split
445 
446  Node( double s = 0 )
447  : split( s )
448  {
449  }
450 
451  bool IsLeaf() const
452  {
453  return left == nullptr && right == nullptr;
454  }
455  };
456 
457  struct LeafNode : public Node
458  {
459  point_list points;
460 
461  LeafNode( const point_list& p )
462  : points( p )
463  {
464  }
465  };
466 
467  Node* m_root = nullptr;
468  int m_dimension = 0;
469  int m_bucketCapacity = 0;
470  size_type m_length = 0;
471 
472  LeafNode* NewLeafNode( const point_list& points )
473  {
474  m_length += points.Length();
475  return new LeafNode( points );
476  }
477 
478  Node* BuildTree( const point_list& points, int depth )
479  {
480  if ( points.IsEmpty() )
481  return nullptr;
482 
483  if ( points.Length() <= size_type( m_bucketCapacity ) )
484  return NewLeafNode( points );
485 
486  int index = depth % m_dimension;
487 
488  Node* node = new Node( SplitValue( points, index ) );
489 
490  point_list left, right;
491  for ( const point& p : points )
492  if ( p[index] <= node->split )
493  left.Add( p );
494  else
495  right.Add( p );
496 
497  // If we are about to build a degenerate subtree, abort this branch of
498  // recursion right now.
499  if ( left.IsEmpty() || right.IsEmpty() )
500  {
501  delete node;
502  return NewLeafNode( points );
503  }
504 
505  node->left = BuildTree( left, depth+1 );
506  node->right = BuildTree( right, depth+1 );
507 
508  // Further degeneracies cannot happen in theory, but let's prevent them
509  // for extra safety.
510  if ( node->IsLeaf() )
511  {
512  delete node;
513  return NewLeafNode( points );
514  }
515 
516  return node;
517  }
518 
519  void SearchTree( point_list& found, const component_vector& p0, const component_vector& p1, const Node* node, int depth ) const
520  {
521  if ( node != nullptr )
522  if ( node->IsLeaf() )
523  {
524  const LeafNode* leaf = static_cast<const LeafNode*>( node );
525  for ( const point& p : leaf->points )
526  for ( int j = 0; ; )
527  {
528  component x = p[j];
529  if ( x < p0[j] || p1[j] < x )
530  break;
531  if ( ++j == m_dimension )
532  {
533  found.Add( p );
534  break;
535  }
536  }
537  }
538  else
539  {
540  int index = depth % m_dimension;
541  if ( p0[index] <= node->split )
542  SearchTree( found, p0, p1, node->left, depth+1 );
543  if ( p1[index] > node->split )
544  SearchTree( found, p0, p1, node->right, depth+1 );
545  }
546  }
547 
548  template <class F>
549  void SearchTree( const component_vector& p0, const component_vector& p1, F callback, void* data, const Node* node, int depth ) const
550  {
551  if ( node != nullptr )
552  if ( node->IsLeaf() )
553  {
554  const LeafNode* leaf = static_cast<const LeafNode*>( node );
555  for ( const point& p : leaf->points )
556  for ( int j = 0; ; )
557  {
558  component x = p[j];
559  if ( x < p0[j] || p1[j] < x )
560  break;
561  if ( ++j == m_dimension )
562  {
563  callback( p, data );
564  break;
565  }
566  }
567  }
568  else
569  {
570  int index = depth % m_dimension;
571  if ( p0[index] <= node->split )
572  SearchTree( p0, p1, callback, data, node->left, depth+1 );
573  if ( p1[index] > node->split )
574  SearchTree( p0, p1, callback, data, node->right, depth+1 );
575  }
576  }
577 
578  void DestroyTree( Node* node )
579  {
580  if ( node != nullptr )
581  if ( node->IsLeaf() )
582  delete static_cast<LeafNode*>( node );
583  else
584  {
585  DestroyTree( node->left );
586  DestroyTree( node->right );
587  delete node;
588  }
589  }
590 
591  double SplitValue( const point_list& points, int index )
592  {
593  component_vector v( points.Length() );
594  for ( int i = 0; i < v.Length(); ++i )
595  v[i] = points[i][index];
596  return v.Median();
597  }
598 };
599 
600 // ----------------------------------------------------------------------------
601 
602 } // pcl
603 
604 #endif // __PCL_KDTree_h
605 
606 // ----------------------------------------------------------------------------
607 // EOF pcl/KDTree.h - Released 2024-06-18T15:48:54Z
bool IsEmpty() const noexcept
Definition: Array.h:312
size_type Length() const noexcept
Definition: Array.h:266
A simple exception with an associated error message.
Definition: Exception.h:239
Generic vector of arbitrary length.
Definition: Vector.h:107
Bucket PR K-d tree for point data in arbitrary dimensions.
Definition: KDTree.h:120
point_list Search(const point &pt, component epsilon) const
Definition: KDTree.h:345
KDTree(const point_list &points, int dimension, int bucketCapacity)
Definition: KDTree.h:191
void Clear()
Definition: KDTree.h:252
typename point::component component
Definition: KDTree.h:131
void Build(const point_list &points, int bucketCapacity=16)
Definition: KDTree.h:280
int Dimension() const
Definition: KDTree.h:406
void Search(const point &pt, component epsilon, F callback, void *data) const
Definition: KDTree.h:390
KDTree(const KDTree &)=delete
bool IsEmpty()
Definition: KDTree.h:422
void Build(const point_list &points, int dimension, int bucketCapacity)
Definition: KDTree.h:315
size_type Length() const
Definition: KDTree.h:414
friend void Swap(KDTree &x1, KDTree &x2)
Definition: KDTree.h:430
KDTree(KDTree &&x)
Definition: KDTree.h:213
KDTree(const point_list &points, int bucketCapacity=16)
Definition: KDTree.h:167
KDTree()=default
void Swap(GenericPoint< T > &p1, GenericPoint< T > &p2) noexcept
Definition: Point.h:1459
size_t size_type
Definition: Defs.h:609
constexpr const T & Max(const T &a, const T &b) noexcept
Definition: Utility.h:119
PCL root namespace.
Definition: AbstractImage.h:77