// ____ ______ __ // / __ \ / ____// / // / /_/ // / / / // / ____// /___ / /___ PixInsight Class Library // /_/ \____//_____/ PCL 2.4.23 // ---------------------------------------------------------------------------- // pcl/PSFFit.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_PSFFit_h #define __PCL_PSFFit_h /// \file pcl/PSFFit.h #include #include #include #include #include namespace pcl { // ---------------------------------------------------------------------------- /*! * \namespace pcl::PSFunction * \brief Point spread function types. * * * * * * * * * * * * * *
PSFunction::Invalid Represents an invalid or unsupported PSF type.
PSFunction::Gaussian Gaussian PSF.
PSFunction::Moffat Moffat PSF with a fitted beta parameter.
PSFunction::MoffatA Moffat PSF with fixed beta=10 parameter.
PSFunction::Moffat8 Moffat PSF with fixed beta=8 parameter.
PSFunction::Moffat6 Moffat PSF with fixed beta=6 parameter.
PSFunction::Moffat4 Moffat PSF with fixed beta=4 parameter.
PSFunction::Moffat25 Moffat PSF with fixed beta=2.5 parameter.
PSFunction::Moffat15 Moffat PSF with fixed beta=1.5 parameter.
PSFunction::Lorentzian Lorentzian PSF, equivalent to a Moffat PSF with fixed beta=1 parameter.
PSFunction::VariableShape Variable shape PSF
*/ namespace PSFunction { enum value_type { Invalid = -1, // Represents an invalid or unsupported PSF type Gaussian = 0, // Gaussian PSF Moffat, // Moffat PSF with a fitted beta parameter MoffatA, // Moffat PSF with fixed beta=10 Moffat8, // Moffat PSF with fixed beta=8 Moffat6, // Moffat PSF with fixed beta=6 Moffat4, // Moffat PSF with fixed beta=4 Moffat25, // Moffat PSF with fixed beta=2.5 Moffat15, // Moffat PSF with fixed beta=1.5 Lorentzian, // Lorentzian PSF, equivalent to a Moffat PSF with fixed beta=1 VariableShape, // Variable shape PSF NumberOfFunctions, Auto = 999, // Automatic selection for optimization, process-dependent. Default = Gaussian }; } // ---------------------------------------------------------------------------- /*! * \namespace pcl::PSFFitStatus * \brief PSF fit process results. * * * * * * * * * * *
PSFFitStatus::Invalid Represents an invalid or unsupported PSF fit status
PSFFitStatus::NotFitted The PSF has not been fitted because the process has not been executed.
PSFFitStatus::FittedOk PSF fitted correctly. The relative error of the solution is at most equal to the specified tolerance.
PSFFitStatus::BadParameters The PSF fitting process failed because of improper input parameters.
PSFFitStatus::NoSolution There is no solution with the specified parameters and source data.
PSFFitStatus::NoConvergence The Levenberg-Marquardt algorithm did not find a valid solution after a prescribed maximum number of iterations.
PSFFitStatus::InaccurateSolution A PSF has been fitted, but the Levenberg-Marquardt algorithm couldn't find a valid solution to the specified tolerance.
PSFFitStatus::UnknownError The PSF fitting process failed for an unspecified reason.
*/ namespace PSFFitStatus { enum value_type { Invalid = -1, NotFitted = 0, FittedOk, BadParameters, NoSolution, NoConvergence, InaccurateSolution, UnknownError }; } // ---------------------------------------------------------------------------- /*! * \class PSFData * \brief PSF fit parameters. */ struct PSFData { /*! * Represents a point spread function type. */ typedef PSFunction::value_type psf_function; /*! * Represents a PSF fitting process status. */ typedef PSFFitStatus::value_type psf_fit_status; /*! * Represents a component of a centroid position vector (required for * QuadTree compatibility). */ typedef DPoint::component component; psf_function function = PSFunction::Invalid; //!< Point spread function type (PSFunction namespace). bool circular = false; //!< Circular or elliptical PSF. psf_fit_status status = PSFFitStatus::NotFitted; //!< Status code (PSFFitStatus namespace). bool celestial = false; //!< True iff equatorial coordinates are available. double B = 0; //!< Local background estimate in pixel value units. double A = 0; //!< Function amplitude (or estimated maximum) in pixel value units. DPoint b0 = 0.0; //!< Barycenter position (initial star detection position) in image coordinates. DPoint c0 = 0.0; //!< Fitted centroid position in image coordinates. DPoint q0 = 0.0; //!< Centroid equatorial coordinates, when celestial=true. double sx = 0; //!< Function width in pixels on the X axis, sx >= sy. double sy = 0; //!< Function width in pixels on the Y axis, sx >= sy. double theta = 0; //!< Rotation angle of the sx axis in degrees, in the [0,180) range. double beta = 0; //!< Moffat beta or shape parameter (dimensionless). double flux = 0; //!< Total flux above the local background, measured over the rectangular fitting region. double signal = 0; //!< Total flux above the local background, measured over the elliptical PSF region. unsigned signalCount = 0; //!< Number of pixels used for signal evaluation. double mad = 0; /*!< Goodness of fit estimate. A robust, mean absolute difference between the fitted PSF and the sample of source image pixels over the fitting region, scaled by the total signal estimate. */ /*! * Default constructor. */ PSFData() = default; /*! * Copy constructor. */ PSFData( const PSFData& ) = default; /*! * Move constructor. */ PSFData( PSFData&& ) = default; /*! * Copy assignment operator. Returns a reference to this object. */ PSFData& operator =( const PSFData& ) = default; /*! * Move assignment operator. Returns a reference to this object. */ PSFData& operator =( PSFData&& ) = default; /*! * Returns true iff this object contained valid PSF fitting parameters. This * happens (or \e should happen, under normal conditions) when the status * data member is equal to PSFFitStatus::FittedOk. */ operator bool() const { return status == PSFFitStatus::FittedOk; } /*! * Conversion to double operator. Returns the PSF signal estimate if * nonzero, or the total flux otherwise. Useful for sorting and statistics * calculations. */ operator double() const { return (signal != 0) ? signal : flux; } /*! * Returns a centroid coordinate. Returns the X centroid coordinate if the * specified index \a i is zero, or the Y coordinate otherwise. * * \note This operator provides compatibility with the QuadTree class. */ double operator []( int i ) const noexcept { return (i == 0) ? c0.x : c0.y; } /*! * Returns the name of the fitted PSF function. See the PSFunction namespace * for supported functions. */ String FunctionName() const; /*! * Returns a string with a brief description of the current fitting status. */ String StatusText() const; /*! * Returns the full width at half maximum (FWHM) on the X axis in pixels. * * For an elliptic PSF, the X axis corresponds to the orientation of the * major function axis as projected on the image. * * For a circular PSF, FWHMx() and FWHMy() are equivalent. */ double FWHMx() const { return FWHM( function, sx, beta ); } /*! * Returns the full width at half maximum (FWHM) on the Y axis in pixels. * * For an elliptic PSF, the Y axis corresponds to the orientation of the * minor function axis as projected on the image. * * For a circular PSF, FWHMx() and FWHMy() are equivalent. */ double FWHMy() const { return FWHM( function, sy, beta ); } /*! * Returns the full width at tenth maximum (FWTM) on the X axis in pixels. * * For an elliptic PSF, the X axis corresponds to the orientation of the * major function axis as projected on the image. * * For a circular PSF, FWTMx() and FWTMy() are equivalent. */ double FWTMx() const { return FWTM( function, sx, beta ); } /*! * Returns the full width at tenth maximum (FWTM) on the Y axis in pixels. * * For an elliptic PSF, the Y axis corresponds to the orientation of the * minor function axis as projected on the image. * * For a circular PSF, FWTMx() and FWTMy() are equivalent. */ double FWTMy() const { return FWTM( function, sy, beta ); } /*! * Returns the double integral of the PSF, or the estimated volume over the * XY plane for z > B. */ double Volume() const { return A*Volume( function, sx, sy, beta ); } /*! * Returns the PSF bounding rectangle. The coordinates of the bounding * rectangle \a r are calculated as follows: * * r.x0 = c0.x - d \n * r.y0 = c0.y - d \n * r.x1 = c0.x + d \n * r.y1 = c0.y + d \n * * where d is equal to: * * FWHMx()/2 for a circular PSF, \n * Max( FWHMx(), FWHMy() )/2 for an elliptic PSF. */ DRect Bounds() const { double d = (circular ? FWHMx() : Max( FWHMx(), FWHMy() ))/2; return DRect( c0.x-d, c0.y-d, c0.x+d, c0.y+d ); } /*! * Returns true iff this object contains valid equatorial coordinates * computed by an astrometric solution of the image for the centroid * coordinates. */ bool HasCelestialCoordinates() const { return celestial; } /*! * Returns an image representation (in 32-bit floating point format) of the * fitted point spread function. */ void ToImage( Image& ) const; /*! * Returns the full width at half maximum (FWHM) corresponding to a * supported function with the specified parameters. * * \param function The type of point spread function. See the PSFunction * namespace for supported functions. * * \param sigma Estimated function width. * * \param beta Moffat beta or VariableShape shape parameter. * Must be > 0. * * The returned value is the FWHM in sigma units, or zero if an invalid or * unsupported function type has been specified. */ static double FWHM( psf_function function, double sigma, double beta = 2 ) { PCL_PRECONDITION( beta > 0 ) switch ( function ) { case PSFunction::Gaussian: return 2.3548200450309493 * sigma; case PSFunction::Moffat: return 2 * sigma * Sqrt( Pow2( 1/beta ) - 1 ); case PSFunction::MoffatA: return 0.5358113941912513 * sigma; case PSFunction::Moffat8: return 0.6016900619596693 * sigma; case PSFunction::Moffat6: return 0.6998915581984769 * sigma; case PSFunction::Moffat4: return 0.8699588840921645 * sigma; case PSFunction::Moffat25: return 1.1305006161394060 * sigma; case PSFunction::Moffat15: return 1.5328418730817597 * sigma; case PSFunction::Lorentzian: return 2 * sigma; case PSFunction::VariableShape: return 2 * sigma * Pow( beta*0.6931471805599453, 1/beta ); default: return 0; // ?! } } /*! * Returns the full width at tenth maximum (FWTM) corresponding to a * supported function with the specified parameters. * * \param function The type of point spread function. See the PSFunction * namespace for supported functions. * * \param sigma Estimated function width. * * \param beta Moffat beta or VariableShape shape parameter. * Must be > 0. * * The returned value is the FWTM in sigma units, or zero if an invalid or * unsupported function type has been specified. */ static double FWTM( psf_function function, double sigma, double beta = 2 ) { PCL_PRECONDITION( beta > 0 ) switch ( function ) { case PSFunction::Gaussian: return 4.291932052578694 * sigma; case PSFunction::Moffat: return 2 * sigma * Sqrt( Pow( 10.0, 1/beta ) - 1 ); case PSFunction::MoffatA: return 1.017694279819175 * sigma; case PSFunction::Moffat8: return 1.155026289161115 * sigma; case PSFunction::Moffat6: return 1.367917055412454 * sigma; case PSFunction::Moffat4: return 1.764402913213331 * sigma; case PSFunction::Moffat25: return 2.459175822514185 * sigma; case PSFunction::Moffat15: return 3.816589489904712 * sigma; case PSFunction::Lorentzian: return 6 * sigma; case PSFunction::VariableShape: return 2 * sigma * Pow( beta*2.302585092994045, 1/beta ); default: return 0; // ?! } } /*! * Returns the double integral of a supported elliptical function with the * specified parameters. * * \param function The type of point spread function. See the PSFunction * namespace for supported functions. * * \param sigma_x Estimated function width on the X axis. * * \param sigma_y Estimated function width on the Y axis. * * \param beta Moffat beta or VariableShape shape parameter. * Must be > 0. Must be > 1 for Moffat and Lorentzian * functions. * * The returned value is the volume of the PSF over the XY plane, or zero if * an invalid or unsupported function type has been specified, or if the * \a beta parameter is invalid for the type of PSF specified. */ static double Volume( psf_function function, double sigma_x, double sigma_y, double beta = 2 ) { PCL_PRECONDITION( beta > 0 ) PCL_PRECONDITION( function != PSFunction::Lorentzian && (function != PSFunction::Moffat || beta > 1) ) switch ( function ) { case PSFunction::Gaussian: return 6.2831853071795862 * sigma_x*sigma_y; case PSFunction::Moffat: return 3.1415926535897931 * sigma_x*sigma_y/(beta - 1); case PSFunction::MoffatA: return 0.3490658503988659 * sigma_x*sigma_y; case PSFunction::Moffat8: return 0.4487989505128276 * sigma_x*sigma_y; case PSFunction::Moffat6: return 0.6283185307179586 * sigma_x*sigma_y; case PSFunction::Moffat4: return 1.0471975511965976 * sigma_x*sigma_y; case PSFunction::Moffat25: return 2.0943951023931953 * sigma_x*sigma_y; case PSFunction::Moffat15: return 6.2831853071795862 * sigma_x*sigma_y; default: return 0; // ?! } } }; // ---------------------------------------------------------------------------- /*! * \class PSFFit * \brief Numerical Point Spread Function (PSF) fit to a source in an image. * \sa StarDetector, PSFSignalEstimator, PSFScaleEstimator */ class PSFFit { public: /*! * Represents a point spread function type. */ typedef PSFData::psf_function psf_function; /*! * Represents a PSF fitting process status. */ typedef PSFData::psf_fit_status psf_fit_status; /*! * Fitted PSF parameters. */ PSFData psf; /*! * Fits a point spread function to a source in the specified \a image. * * \param image The source image. * * \param center The intial search point in image coordinates. For * consistent results, these coordinates must be the * result of a robust object detection process. * * \param rect The PSF sampling region in image coordinates. * This is the rectangular region of the image where * the function fitting process will take place. PSF * parameters will be evaluated from source pixel * values acquired exclusively from this region. * * \param function The point spread function type to be fitted. A * Gaussian PSF is fitted by default. * * \param circular Whether to fit a circular or an elliptical PSF. * Elliptical functions are fitted by default. * * \param betaMin,betaMax The range of shape parameter values when * \a function is PSFunction::VariableShape; ignored * for other point spread functions. When the values of these * parameters are such that \a betaMin < \a betaMax, an optimal * value of the beta (shape) PSF parameter will be searched for * iteratively within the specified range. The shape parameter will be * optimized for minimization of the absolute difference between the * estimated PSF and the sample of source image pixels. When * \a betaMin ≥ \a betaMax, the shape parameter will stay constant * and equal to the specified \a betaMin value during the entire PSF * fitting process, and the rest of PSF parameters will be estimated * accordingly. The valid range of beta parameter values is [1.0,6.0]. * Values outside this range may lead to numerically unstable PSF * fitting processes. * * \param tolerance Tolerance of the Levenberg-Marquardt algorithm. * The default value is 1.0e-08. The valid range is * from 1.0e-12 to 0.001. * * \param bkgMaxVar Maximum relative difference allowed between the * fitted local background (\e B PSF parameter) and * the initial local background estimated as the median pixel sample * value of the sampling region. The default value is 0.1, meaning * that a maximum relative difference of a 10% will be allowed. This * parameter is useful to enforce stability of the PSF fitting * process: assuming that the median pixel sample value is a robust * estimate of the local background, by constraining the range of * variation of the \e B parameter the PSF fitting process can be more * accurate and robust. * * \param growthForFlux Growing factor in units of the Full Width at * Tenth Maximum (FWTM) for extension/contraction of * the PSF flux measurement region. The default * value is 1.0. * * The implementation of the Levenberg-Marquardt algorithm used internally * by this function is extremely sensitive to the specified \a center and * \a rect parameters. These starting parameters should always be * calculated using robust procedures to achieve consistent results. * * In the most frequent use case, where a star detection procedure is * typically used to obtain these starting parameters, robustness to poorly * sampled data and resilience to outlier pixels and noise are particularly * important to this task. */ PSFFit( const ImageVariant& image, const DPoint& center, const DRect& rect, psf_function function = PSFunction::Gaussian, bool circular = false, float betaMin = 1.0F, float betaMax = 4.0F, double tolerance = 1.0e-08, float bkgMaxVar = 0.1F, float growthForFlux = 1.0F ); /*! * Copy constructor. */ PSFFit( const PSFFit& ) = default; /*! * Move constructor. */ PSFFit( PSFFit&& ) = default; /*! * Copy assignment opèrator. Returns a reference to this object. */ PSFFit& operator =( const PSFFit& x ) = default; /*! * Move assignment operator. Returns a reference to this object. */ PSFFit& operator =( PSFFit&& x ) = default; /*! * Returns true iff this object contains a valid PSF fit, i.e. valid PSF * fitted parameters. */ operator bool() const { return psf; } private: Matrix S; // matrix of sampled pixel data Vector P; // vector of function parameters // The initial local background measured on the sampling region and the // maximum allowed relative difference, for stabilization of local // background PSF parameters. double m_bkg; float m_bkgMaxVar; // Growing factor in units of the Full Width at Tenth Maximum (FWTM) for // extension/contraction of the PSF flux measurement region. float m_growthForFlux = 1.0F; // Keep track of successive beta values in L-M iterations for stabilization // of shape parameters. This guarantees convergence for Moffat functions. mutable float m_beta; Vector GoodnessOfFit( psf_function, bool circular, bool test = false ) const; friend class PSFFitEngine; }; // ---------------------------------------------------------------------------- } // pcl #endif // __PCL_PSFFit_h // ---------------------------------------------------------------------------- // EOF pcl/PSFFit.h - Released 2022-03-12T18:59:29Z