// ____ ______ __ // / __ \ / ____// / // / /_/ // / / / // / ____// /___ / /___ PixInsight Class Library // /_/ \____//_____/ PCL 2.4.23 // ---------------------------------------------------------------------------- // pcl/HistogramTransformation.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_HistogramTransformation_h #define __PCL_HistogramTransformation_h /// \file pcl/HistogramTransformation.h #include #include #include #include namespace pcl { // ---------------------------------------------------------------------------- class PCL_CLASS Histogram; /*! * \class HistogramTransformation * \brief Multiple histogram transformation. * * ### TODO: Write a detailed description for %HistogramTransformation. */ class PCL_CLASS HistogramTransformation : public ImageTransformation, public ParallelProcess { public: /*! * Represents a set of chained histogram transformations. */ typedef Array transformation_list; /*! * Constructs a %HistogramTransformation object with the specified * parameters. * * \param mb Midtones balance in the range [0,1]. * * \param sc Shadows clipping point in the range [0,1]. * * \param hc Highlights clipping point in the range [0,1]. * * \param lr Low dynamic range expansion bound, \a lr ≤ 0. * * \param hr High dynamic range expansion bound, \a hr ≥ 1. * * The default parameter values define an identity transformation: * * \a mb = 0.5, \a sc = 0, \a hc = 1, \a lr = 0, \a hr = 1. */ HistogramTransformation( double mb = 0.5, double sc = 0, double hc = 1, double lr = 0, double hr = 1 ) { PCL_PRECONDITION( mb >= 0 && mb <= 1 ) PCL_PRECONDITION( sc >= 0 && sc <= 1 ) PCL_PRECONDITION( hc >= 0 && hc <= 1 ) PCL_PRECONDITION( lr <= 0 ) PCL_PRECONDITION( hr >= 1 ) m_midtonesBalance = pcl::Range( mb, 0.0, 1.0 ); m_clipLow = pcl::Range( sc, 0.0, 1.0 ); m_clipHigh = pcl::Range( hc, 0.0, 1.0 ); if ( m_clipHigh < m_clipLow ) pcl::Swap( m_clipLow, m_clipHigh ); m_expandLow = Min( lr, 0.0 ); m_expandHigh = Max( 1.0, hr ); UpdateFlags(); } /*! * Copy constructor. */ HistogramTransformation( const HistogramTransformation& ) = default; /*! * Move constructor. */ HistogramTransformation( HistogramTransformation&& ) = default; /*! * Destroys a %HistogramTransformation object. */ virtual ~HistogramTransformation() { } /*! * Copy assignment operator. Returns a reference to this object. */ HistogramTransformation& operator =( const HistogramTransformation& ) = default; /*! * Move assignment operator. Returns a reference to this object. */ HistogramTransformation& operator =( HistogramTransformation&& ) = default; /*! * Returns the number of histogram transforms in the transformation chain. */ size_type Length() const { return 1 + m_transformChain.Length(); } /*! * Returns a reference to the (immutable) transformation at the specified * \a index in the current transformation chain. */ const HistogramTransformation& operator []( size_type index ) const { return (index == 0) ? *this : m_transformChain[index-1]; } /*! * Returns a reference to the (mutable) transformation at the specified * \a index in this transformation chain. */ HistogramTransformation& operator []( size_type index ) { return (index == 0) ? *this : m_transformChain[index-1]; } /*! * Returns a reference to the (immutable) first histogram transformation in * the current chain. The returned value is always a reference to this * object. */ const HistogramTransformation& operator *() const { return *this; } /*! * Returns a reference to the (mutable) first histogram transformation in * the current chain. The returned value is always a reference to this * object. */ HistogramTransformation& operator *() { return *this; } /*! * Adds a histogram transformation to the transformation chain of this * %HistogramTransformation object. */ void Add( const HistogramTransformation& H ) { m_transformChain.Add( H ); } /*! # */ double MidtonesBalance() const { return m_midtonesBalance; } /*! # */ double ShadowsClipping() const { return m_clipLow; } /*! # */ double HighlightsClipping() const { return m_clipHigh; } /*! # */ double LowRange() const { return m_expandLow; } /*! # */ double HighRange() const { return m_expandHigh; } /*! # */ bool IsIdentityTransformation() const { return m_midtonesBalance == 0.5 && m_clipLow == 0 && m_clipHigh == 1 && m_expandLow == 0 && m_expandHigh == 1; } /*! # */ bool IsIdentityTransformationSet() const; /*! # */ void SetMidtonesBalance( double b ) { PCL_PRECONDITION( b >= 0 && b <= 1 ) m_midtonesBalance = pcl::Range( b, 0.0, 1.0 ); UpdateFlags(); } /*! # */ void SetShadowsClipping( double c ) { PCL_PRECONDITION( c >= 0 && c <= 1 ) m_clipLow = pcl::Range( c, 0.0, 1.0 ); if ( m_clipHigh < m_clipLow ) pcl::Swap( m_clipLow, m_clipHigh ); UpdateFlags(); } /*! # */ void SetHighlightsClipping( double c ) { PCL_PRECONDITION( c >= 0 && c <= 1 ) m_clipHigh = pcl::Range( c, 0.0, 1.0 ); if ( m_clipHigh < m_clipLow ) pcl::Swap( m_clipLow, m_clipHigh ); UpdateFlags(); } /*! # */ void SetClipping( double sc, double hc ) { PCL_PRECONDITION( sc >= 0 && sc <= 1 ) PCL_PRECONDITION( hc >= 0 && hc <= 1 ) m_clipLow = pcl::Range( sc, 0.0, 1.0 ); m_clipHigh = pcl::Range( hc, 0.0, 1.0 ); if ( m_clipHigh < m_clipLow ) pcl::Swap( m_clipLow, m_clipHigh ); UpdateFlags(); } /*! # */ void SetLowRange( double r ) { PCL_PRECONDITION( r <= 0 ) m_expandLow = Min( 0.0, r ); UpdateFlags(); } /*! # */ void SetHighRange( double r ) { PCL_PRECONDITION( r >= 1 ) m_expandHigh = Max( 1.0, r ); UpdateFlags(); } /*! # */ void SetRange( double lr, double hr ) { PCL_PRECONDITION( lr <= 0 ) PCL_PRECONDITION( hr >= 1 ) m_expandLow = Min( 0.0, lr ); m_expandHigh = Max( 1.0, hr ); UpdateFlags(); } /*! * Resets all transformation parameters to yield an identity histogram * transformation. */ void Reset() { m_midtonesBalance = 0.5; m_clipLow = 0; m_clipHigh = 1; m_expandLow = 0; m_expandHigh = 1; m_transformChain.Clear(); UpdateFlags(); } // Avoid "hides virtual function in base" warnings by clang using ImageTransformation::Apply; /*! # */ void Apply( double*, size_type, double x0 = 0, double x1 = 1 ) const; /*! # */ void Apply( float*, size_type, float x0 = 0, float x1 = 1 ) const; /*! # */ void Apply( Histogram& dstH, const Histogram& srcH ) const; /*! # */ void Make8BitLUT( uint8* ) const; /*! # */ void Make16BitLUT( uint8* ) const; /*! # */ void Make16BitLUT( uint16* ) const; /*! # */ void Make20BitLUT( uint8* ) const; /*! # */ void Make20BitLUT( uint16* ) const; /*! # */ void Make24BitLUT( uint8* ) const; /*! # */ void Make24BitLUT( uint16* ) const; /*! * Returns the value of the midtones transfer function (MTF) for a * given midtones balance \a m and a sample point \a x. Both \a m and \a x * must be in the range [0,1]. */ static double MidtonesTransferFunction( double m, double x ) { /* * Bulirsch-Stoer algorithm for a diagonal rational interpolation * function with three fixed data points: (0,0) (m,1/2) (1,1). * * This is the MTF function after direct substitution in the B-S * equations: * * double r = 1 + 0.5/((x - m)/(x - 1)/2 - 1); * return r + r/(x/(x - 1) * (1 - r/(r - 0.5)) - 1); * * We can simplify: * * double r = (m - 1)/(x + m - 2); * * and then we can further simplify to: * * return (m - 1)*x/((2*m - 1)*x - m); * * Finally, precalculating (m - 1) we can save a multiplication. */ if ( x > 0 ) // guard us against degenerate cases { if ( x < 1 ) { double m1 = m - 1; return m1*x/((m + m1)*x - m); } return 1; } return 0; } /*! * A convenience synonym for MidtonesTransferFunction( m, x ). */ static double MTF( double m, double x ) { return MidtonesTransferFunction( m, x ); } /*! * Transforms a real \a value in the normalized [0,1] range. */ void Transform( double& value ) const { if ( m_flags.hasClipping ) value = m_flags.hasDelta ? ((value <= m_clipLow) ? 0.0 : ((value >= m_clipHigh) ? 1.0 : (value - m_clipLow)/m_flags.d)) : m_clipLow; if ( m_flags.hasMTF ) value = HistogramTransformation::MTF( m_midtonesBalance, value ); if ( m_flags.hasRange ) value = (value - m_expandLow)/m_flags.dr; } /*! * \struct pcl::HistogramTransformation::Flags * \brief Characterizes a histogram transformation pertaining to a * histogram transformation chain. */ struct Flags { double d = 1.0; //!< Total width of the stretched dynamic range double dr = 1.0; //!< Total width of the expanded dynamic range bool hasClipping = false; //!< The transformation defines clipping parameters bool hasMTF = false; //!< The transformation defines a midtones transfer function bool hasRange = false; //!< The transformation defines dynamic range expansion parameters bool hasDelta = false; //!< The transformation defines a nonzero stretched range of sample values /*! * Default constructor. */ Flags() = default; /*! * Copy constructor. */ Flags( const Flags& ) = default; /*! * Constructs a new %Flags object initialized for the parameters of the * specified HistogramTransformation \a H. */ Flags( const HistogramTransformation& H ) { hasClipping = H.m_clipLow != 0 || H.m_clipHigh != 1; hasMTF = H.m_midtonesBalance != 0.5; hasRange = H.m_expandLow != 0 || H.m_expandHigh != 1; hasDelta = false; if ( hasClipping ) { d = H.m_clipHigh - H.m_clipLow; hasDelta = 1 + d != 1; } if ( hasRange ) dr = H.m_expandHigh - H.m_expandLow; } }; /*! * Returns the set of flags characterizing this histogram transformation. */ Flags TransformationFlags() const { return m_flags; } private: double m_midtonesBalance = 0.5; // midtones balance double m_clipLow = 0; // shadows clipping point double m_clipHigh = 1; // highlights clipping point double m_expandLow = 0; // shadows dynamic range expansion double m_expandHigh = 1; // highlights dynamic range expansion Flags m_flags; // transformation flags transformation_list m_transformChain; // more transformations /*! * \internal */ void UpdateFlags() { m_flags = Flags( *this ); } void Apply( pcl::Image& ) const override; void Apply( pcl::DImage& ) const override; void Apply( pcl::UInt8Image& ) const override; void Apply( pcl::UInt16Image& ) const override; void Apply( pcl::UInt32Image& ) const override; friend class LUT2408Thread; friend class LUT2416Thread; }; // ---------------------------------------------------------------------------- } // pcl #endif // __PCL_HistogramTransformation_h // ---------------------------------------------------------------------------- // EOF pcl/HistogramTransformation.h - Released 2022-03-12T18:59:29Z