Support for XISF
This commit is contained in:
Vendored
+607
@@ -0,0 +1,607 @@
|
||||
// ____ ______ __
|
||||
// / __ \ / ____// /
|
||||
// / /_/ // / / /
|
||||
// / ____// /___ / /___ PixInsight Class Library
|
||||
// /_/ \____//_____/ PCL 2.4.23
|
||||
// ----------------------------------------------------------------------------
|
||||
// pcl/KDTree.h - Released 2022-03-12T18:59:29Z
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of the PixInsight Class Library (PCL).
|
||||
// PCL is a multiplatform C++ framework for development of PixInsight modules.
|
||||
//
|
||||
// Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved.
|
||||
//
|
||||
// Redistribution and use in both source and binary forms, with or without
|
||||
// modification, is permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. All redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. All redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
|
||||
// of their contributors, may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission. For written
|
||||
// permission, please contact info@pixinsight.com.
|
||||
//
|
||||
// 4. All products derived from this software, in any form whatsoever, must
|
||||
// reproduce the following acknowledgment in the end-user documentation
|
||||
// and/or other materials provided with the product:
|
||||
//
|
||||
// "This product is based on software from the PixInsight project, developed
|
||||
// by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
|
||||
//
|
||||
// Alternatively, if that is where third-party acknowledgments normally
|
||||
// appear, this acknowledgment must be reproduced in the product itself.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
|
||||
// INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
|
||||
// DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __PCL_KDTree_h
|
||||
#define __PCL_KDTree_h
|
||||
|
||||
/// \file pcl/KDTree.h
|
||||
|
||||
#include <pcl/Defs.h>
|
||||
|
||||
#include <pcl/Array.h>
|
||||
#include <pcl/Vector.h>
|
||||
|
||||
namespace pcl
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \class KDTree
|
||||
* \brief Bucket PR K-d tree for point data in arbitrary dimensions.
|
||||
*
|
||||
* An n-dimensional K-d tree is a specialized binary tree for partitioning of
|
||||
* a set of points in an n-dimensional space. K-d trees have important
|
||||
* applications in computational geometry problems requiring efficient
|
||||
* rectangular range searching.
|
||||
*
|
||||
* This class implements a <em>bucket point region K-d tree</em> structure
|
||||
* (see Reference 2).
|
||||
*
|
||||
* The template type argument T represents the type of a \e point object stored
|
||||
* in a %KDTree structure. The type T must have the following properties:
|
||||
*
|
||||
* \li The standard default and copy constructors are required:\n
|
||||
* \n
|
||||
* T::T() \n
|
||||
* T::T( const T& )
|
||||
*
|
||||
* \li The \c T::component subtype must be defined. It represents a component
|
||||
* of an object of type T. For example, if T is a vector type, T::component
|
||||
* must be the type of a vector component.
|
||||
*
|
||||
* \li The array subscript operator must be defined as follows:\n
|
||||
* \n
|
||||
* component T::operator []( int i ) const \n
|
||||
* \n
|
||||
* This operator must return the value of the i-th component of an object being
|
||||
* stored in the K-d tree, such that 0 <= i < N, where N > 0 is the dimension
|
||||
* of the point space.
|
||||
*
|
||||
* \note We use this implementation of K-d trees in some essential PixInsight
|
||||
* tools with success (e.g., StarAlignment), and hopefully it will be also
|
||||
* useful for you, but we don't claim it to be complete. This is a practical
|
||||
* and relatively simple implementation, where only the construction,
|
||||
* destruction and range search operations are available. In particular, this
|
||||
* implementation does not include point addition, deletion and iteration
|
||||
* operations. Future versions of PCL will include more complete
|
||||
* implementations of this fundamental data structure.
|
||||
*
|
||||
* \b References
|
||||
*
|
||||
* \li 1. Mark de Berg et al., <em>Computational Geometry: Algorithms and
|
||||
* Applications Third Edition,</em> Springer, 2010, Section 5.2.
|
||||
*
|
||||
* \li 2. Hanan Samet, <em>Foundations of Multidimensional and Metric Data
|
||||
* Structures,</em> Morgan Kaufmann, 2006, Section 1.5.
|
||||
*
|
||||
* \sa QuadTree
|
||||
*/
|
||||
template <class T>
|
||||
class PCL_CLASS KDTree
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Represents an N-dimensional point stored in this K-d tree.
|
||||
*/
|
||||
typedef T point;
|
||||
|
||||
/*!
|
||||
* Represents a point component.
|
||||
*/
|
||||
typedef typename point::component component;
|
||||
|
||||
/*!
|
||||
* A vector of point components. Used internally for tree build and range
|
||||
* search operations.
|
||||
*/
|
||||
typedef GenericVector<component> component_vector;
|
||||
|
||||
/*!
|
||||
* A list of points. Used for tree build and search operations.
|
||||
*/
|
||||
typedef Array<point> point_list;
|
||||
|
||||
/*!
|
||||
* Constructs an empty K-d tree.
|
||||
*/
|
||||
KDTree() = default;
|
||||
|
||||
/*!
|
||||
* Constructs a K-d tree and builds it for the specified list of \a points.
|
||||
*
|
||||
* \param points A list of points that will be stored in this
|
||||
* K-d tree.
|
||||
*
|
||||
* \param bucketCapacity The maximum number of points in a leaf tree node.
|
||||
* Must be >= 1. The default value is 16.
|
||||
*
|
||||
* The dimension of the point space is taken as the length of the first
|
||||
* point in the list (by calling points[0].Length()), and must be > 0. All
|
||||
* points in the \a points list must be able to provide at least
|
||||
* \a dimension components through a zero-based array subscript operator.
|
||||
*
|
||||
* If the specified list of \a points is empty, this constructor yields an
|
||||
* empty K-d tree. If the dimension of the point space is less than one, an
|
||||
* Error exception is thrown.
|
||||
*/
|
||||
KDTree( const point_list& points, int bucketCapacity = 16 )
|
||||
{
|
||||
Build( points, bucketCapacity );
|
||||
}
|
||||
|
||||
/*!
|
||||
* Constructs a K-d tree of the specified \a dimension and builds it for a
|
||||
* list of \a points.
|
||||
*
|
||||
* \param points A list of points that will be stored in this
|
||||
* K-d tree.
|
||||
*
|
||||
* \param dimension The dimension of the point space. Must be > 0.
|
||||
*
|
||||
* \param bucketCapacity The maximum number of points in a leaf tree node.
|
||||
* Must be >= 1.
|
||||
*
|
||||
* All points in the \a points list must be able to provide at least
|
||||
* \a dimension components through a zero-based array subscript operator.
|
||||
*
|
||||
* If the specified list of \a points is empty, this constructor yields an
|
||||
* empty K-d tree. If the dimension of the point space is less than one, an
|
||||
* Error exception is thrown.
|
||||
*/
|
||||
KDTree( const point_list& points, int dimension, int bucketCapacity )
|
||||
{
|
||||
Build( points, dimension, bucketCapacity );
|
||||
}
|
||||
|
||||
/*!
|
||||
* Copy constructor. Copy construction is disabled because this class uses
|
||||
* internal data structures that cannot be copy-constructed. However,
|
||||
* %KDTree implements move construction and move assignment.
|
||||
*/
|
||||
KDTree( const KDTree& ) = delete;
|
||||
|
||||
/*!
|
||||
* Copy assignment operator. Copy assignment is disabled because this class
|
||||
* uses internal data structures that cannot be copy-assigned. However,
|
||||
* %KDTree implements move assignment and move construction.
|
||||
*/
|
||||
KDTree& operator =( const KDTree& ) = delete;
|
||||
|
||||
/*!
|
||||
* Move constructor.
|
||||
*/
|
||||
KDTree( KDTree&& x )
|
||||
: m_root( x.m_root )
|
||||
, m_dimension( x.m_dimension )
|
||||
, m_bucketCapacity( x.m_bucketCapacity )
|
||||
, m_length( x.m_length )
|
||||
{
|
||||
x.m_root = nullptr;
|
||||
x.m_length = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move assignment operator. Returns a reference to this object.
|
||||
*/
|
||||
KDTree& operator =( KDTree&& x )
|
||||
{
|
||||
if ( &x != this )
|
||||
{
|
||||
DestroyTree( m_root );
|
||||
m_root = x.m_root;
|
||||
m_dimension = x.m_dimension;
|
||||
m_bucketCapacity = x.m_bucketCapacity;
|
||||
m_length = x.m_length;
|
||||
x.m_root = nullptr;
|
||||
x.m_length = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Destroys a K-d tree. All the stored point objects are deleted.
|
||||
*/
|
||||
~KDTree()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Removes all the stored point objects, yielding an empty K-d tree.
|
||||
*/
|
||||
void Clear()
|
||||
{
|
||||
DestroyTree( m_root );
|
||||
m_root = nullptr;
|
||||
m_length = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Builds a new K-d tree for the specified list of \a points.
|
||||
*
|
||||
* \param points A list of points that will be stored in this
|
||||
* K-d tree.
|
||||
*
|
||||
* \param bucketCapacity The maximum number of points in a leaf tree node.
|
||||
* Must be >= 1. The default value is 16.
|
||||
*
|
||||
* The dimension of the point space is taken as the length of the first
|
||||
* point in the list (by calling points[0].Length()), and must be > 0. All
|
||||
* points in the \a points list must be able to provide at least
|
||||
* \a dimension components through a zero-based array subscript operator.
|
||||
*
|
||||
* If the tree stores point objects before calling this function, they are
|
||||
* destroyed and removed before building a new tree.
|
||||
*
|
||||
* If the specified list of \a points is empty, this member function yields
|
||||
* an empty K-d tree. If the dimension of the point space is less than one,
|
||||
* an Error exception is thrown.
|
||||
*/
|
||||
void Build( const point_list& points, int bucketCapacity = 16 )
|
||||
{
|
||||
Clear();
|
||||
m_bucketCapacity = Max( 1, bucketCapacity );
|
||||
if ( !points.IsEmpty() )
|
||||
{
|
||||
m_dimension = points[0].Length();
|
||||
if ( m_dimension < 1 )
|
||||
throw Error( "KDTree::Build(): Invalid point space dimension." );
|
||||
m_root = BuildTree( points, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Builds a new K-d tree of the specified \a dimension for a list of
|
||||
* \a points.
|
||||
*
|
||||
* \param points A list of points that will be stored in this
|
||||
* K-d tree.
|
||||
*
|
||||
* \param dimension The dimension of the point space. Must be > 0.
|
||||
*
|
||||
* \param bucketCapacity The maximum number of points in a leaf tree node.
|
||||
* Must be >= 1.
|
||||
*
|
||||
* All points in the \a points list must be able to provide at least
|
||||
* \a dimension components through a zero-based array subscript operator.
|
||||
*
|
||||
* If the tree stores point objects before calling this function, they are
|
||||
* destroyed and removed before building a new tree.
|
||||
*
|
||||
* If the specified list of \a points is empty, this member function yields
|
||||
* an empty K-d tree. If the dimension of the point space is less than one,
|
||||
* an Error exception is thrown.
|
||||
*/
|
||||
void Build( const point_list& points, int dimension, int bucketCapacity )
|
||||
{
|
||||
Clear();
|
||||
m_bucketCapacity = Max( 1, bucketCapacity );
|
||||
if ( (m_dimension = dimension) < 1 )
|
||||
throw Error( "KDTree::Build(): Invalid point space dimension." );
|
||||
m_root = BuildTree( points, 0 );
|
||||
}
|
||||
|
||||
/*!
|
||||
* Performs a range search in this K-d tree.
|
||||
*
|
||||
* \param pt Reference to the point being searched for. The coordinates
|
||||
* of this point define the center of the hyper-rectangular
|
||||
* search range in the N-dimensional point space.
|
||||
*
|
||||
* \param epsilon Search tolerance, or half-side of the search
|
||||
* hyperrectangle.
|
||||
*
|
||||
* Returns a (possibly empty) list with all the points found in the tree
|
||||
* within the search range. In two dimensions, the search range would be the
|
||||
* rectangle defined by the points:
|
||||
*
|
||||
* (pt[0] - epsilon, pt[1] - epsilon) and \n
|
||||
* (pt[0] + epsilon, pt[1] + epsilon)
|
||||
*
|
||||
* with an obvious extension to higher dimensions. If \a epsilon is zero,
|
||||
* the search can only return the set of stored points that are identical to
|
||||
* the specified search point.
|
||||
*/
|
||||
point_list Search( const point& pt, component epsilon ) const
|
||||
{
|
||||
component_vector p0( m_dimension );
|
||||
component_vector p1( m_dimension );
|
||||
for ( int i = 0; i < m_dimension; ++i )
|
||||
{
|
||||
p0[i] = pt[i] - epsilon;
|
||||
p1[i] = pt[i] + epsilon;
|
||||
}
|
||||
point_list found;
|
||||
SearchTree( found, p0, p1, m_root, 0 );
|
||||
return found;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Performs a range search in this K-d tree.
|
||||
*
|
||||
* \param pt Reference to the point being searched for. The
|
||||
* coordinates of this point define the center of the
|
||||
* hyper-rectangular search range in the N-dimensional
|
||||
* point space.
|
||||
*
|
||||
* \param epsilon Search tolerance, or half-side of the search
|
||||
* hyperrectangle.
|
||||
*
|
||||
* \param callback Callback functional.
|
||||
*
|
||||
* \param data Callback data.
|
||||
*
|
||||
* The callback function prototype should be:
|
||||
*
|
||||
* \code void callback( const point& pt, void* data ) \endcode
|
||||
*
|
||||
* The callback function will be called once for each point found in the
|
||||
* tree within the specified search range. In two dimensions, the search
|
||||
* range would be the rectangle defined by the points:
|
||||
*
|
||||
* (pt[0] - epsilon, pt[1] - epsilon) and \n
|
||||
* (pt[0] + epsilon, pt[1] + epsilon)
|
||||
*
|
||||
* with an obvious extension to higher dimensions. If \a epsilon is zero,
|
||||
* the search can only return the set of stored points that are identical to
|
||||
* the specified search point.
|
||||
*/
|
||||
template <class F>
|
||||
void Search( const point& pt, component epsilon, F callback, void* data ) const
|
||||
{
|
||||
component_vector p0( m_dimension );
|
||||
component_vector p1( m_dimension );
|
||||
for ( int i = 0; i < m_dimension; ++i )
|
||||
{
|
||||
p0[i] = pt[i] - epsilon;
|
||||
p1[i] = pt[i] + epsilon;
|
||||
}
|
||||
SearchTree( p0, p1, callback, data, m_root, 0 );
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the dimension of this K-d tree. This is the number of components
|
||||
* in a point stored in the tree.
|
||||
*/
|
||||
int Dimension() const
|
||||
{
|
||||
return m_dimension;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the total number of points stored in this K-d tree.
|
||||
*/
|
||||
size_type Length() const
|
||||
{
|
||||
return m_length;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns true iff this K-d tree is empty.
|
||||
*/
|
||||
bool IsEmpty()
|
||||
{
|
||||
return m_root == nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Exchanges two %KDTree objects \a x1 and \a x2.
|
||||
*/
|
||||
friend void Swap( KDTree& x1, KDTree& x2 )
|
||||
{
|
||||
pcl::Swap( x1.m_root, x2.m_root );
|
||||
pcl::Swap( x1.m_dimension, x2.m_dimension );
|
||||
pcl::Swap( x1.m_bucketCapacity, x2.m_bucketCapacity );
|
||||
pcl::Swap( x1.m_length, x2.m_length );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct Node
|
||||
{
|
||||
double split = 0; // position of this node's splitting hyperplane
|
||||
Node* left = nullptr; // child points at coordinates <= split
|
||||
Node* right = nullptr; // child points at coordinates > split
|
||||
|
||||
Node( double s = 0 )
|
||||
: split( s )
|
||||
{
|
||||
}
|
||||
|
||||
bool IsLeaf() const
|
||||
{
|
||||
return left == nullptr && right == nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct LeafNode : public Node
|
||||
{
|
||||
point_list points;
|
||||
|
||||
LeafNode( const point_list& p )
|
||||
: points( p )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
Node* m_root = nullptr;
|
||||
int m_dimension = 0;
|
||||
int m_bucketCapacity = 0;
|
||||
size_type m_length = 0;
|
||||
|
||||
LeafNode* NewLeafNode( const point_list& points )
|
||||
{
|
||||
m_length += points.Length();
|
||||
return new LeafNode( points );
|
||||
}
|
||||
|
||||
Node* BuildTree( const point_list& points, int depth )
|
||||
{
|
||||
if ( points.IsEmpty() )
|
||||
return nullptr;
|
||||
|
||||
if ( points.Length() <= size_type( m_bucketCapacity ) )
|
||||
return NewLeafNode( points );
|
||||
|
||||
int index = depth % m_dimension;
|
||||
|
||||
Node* node = new Node( SplitValue( points, index ) );
|
||||
|
||||
point_list left, right;
|
||||
for ( const point& p : points )
|
||||
if ( p[index] <= node->split )
|
||||
left.Add( p );
|
||||
else
|
||||
right.Add( p );
|
||||
|
||||
// If we are about to build a degenerate subtree, abort this branch of
|
||||
// recursion right now.
|
||||
if ( left.IsEmpty() || right.IsEmpty() )
|
||||
{
|
||||
delete node;
|
||||
return NewLeafNode( points );
|
||||
}
|
||||
|
||||
node->left = BuildTree( left, depth+1 );
|
||||
node->right = BuildTree( right, depth+1 );
|
||||
|
||||
// Further degeneracies cannot happen in theory, but let's prevent them
|
||||
// for extra safety.
|
||||
if ( node->IsLeaf() )
|
||||
{
|
||||
delete node;
|
||||
return NewLeafNode( points );
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void SearchTree( point_list& found, const component_vector& p0, const component_vector& p1, const Node* node, int depth ) const
|
||||
{
|
||||
if ( node != nullptr )
|
||||
if ( node->IsLeaf() )
|
||||
{
|
||||
const LeafNode* leaf = static_cast<const LeafNode*>( node );
|
||||
for ( const point& p : leaf->points )
|
||||
for ( int j = 0; ; )
|
||||
{
|
||||
component x = p[j];
|
||||
if ( x < p0[j] || p1[j] < x )
|
||||
break;
|
||||
if ( ++j == m_dimension )
|
||||
{
|
||||
found.Add( p );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = depth % m_dimension;
|
||||
if ( p0[index] <= node->split )
|
||||
SearchTree( found, p0, p1, node->left, depth+1 );
|
||||
if ( p1[index] > node->split )
|
||||
SearchTree( found, p0, p1, node->right, depth+1 );
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void SearchTree( const component_vector& p0, const component_vector& p1, F callback, void* data, const Node* node, int depth ) const
|
||||
{
|
||||
if ( node != nullptr )
|
||||
if ( node->IsLeaf() )
|
||||
{
|
||||
const LeafNode* leaf = static_cast<const LeafNode*>( node );
|
||||
for ( const point& p : leaf->points )
|
||||
for ( int j = 0; ; )
|
||||
{
|
||||
component x = p[j];
|
||||
if ( x < p0[j] || p1[j] < x )
|
||||
break;
|
||||
if ( ++j == m_dimension )
|
||||
{
|
||||
callback( p, data );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = depth % m_dimension;
|
||||
if ( p0[index] <= node->split )
|
||||
SearchTree( p0, p1, callback, data, node->left, depth+1 );
|
||||
if ( p1[index] > node->split )
|
||||
SearchTree( p0, p1, callback, data, node->right, depth+1 );
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyTree( Node* node )
|
||||
{
|
||||
if ( node != nullptr )
|
||||
if ( node->IsLeaf() )
|
||||
delete static_cast<LeafNode*>( node );
|
||||
else
|
||||
{
|
||||
DestroyTree( node->left );
|
||||
DestroyTree( node->right );
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
double SplitValue( const point_list& points, int index )
|
||||
{
|
||||
component_vector v( points.Length() );
|
||||
for ( int i = 0; i < v.Length(); ++i )
|
||||
v[i] = points[i][index];
|
||||
return v.Median();
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
} // pcl
|
||||
|
||||
#endif // __PCL_KDTree_h
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// EOF pcl/KDTree.h - Released 2022-03-12T18:59:29Z
|
||||
Reference in New Issue
Block a user