Files
tenmon/3rdparty/include/pcl/SurfaceSimplifier.h
T
2022-04-12 08:17:18 +02:00

582 lines
20 KiB
C++

// ____ ______ __
// / __ \ / ____// /
// / /_/ // / / /
// / ____// /___ / /___ PixInsight Class Library
// /_/ \____//_____/ PCL 2.4.23
// ----------------------------------------------------------------------------
// pcl/SurfaceSimplifier.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_SurfaceSimplifier_h
#define __PCL_SurfaceSimplifier_h
/// \file pcl/SurfaceSimplifier.h
#include <pcl/Defs.h>
#include <pcl/Diagnostics.h>
#include <pcl/Matrix.h>
#include <pcl/QuadTree.h>
namespace pcl
{
// ----------------------------------------------------------------------------
/*
* Default tolerance for surface simplification.
*
* The tolerance parameter determines the maximum absolute deviation of
* function values from a plane allowed to simplify the subset of points within
* a subregion of the input point space. This parameter is specified in
* bivariate function values, i.e. in Z-axis units.
*/
#define __PCL_SURFACE_SIMPLIFIER_DEFAULT_TOLERANCE 0.01
/*
* Default state of the surface simplification outlier rejection feature.
*
* When enabled, a prescribed fraction of outlier points will be rejected on
* each subregion for estimation of local curvature.
*/
#define __PCL_SURFACE_SIMPLIFIER_DEFAULT_REJECTION_ENABLED true
/*
* Default rejection fraction for surface simplification.
*
* This parameter specifies the maximum fraction of outliers allowed for
* simplification of a subset of points in a region of the input point space.
* Point rejection makes the surface simplification algorithm robust to outlier
* function values.
*/
#define __PCL_SURFACE_SIMPLIFIER_DEFAULT_REJECT_FRACTION 0.2F
/*
* Whether to include subregion centroids in simplified point sets.
*
* When enabled, the centroid of each simplified subregion will also be
* included in the corresponding list of simplified points. This can improve
* the shape preservation behavior of the surface simplification algorithm, at
* the cost of a reduced amount of additional points.
*/
#define __PCL_SURFACE_SIMPLIFIER_DEFAULT_INCLUDE_CENTROIDS false
// ----------------------------------------------------------------------------
/*!
* \class SurfaceSimplifier
* \brief Shape-preserving simplification of 2-D surfaces
*
* Given a finite set of three dimensional points representing sampled values
* of a real bivariate function of the form
*
* z = f(x,y),
*
* the shape-preserving surface simplification algorithm attempts to generate a
* reduced set of points with equivalent geometric properties to within a
* prescribed maximum error parameter.
*
* The implemented algorithm divides the input point space recursively on the
* XY plane into rectangular regions using custom quadtree structures. For each
* region, the algorithm finds the orientation of the dominant plane through
* principal component analysis. The deviation of function values from the
* dominant plane is evaluated for the points in the region, and if the region
* is considered flat to within a tolerance parameter, its points are replaced
* with a simplified (reduced) set of points that tends to preserve the local
* shape of the original function over the region. If the region is tagged as
* curve, it is further divided using a new quadtree recursion, until no
* additional simplification can be achieved.
*
* Surface simplification is an important auxiliary tool to improve the
* practical application of surface interpolation and approximation devices.
* These algorithms allow us to work with large-scale data sets by selecting a
* subset of essential data points, usually much smaller than the original set,
* adapted to solve a particular problem. Surface simplification is
* particularly useful for the application of computationally expensive
* approximation algorithms, such as surface splines or thin plates. A good
* example is computation of high accuracy astrometric solutions, where surface
* simplification allows us to use large sets of thousands of stars to generate
* thin plate models of local distortions. Since generation of thin plates has
* roughly O(N^3) time complexity, the efficient reduction of input point sets
* is crucial for this application.
*/
class PCL_CLASS SurfaceSimplifier
{
private:
/*!
* \struct SurfaceSimplifier::point
* \brief Working 3-D point structure for quadtree storage used by the
* recursive surface simplification algorithm.
* \internal
*/
struct point
{
typedef double component;
component x, y, z;
point() = default;
point( const point& ) = default;
template <typename T>
point( T a_x, T a_y, T a_z )
: x( double( a_x ) )
, y( double( a_y ) )
, z( double( a_z ) )
{
}
template <typename T>
point( T k )
{
x = y = z = double( k );
}
component operator[]( int i ) const
{
return i ? y : x;
}
point& operator +=( const point& p )
{
x += p.x; y += p.y; z += p.z;
return *this;
}
template <typename T>
point& operator /=( T k )
{
x /= k; y /= k; z /= k;
return *this;
}
double SquaredDistanceTo( const point& p ) const
{
double dx = p.x - x, dy = p.y - y;
return dx*dx + dy*dy;
}
};
typedef QuadTree<point> tree;
typedef typename tree::rectangle rectangle;
typedef typename tree::point_list point_list;
public:
/*!
* Constructs a new %SurfaceSimplifier object with default parameters:
*
* \li Tolerance = 0.01 in function value units (Z-axis values).
* \li Outlier rejection enabled
* \li Outlier rejection fraction = 0.2
* \li Inclusion of centroid points disabled
*/
SurfaceSimplifier() = default;
/*!
* Constructs a new %SurfaceSimplifier instance with the specified
* \a tolerance.
*/
SurfaceSimplifier( double tolerance )
: m_tolerance( Abs( tolerance ) )
{
PCL_PRECONDITION( tolerance >= 0 )
}
/*!
* Copy constructor.
*/
SurfaceSimplifier( const SurfaceSimplifier& ) = default;
/*!
* Returns the current tolerance of this surface simplifier.
*
* See the SetTolerance() member function for a description of the
* tolerance parameter.
*/
double Tolerance() const
{
return m_tolerance;
}
/*!
* Sets the \a tolerance of this surface simplifier.
*
* The tolerance parameter determines the maximum absolute deviation of
* function values from a plane allowed to simplify the subset of points
* within a subregion of the input point space.
*
* The value of this parameter is specified in bivariate function value
* units, i.e. in Z-axis units. Higher tolerances tend to allow for more
* simplification, and hence for shorter simplified point lists. However, an
* excessive tolerance value may degrade the accuracy of the simplified
* surface in terms of preservation of the original function's structure and
* shape. This parameter must be tailored carefully to the requirements of
* the function being simplified and the tasks where the simplified version
* is going to be applied.
*/
void SetTolerance( double tolerance )
{
PCL_PRECONDITION( tolerance > 0 )
m_tolerance = Abs( tolerance );
}
/*!
* Returns true iff outlier rejection is enabled for this object.
*
* See the EnableRejection() and RejectFraction() member functions for a
* description of outlier rejection in the surface simplification algorithm.
*/
bool IsRejectionEnabled() const
{
return m_enableRejection;
}
/*!
* Enables outlier rejection for this surface simplifier.
*
* When enabled, a prescribed fraction of outlier points (see the
* RejectFraction() member function) will be rejected on each subregion for
* estimation of local curvature. An adequate amount of rejection is
* important to achieve a robust result, especially for simplification of
* noisy data where outliers may generate false curvatures that prevent
* efficient simplification.
*/
void EnableRejection( bool enabled = true )
{
m_enableRejection = enabled;
}
/*!
* Disables outlier rejection for this surface simplifier. See
* EnableRejection() for more information.
*/
void DisableRejection( bool disable = true )
{
EnableRejection( !disable );
}
/*!
* Returns the fraction of outlier points rejected for estimation of local
* curvature.
*
* See the EnableRejection() and RejectFraction() member functions for a
* description of outlier rejection in the surface simplification algorithm.
*/
float RejectFraction() const
{
return m_rejectFraction;
}
/*!
* Sets the fraction of outlier points rejected by this surface simplifier.
*
* This parameter defines a fraction of outlier points that will be rejected
* on each subregion of the point space being simplified, for estimation of
* local curvature.
*
* Rejecting an adequate fraction of points makes the surface simplification
* algorithm more immune to noise in the input data, including erroneous
* points that may deviate considerably from the true surface represented by
* the sampled function. The result is a more robust and accurate simplified
* surface. However, too high of a rejection fraction may remove significant
* data and lead to an inaccurate result.
*
* The specified \a rejectFraction value must be in the (0,1) range. The
* default value upon construction is 0.2, which is quite appropriate in
* most cases.
*/
void SetRejectFraction( float rejectFraction )
{
PCL_PRECONDITION( rejectFraction > 0 && rejectFraction < 1 )
m_rejectFraction = Range( rejectFraction, 0.0F, 1.0F );
}
/*!
* Returns true iff inclusion of centroid points is enabled for this object.
*
* See the EnableCentroidInclusion() member function for a description of
* the centroid inclusion feature of the surface simplification algorithm.
*/
bool IsCentroidInclusionEnabled() const
{
return m_includeCentroids;
}
/*!
* Enables inclusion of centroid points for this surface simplifier.
*
* When a subregion of the input space is simplified, the surface
* simplification algorithm replaces the subset of points in the subregion
* with a simplified, reduced set. If this option is enabled, the average
* point of the subset, also known as \e centroid, is also included in the
* simplified point list. This usually improves the shape preservation
* behavior of the algorithm, at the cost of a small amount of additional
* points in the simplified point list.
*/
void EnableCentroidInclusion( bool enable = true )
{
m_includeCentroids = enable;
}
/*!
* Disables inclusion of centroid points for this surface simplifier. See
* EnableCentroidInclusion() for more information.
*/
void DisableCentroidInclusion( bool disable = true )
{
EnableCentroidInclusion( !disable );
}
/*!
* Attempts to simplify a set of points given by its separate coordinates
* and function values.
*
* \param[out] xs The X coordinates of the simplified point set.
*
* \param[out] ys The Y coordinates of the simplified point set.
*
* \param[out] zs The function values for the simplified point set.
*
* \param x The X coordinates of the input point set.
*
* \param y The Y coordinates of the input point set.
*
* \param z The function values for the input point set.
*
* The template argument C must be a direct container of numeric scalars
* with random access semantics, such as Array or GenericVector.
*
* If the specified surface can be simplified with the current working
* parameters defined for this object, the output containers will have less
* elements (usually \e much less) than the input containers. Otherwise an
* exact copy of the input containers will be obtained in \a xs, \a ys and
* \a zs. This will happen also if the input containers have less than four
* coordinates, since a triangular facet cannot be simplified.
*/
template <class C>
void Simplify( C& xs, C& ys, C& zs, const C& x, const C& y, const C& z ) const
{
int n = int( Min( Min( x.Length(), y.Length() ), z.Length() ) );
if ( n < 4 )
{
xs = x; ys = y; zs = z;
return;
}
point_list P;
for ( int i = 0; i < n; ++i )
P << point( x[i], y[i], z[i] );
tree R( P, n );
P = Simplify( R );
if ( int( P.Length() ) >= n )
{
xs = x; ys = y; zs = z;
return;
}
R.Build( P, n );
P = Simplify( R );
n = int( P.Length() );
xs = C( n );
ys = C( n );
zs = C( n );
for ( int i = 0; i < n; ++i )
{
xs[i] = typename C::item_type( P[i].x );
ys[i] = typename C::item_type( P[i].y );
zs[i] = typename C::item_type( P[i].z );
}
}
private:
double m_tolerance = __PCL_SURFACE_SIMPLIFIER_DEFAULT_TOLERANCE;
float m_rejectFraction = __PCL_SURFACE_SIMPLIFIER_DEFAULT_REJECT_FRACTION;
bool m_enableRejection = __PCL_SURFACE_SIMPLIFIER_DEFAULT_REJECTION_ENABLED;
bool m_includeCentroids = __PCL_SURFACE_SIMPLIFIER_DEFAULT_INCLUDE_CENTROIDS;
/*!
* Recursive part of the shape-preserving surface simplification algorithm.
* Returns the simplified point list for the set of points stored in the
* local quadtree \a R.
* \internal
*/
point_list Simplify( tree& R ) const
{
point_list Q;
R.Traverse(
[&Q,this]( const rectangle& rect, point_list& points, void*& )
{
int n = int( points.Length() );
if ( n < 4 ) // cannot simplify triangles
{
Q << points;
return;
}
/*
* Compute local centroid coordinates.
*/
point p0( 0 );
for ( const point& p : points )
p0 += p;
p0 /= n;
/*
* Form the covariance matrix.
*/
double xx = 0, xy = 0, xz = 0, yy = 0, yz = 0, zz = 0;
for ( const point& p : points )
{
double dx = p.x - p0.x;
double dy = p.y - p0.y;
double dz = p.z - p0.z;
xx += dx*dx;
xy += dx*dy;
xz += dx*dz;
yy += dy*dy;
yz += dy*dz;
zz += dz*dz;
}
int n1 = n - 1;
xx /= n1;
xy /= n1;
xz /= n1;
yy /= n1;
yz /= n1;
zz /= n1;
Matrix M( xx, xy, xz,
xy, yy, yz,
xz, yz, zz );
/*
* Test all local function values against the plane fitted at the
* centroid, with optional outlier rejection. The plane normal
* vector is the least eigenvector, from which we can form the
* plane equation: ax + by + cz = 0.
*/
ComputeEigenvectors( M );
double a = M[0][0];
double b = M[1][0];
double c = M[2][0];
int mr = m_enableRejection ? Max( 1, TruncInt( m_rejectFraction*n ) ) : 1; // max. number of outliers
int nr = 0;
point_list P = points;
for ( point& p : P )
{
double z = p0.z - (a*(p.x - p0.x) + b*(p.y - p0.y))/c;
if ( Abs( p.z - z ) > m_tolerance )
{
if ( ++nr == mr )
{
/*
* If the current region deviates from the fitted plane
* more than allowed by the tolerance parameter after
* outlier rejection, try to simplify it further with a
* deeper quadtree subdivision.
*/
tree R( points, TruncInt( 0.75*n ) );
Q << Simplify( R );
return;
}
// Winsorize outlier function values.
p.z = (p.z > z) ? z + m_tolerance : z - m_tolerance;
}
}
/*
* If the current region is flat to within the tolerance parameter,
* take the convex hull as its simplified point set. This is what
* makes our simplification algorithm shape-preserving.
*/
if ( m_includeCentroids )
{
// If one or more points have been rejected, calculate a new
// centroid function value from Winsorized z coordinates.
if ( nr > 0 )
{
p0.z = 0;
for ( const point& p : P )
p0.z += p.z;
p0.z /= n;
}
Q << p0;
}
Q << ConvexHull( P );
}
);
return Q;
}
/*!
* \internal
* Compute the eigenvectors of a 3x3 symmetric matrix.
*/
static void ComputeEigenvectors( Matrix& );
/*!
* \internal
* Compute the convex hull of a point set.
*/
static point_list ConvexHull( point_list& );
};
// ----------------------------------------------------------------------------
} // pcl
#endif // __PCL_SurfaceSimplifier_h
// ----------------------------------------------------------------------------
// EOF pcl/SurfaceSimplifier.h - Released 2022-03-12T18:59:29Z