// ____ ______ __ // / __ \ / ____// / // / /_/ // / / / // / ____// /___ / /___ PixInsight Class Library // /_/ \____//_____/ PCL 2.4.23 // ---------------------------------------------------------------------------- // pcl/WorldTransformation.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_WorldTransformation_h #define __PCL_WorldTransformation_h /// \file pcl/WorldTransformation.h #include #include #include #include #include /* * Based on original work contributed by Andrés del Pozo. */ namespace pcl { // ---------------------------------------------------------------------------- #define __PCL_WCS_DEFAULT_SPLINE_ORDER 2 #define __PCL_WCS_DEFAULT_SPLINE_SMOOTHNESS 0.025F #define __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_ENABLED true #define __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_TOLERANCE 0.25F // px #define __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_REJECT_FRACTION 0.10F #define __PCL_WCS_MAX_SPLINE_POINTS 2100 // ---------------------------------------------------------------------------- /*! * \class WorldTransformation * \brief Abstract base class of world coordinate transformations * * \ingroup astrometry_support */ class PCL_CLASS WorldTransformation { public: /*! * Default constructor. */ WorldTransformation() = default; /*! * Copy constructor. */ WorldTransformation( const WorldTransformation& ) = default; /*! * Virtual destructor. */ virtual ~WorldTransformation() { } /*! * Returns true iff this transformation is empty (uninitialized, invalid). */ virtual bool IsEmpty() const { return false; } /*! * Returns a dynamically allocated copy of this object. */ virtual WorldTransformation* Clone() const = 0; /*! * Transforms from native spherical coordinates to image coordinates. * * The point \a p contains native spherical coordinates: \a p.x is the * native longitude and \a p.y is the native latitude, both expressed in * degrees. Returns image coordinates in pixels corresponding to \a p. */ virtual DPoint Direct( const DPoint& p ) const = 0; /*! * Transforms from image coordinates to native spherical coordinates. * * The specified point \a p contains image coordinates in pixels. Returns a * point \a q where \a q.x is the native longitude and \a q.y is the native * latitude, both expressed in degrees, corresponding to \a p. */ virtual DPoint Inverse( const DPoint& p ) const = 0; /*! * Returns an approximate linear transformation from Image to World * coordinates. */ virtual const LinearTransformation& ApproximateLinearTransform() const = 0; }; // ---------------------------------------------------------------------------- /*! * \class LinearWorldTransformation * \brief WCS linear world coordinate transformation * * \ingroup astrometry_support */ class PCL_CLASS LinearWorldTransformation : public WorldTransformation { public: /*! * Constructor from a linear transformation. The specified transformation * \a transIW must transform from image coordinates to native spherical * coordinates. */ LinearWorldTransformation( const LinearTransformation& transIW ) : m_transWI( transIW.Inverse() ) , m_transIW( transIW ) { } /*! * Copy constructor. */ LinearWorldTransformation( const LinearWorldTransformation& ) = default; /*! * Move constructor. */ LinearWorldTransformation( LinearWorldTransformation&& ) = default; /*! * Virtual destructor. */ virtual ~LinearWorldTransformation() { } /*! * Returns false, since a linear WCS transformation cannot be empty. */ bool IsEmpty() const override { return false; } /*! */ WorldTransformation* Clone() const override { return new LinearWorldTransformation( *this ); } /*! */ DPoint Direct( const DPoint& p ) const override { return m_transWI.Transform( p ); } /*! */ DPoint Inverse( const DPoint& p ) const override { return m_transIW.Transform( p ); } /*! * Returns a reference to the internal linear transformation (from image to * native spherical coordinates). */ const LinearTransformation& ApproximateLinearTransform() const override { return m_transIW; } private: LinearTransformation m_transWI; // world -> image LinearTransformation m_transIW; // image -> world }; // ---------------------------------------------------------------------------- /*! * \class SplineWorldTransformation * \brief Surface spline world coordinate transformation * * %SplineWorldTransformation implements a world coordinate transform based on * 2-D interpolating/approximating surface splines (also known as thin * plates). These mathematical modeling devices allow for accurate * representations of coordinate systems subject to arbitrary local * distortions, which are impossible to achieve with linear transformations. * * The underlying implementation of this class uses the SurfaceSpline, * PointSurfaceSpline, SurfaceSimplifier and PointGridInterpolation classes. * * \ingroup astrometry_support */ class PCL_CLASS SplineWorldTransformation : public WorldTransformation { public: /*! * Constructs a 2-D spline based world coordinate transformation. * * \param controlPointsW Array of world control points. Each point in this * array must contain spherical coordinates in the * native world coordinate system of the astrometric * solution, expressed in degrees. For a given * point p in this array, p.x is the native * longitude coordinate and p.y is the latitude. * * \param controlPointsI Array of image control points. Each point in this * array must contain the coordinates on the X and Y * axes of the image plane corresponding to the same * point in the \a controlPointsW array. In other * words, there must be a one-to-one correspondence * between world and image control points. * * \param smoothness When this parameter is greater than zero, * approximating splines will be generated instead * of interpolating splines. The higher this value, * the closest will be the 2-D approximating surface * to the reference plane of the image. * Approximating surface splines are robust to * outlier control points and hence recommended in * virtually all cases. The value of this parameter * should be relatively small if \a enableSimplifier * is true, since the surface simplification * algorithm already performs robust rejection of * outliers. The default value of 0.025 is normally * quite appropriate. * * \param weights When the \a smoothness parameter is greater than * zero and this vector is not empty, it must define * a positive weight greater than zero for each * point defined by the \a controlPointsW and * \a controlPointsI arrays. If \a smoothness is * zero or negative, this parameter will be ignored. * See the PointSurfaceSpline::Initialize() member * function for detailed information on 2-D spline * node weights. If \a enableSimplifier is true, * control point weights are not used and hence the * value of this parameter will be ignored. * * \param order Derivative order of continuity. The default value * is 2, which is also the recommended value. Higher * orders may improve adaptability to complex field * distortions only in some special cases, but at * the risk of leading to ill-conditioned linear * systems, and hence to errors while building the * surface splines. * * \param enableSimplifier If true, a surface simplification algorithm will * be applied to the lists of control points for * surface spline generation. The use of surface * simplification greatly improves efficiency of * surface splines by removing all redundant points * and keeping only the control points required to * define the coordinate transformations accurately. * In addition, the applied surface simplification * algorithm implements robust PCA fitting and * outlier rejection techniques that improve the * generated interpolation devices in terms of * resilience to noise and invalid data in the * underlying astrometric solution. This option is * enabled by default. When this parameter is true, * the \a weights parameter is ignored. * * \param simplifierTolerance Tolerance of the surface simplification * algorithm in pixels. The default value is 0.25 * pixels. * * \param simplifierRejectFraction Fraction of rejected control points for * simplification of surface subregions. The default * value is 0.10. * * Thanks to the implemented surface simplification algorithms, we can work * with very large sets of control points to generate astrometric solutions * able to model strong and complex arbitrary field distortions accurately * and efficiently. Surface simplification concentrates control points just * where they are necessary to represent distortions, leaving undistorted * regions of the image covered by a minimal grid. * * Newly constructed instances are guaranteed to be valid (in the structural * and numerical senses; note that this does not necessarily imply that the * underlying astrometric solution is valid). In the event of invalid input * data or spline initialization problems, this constructor will throw an * Error exception. * * After surface spline initialization, an approximate linear transformation * will also be calculated automatically. */ SplineWorldTransformation( const Array& controlPointsW, const Array& controlPointsI, float smoothness = __PCL_WCS_DEFAULT_SPLINE_SMOOTHNESS, const FVector& weights = FVector(), int order = __PCL_WCS_DEFAULT_SPLINE_ORDER, bool enableSimplifier = __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_ENABLED, float simplifierTolerance = __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_TOLERANCE, float simplifierRejectFraction = __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_REJECT_FRACTION ) : m_controlPointsW( controlPointsW ) , m_controlPointsI( controlPointsI ) , m_order( order ) , m_smoothness( smoothness ) , m_weights( weights ) , m_enableSimplifier( enableSimplifier ) , m_simplifierTolerance( simplifierTolerance ) , m_simplifierRejectFraction( simplifierRejectFraction ) { InitializeSplines(); CalculateLinearApproximation(); } /*! * Constructs a %SplineWorldTransformation instance by deserializing the * specified raw \a data. * * An approximate linear transformation will be calculated automatically. */ SplineWorldTransformation( const ByteArray& data ) { Deserialize( data ); InitializeSplines(); CalculateLinearApproximation(); } /*! * Constructs a %SplineWorldTransformation instance by deserializing the * specified raw \a data, with a prescribed approximate linear * transformation \a linearTransIW. */ SplineWorldTransformation( const ByteArray& data, const LinearTransformation& linearTransIW ) { Deserialize( data ); InitializeSplines(); m_linearIW = linearTransIW; } /*! * Copy constructor. */ SplineWorldTransformation( const SplineWorldTransformation& ) = default; /*! * Move constructor. */ SplineWorldTransformation( SplineWorldTransformation&& ) = default; /*! * Virtual destructor. */ virtual ~SplineWorldTransformation() { } /*! * Copy assignment operator. Returns a reference to this object. */ SplineWorldTransformation& operator =( const SplineWorldTransformation& ) = default; /*! * Move assignment operator. Returns a reference to this object. */ SplineWorldTransformation& operator =( SplineWorldTransformation&& ) = default; /*! * Returns true iff this object has no working data. * * With the restrictions imposed by class constructors, this can only happen * if this object is an xvalue after move construction or assignment. */ bool IsEmpty() const override { return m_controlPointsW.IsEmpty() || m_controlPointsI.IsEmpty(); } /*! * Returns a dynamically allocated copy of this object. */ WorldTransformation* Clone() const override { return new SplineWorldTransformation( *this ); } /*! * Transforms from native spherical coordinates to image coordinates. * * The point \a p contains native spherical coordinates: \a p.x is the * native longitude and \a p.y is the native latitude, both expressed in * degrees. Returns image coordinates in pixels corresponding to \a p. * * If grid interpolations have been initialized for this object, and the * specified point \a p is included in the reference rectangle used for grid * initialization, they will be used transparently by this member function. * Otherwise the surface splines will be evaluated directly, which can be * much slower, depending on the number of control points defined by the * astrometric solution. * * \sa Inverse(), InitializeGridInterpolations() */ DPoint Direct( const DPoint& p ) const override { if ( m_gridWI.IsValid() ) if ( m_gridWI.ReferenceRect().IncludesFast( p ) ) return m_gridWI( p ); return m_splineWI( p ); } /*! * Transforms from image coordinates to native spherical coordinates. * * The specified point \a p contains image coordinates in pixels. Returns a * point \a q where \a q.x is the native longitude and \a q.y is the native * latitude, both expressed in degrees, corresponding to \a p. * * If grid interpolations have been initialized for this object, and the * specified point \a p is included in the reference rectangle used for grid * initialization, they will be used transparently by this member function. * Otherwise the surface splines will be evaluated directly, which can be * much slower, depending on the number of control points defined by the * astrometric solution. * * \sa Direct(), InitializeGridInterpolations() */ DPoint Inverse( const DPoint& p ) const override { if ( m_gridIW.IsValid() ) if ( m_gridIW.ReferenceRect().IncludesFast( p ) ) return m_gridIW( p ); return m_splineIW( p ); } /*! * Returns an approximate linear transformation from image to native * spherical coordinates, computed from the internal point surface splines. */ const LinearTransformation& ApproximateLinearTransform() const override { return m_linearIW; } /*! * Initializes the internal grid interpolation devices for the specified * reference rectangular region \a rect and grid distance \a delta in * pixels. * * A grid distance of 24 pixels is applied by default. This is normally more * than sufficient to yield accurate coordinate readouts, even for strongly * distorted images. * * See GridInterpolation::Initialize() for detailed information on spline * grid interpolation and its working parameters. * * \note This member function will show no console messages. If some * feedback must be provided to the user during the potentially long * operation, it must be given before calling this function. */ void InitializeGridInterpolations( const Rect& rect, int delta = 24 ) { m_gridWI.Initialize( rect, delta, m_splineWI, false/*verbose*/ ); m_gridIW.Initialize( rect, delta, m_splineIW, false/*verbose*/ ); } /*! * Returns true if the internal grid interpolation devices have been * initialized. See InitializeGridInterpolations() for information on grid * interpolation. */ bool HasGridInterpolations() const { return m_gridWI.IsValid() && m_gridIW.IsValid(); } /*! * Serializes this %SplineWorldTransformation instance in raw binary format * and stores the result in the specified \a data array. */ void Serialize( ByteArray& data ) const; /*! * Returns the number of control points employed to generate the surface * splines used for coordinate transformations. * * The value returned by this function is the length of the original list of * control points specified upon object construction or deserialization; the * actual sets of data points being used internally by surface splines are * usually smaller because surface simplification is enabled by default. * * \sa NativeControlPoints(), ImageControlPoints(), GetSplineLengths() */ int NumberOfControlPoints() const { return int( m_controlPointsW.Length() ); } /*! * Returns a reference to the list of control points in native spherical * coordinates. * * For each point \e p in the returned array, \e p.x is the native longitude * and \e p.y is the native latitude, both expressed in degrees. * * For each point \a p in the returned array, there is a corresponding point * \a q in the array of image control points returned by the * \a ImageControlPoints() member function, whose \e q.x and \e q.y * components are the image coordinates in pixels corresponding to \e p. */ const Array& NativeControlPoints() const { return m_controlPointsW; } /*! * Returns a reference to the list of control points in image coordinates. * * For each point \e p in the returned array, \e p.x and \e p.y are the * components of a control point in image coordinates, expressed in pixels. * * For each point \a p in the returned array, there is a corresponding point * \a q in the array of native control points returned by the * \a NativeControlPoints() member function, whose \e q.x and \e q.y * components are the spherical native coordinates in degrees corresponding * to \e p. */ const Array& ImageControlPoints() const { return m_controlPointsI; } /*! * Provides the number of data points in the internal surface splines used * for coordinate transformations. * * \param[out] xWI Number of spline points used for world-to-image * coordinate transformations on the native longitude * direction. * * \param[out] yWI Number of spline points used for world-to-image * coordinate transformations on the native latitude * direction. * * \param[out] xIW Number of spline points used for image-to-world * coordinate transformations on the X-axis direction. * * \param[out] yIW Number of spline points used for image-to-world * coordinate transformations on the Y-axis direction. */ void GetSplineLengths( int& xWI, int& yWI, int& xIW, int& yIW ) const { xWI = m_splineWI.SplineX().Length(); yWI = m_splineWI.SplineY().Length(); xIW = m_splineIW.SplineX().Length(); yIW = m_splineIW.SplineY().Length(); } /*! * Returns true iff the lists of transformation control points were * truncated before generation of surface splines. If surface simplification * is enabled, truncation may happen, when necessary, after simplification. * If no simplification is used, truncation may happen directly on the * original lists of control points. * * A maximum of 2100 control points is imposed in the current implementation * to prevent excessive execution times for surface spline generation, which * grow approximately with O(n^3) complexity. */ bool TruncatedControlPoints() const { return m_truncated; } /*! * Returns true iff the surface simplification algorithm has been enabled * for generation of surface splines. See the class constructor for more * information. */ bool IsSimplifierEnabled() const { return m_enableSimplifier; } /*! * Returns the tolerance in pixels of the surface simplifier used for * generation of surface splines. See the class constructor for more * information. */ float SimplifierTolerance() const { return m_simplifierTolerance; } /*! * Returns the outlier rejection fraction of the surface simplifier used for * generation of surface splines. See the class constructor for more * information. */ float SimplifierRejectFraction() const { return m_simplifierRejectFraction; } private: Array m_controlPointsW; Array m_controlPointsI; int m_order = __PCL_WCS_DEFAULT_SPLINE_ORDER; float m_smoothness = __PCL_WCS_DEFAULT_SPLINE_SMOOTHNESS; FVector m_weights; bool m_enableSimplifier = __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_ENABLED; float m_simplifierTolerance = __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_TOLERANCE; float m_simplifierRejectFraction = __PCL_WCS_SURFACE_SIMPLIFIER_DEFAULT_REJECT_FRACTION; bool m_truncated = false; PointSurfaceSpline m_splineWI; PointSurfaceSpline m_splineIW; PointGridInterpolation m_gridWI; PointGridInterpolation m_gridIW; LinearTransformation m_linearIW; void Deserialize( const ByteArray& ); void InitializeSplines(); void CalculateLinearApproximation(); }; // ---------------------------------------------------------------------------- } // pcl #endif // __PCL_WorldTransformation_h // ---------------------------------------------------------------------------- // EOF pcl/WorldTransformation.h - Released 2022-03-12T18:59:29Z