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

1588 lines
50 KiB
C++

// ____ ______ __
// / __ \ / ____// /
// / /_/ // / / /
// / ____// /___ / /___ PixInsight Class Library
// /_/ \____//_____/ PCL 2.4.23
// ----------------------------------------------------------------------------
// pcl/RGBColorSystem.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_RGBColorSystem_h
#define __PCL_RGBColorSystem_h
/// \file pcl/RGBColorSystem.h
#include <pcl/Defs.h>
#include <pcl/Math.h>
#include <pcl/ReferenceCounter.h>
#include <pcl/Vector.h>
#define _1_3 3.333333333333333e-01 // 1/3
#define _1_6 1.666666666666667e-01 // 1/6
#define _2_3 6.666666666666667e-01 // 2/3
#define _16_116 1.379310344827586e-01 // 16/116
#define CIEEpsilon 8.856451679035631e-03 // 216/24389
#define CIEKappa 9.032962962962963e+02 // 24389/27
#define CIEKappa116 7.787037037037037e+00 // CIEKappa/116
#define sRGBEpsilon 0.04045
#define sRGBEpsilonInv 0.0031308
#define sRGBGamma 2.4
#define sRGBGammaInv 4.166666666666667e-01 //1/sRGBGamma
namespace pcl
{
// ----------------------------------------------------------------------------
/*!
* \class RGBColorSystem
* \brief A colorimetrically defined RGB working color space.
*
* %RGBColorSystem is a rigorous and efficient implementation of a RGB working
* space (RGBWS). This class provides all conversions between the supported
* color spaces: RGB, CIE XYZ, CIE L*a*b*, CIE L*c*h* and grayscale, plus the
* HSV and HSI color ordering systems, which are also supported for
* convenience, although obviously not linked to any particular RGBWS.
*
* Color components and channel values are represented as reals by means of
* \c double floating point variables. Normalization of components and channel
* values to the standard real range [0,1] is ensured for all supported color
* spaces and color ordering systems.
*
* Chrominance coordinates and luminance coefficients are always considered
* relative to the D50 reference white, in conformance to the ICC standard.
*
* Finally, our implementation maximizes dynamic range usage (coding
* efficiency) by ensuring that all components and channel values make use of
* the entire [0,1] normalized real range to the maximum possible extent.
*
* \sa ImageColor, AbstractImage, GenericImage, ImageVariant
*/
class PCL_CLASS RGBColorSystem
{
public:
/*!
* The type used to represent components and channel values for all
* supported color spaces and color ordering systems.
*/
typedef double sample;
/*!
* Constructs a %RGBColorSystem object as a new instance of the default RGB
* working space. The default RGBWS is sRGB in current versions of the
* PixInsight platform.
*
* This constructor increments the reference counter of the source RGB
* working space data.
*/
RGBColorSystem()
: m_data( RGBColorSystem::sRGB.m_data )
{
if ( m_data != nullptr )
m_data->Attach();
else
m_data = new Data( 2.2F/*gamma*/, true/*issRGB*/, sRGB_x, sRGB_y, sRGB_Y );
}
/*!
* Constructs a %RGBColorSystem object as a copy of an existing instance.
*
* This constructor increments the reference counter of the source RGB
* working space data.
*/
RGBColorSystem( const RGBColorSystem& s )
: m_data( s.m_data )
{
m_data->Attach();
}
/*!
* Constructs a new %RGBColorSystem instance by its RGB working space
* parameters.
*
* \param gamma Gamma value of the RGB working space.
*
* \param issRGB If true, this space uses the sRGB gamma function. If this
* parameter is false, the space uses a standard raise gamma
* function.
*
* \param x,y Vectors of chromaticity coordinates of the RGB working
* space, relative to the D50 reference white.
*
* \param Y Vector of luminance coefficients of the RGB working space,
* with respect to the D50 reference white.
*
* This constructor creates a new RGB working space data object with a
* reference count value of one.
*/
RGBColorSystem( float gamma, bool issRGB, const FVector& x, const FVector& y, const FVector& Y )
{
m_data = new Data( gamma, issRGB, x, y, Y );
}
/*!
* Constructs a new %RGBColorSystem instance by its RGB working space
* parameters.
*
* \param gamma Gamma value of the RGB working space.
*
* \param issRGB If true, this space uses the sRGB gamma function. If this
* parameter is false, the space uses a standard raise gamma
* function.
*
* \param x,y Arrays of chromaticity coordinates of the RGB working
* space, relative to the D50 reference white.
*
* \param Y Arrays of luminance coefficients of the RGB working space,
* with respect to the D50 reference white.
*
* This constructor creates a new RGB working space data object with a
* reference count value of one.
*/
RGBColorSystem( float gamma, bool issRGB, const float* x, const float* y, const float* Y )
{
m_data = new Data( gamma, issRGB, FVector( x, 3 ), FVector( y, 3 ), FVector( Y, 3 ) );
}
/*!
* Destroys a %RGBColorSystem instance.
*
* Decrements the reference count of the RGB working space data object. If
* the data object becomes \e garbage, i.e. if its reference count becomes
* zero, it is destroyed and its allocated space is disposed.
*/
virtual ~RGBColorSystem()
{
if ( m_data != nullptr )
{
DetachFromData();
m_data = nullptr;
}
}
/*!
* Returns true iff this %RGBColorSystem object uniquely references its RGB
* working space data.
*/
bool IsUnique() const
{
return m_data->IsUnique();
}
/*!
* Returns true iff this %RGBColorSystem object is an alias of another
* %RGBColorSystem instance \a s.
*
* Two instances of %RGBColorSystem are aliases if both share the same RGB
* working space data.
*/
bool IsAliasOf( const RGBColorSystem& s ) const
{
return m_data == s.m_data;
}
/*!
* Ensures that this %RGBColorSystem object uniquely references its RGB
* working space data.
*
* If necessary, this member function generates a duplicate of the RGB
* working space data, references it, and then decrements the reference
* counter of the original data.
*/
void EnsureUnique()
{
if ( !IsUnique() )
{
Data* newData = new Data( *m_data );
DetachFromData();
m_data = newData;
}
}
/*!
* Returns the gamma value of this RGB working space.
*
* \note If this space uses a sRGB gamma function, the returned value is
* 2.2, but the space doesn't use that value as the exponent of a standard
* raise gamma function.
*/
float Gamma() const
{
return m_data->gamma;
}
/*!
* Returns true iff this space uses a sRGB gamma function.
*/
bool IsSRGB() const
{
return m_data->issRGB;
}
/*!
* Returns true iff this space uses a linear gamma function.
*
* A linear RGB space has gamma=1 and doesn't use a sRGB gamma function.
*/
bool IsLinear() const
{
return m_data->isLinear;
}
/*!
* Returns a reference to a vector with the elements of the immutable 3x3
* RGB to CIE XYZ conversion matrix used in this RGB working space.
*
* The RGB->XYZ conversion matrix is a function of the luminance
* coefficients and chromaticity coordinates that define this RGB color
* space.
*
* The nine matrix elements are stored contiguously in row order in the
* returned vector: M[0][0], M[0][1], ..., M[2][2].
*/
const Vector& RGBToXYZMatrix() const
{
return m_data->M;
}
/*!
* Returns a reference to a vector with the elements of the immutable 3x3
* CIE XYZ to RGB inverse conversion matrix used in this RGB working space.
*
* The XYZ->RGB inverse conversion matrix is a function of the luminance
* coefficients and chromaticity coordinates that define this RGB color
* space.
*
* The nine inverse matrix elements are stored contiguously in row order in
* the returned vector: M_[0][0], M_[0][1], ..., M_[2][2].
*/
const Vector& XYZToRGBMatrix() const
{
return m_data->M_;
}
/*!
* Returns a reference to the immutable vector of X chromaticity coordinates
* in this RGB working space.
*
* In PCL, chromaticity coordinates are relative to the D50 reference white.
*/
const FVector& ChromaticityXCoordinates() const
{
return m_data->x;
}
/*!
* Returns a reference to the immutable vector of Y chromaticity coordinates
* in this RGB working space.
*
* In PCL, chromaticity coordinates are relative to the D50 reference white.
*/
const FVector& ChromaticityYCoordinates() const
{
return m_data->y;
}
/*!
* Returns a reference to the immutable vector of luminance coefficients in
* this RGB working space.
*
* In PCL, luminance coefficients are relative to the D50 reference white.
*/
const FVector& LuminanceCoefficients() const
{
return m_data->Y;
}
/*!
* Returns true iff two %RGBColorSystem instances define the same RGB working
* space. This happens when either both instances are aliases, or if they
* define exactly the same RGB space parameters.
*/
friend bool operator ==( const RGBColorSystem& S1, const RGBColorSystem& S2 )
{
return S1.IsAliasOf( S2 ) || *S1.m_data == *S2.m_data;
}
/*!
* Causes this %RGBColorSystem instance to reference the same RGB working
* space as another instance.
*
* The reference count of the previously referenced space is decremented,
* and the previous space is deleted if it becomes unreferenced. The
* reference count of the new space is then incremented.
*/
void Assign( const RGBColorSystem& s )
{
s.m_data->Attach();
DetachFromData();
m_data = s.m_data;
}
/*!
* Assignment iterator. Returns a reference to this object.
*
* This operator calls Assign() with the specified source space \a s.
*/
RGBColorSystem& operator =( const RGBColorSystem& s )
{
Assign( s );
return *this;
}
/*!
* Returns the lightness component in the CIE L*a*b* space corresponding to
* a set of specified RGB components.
*
* \param R,G,B RGB components from which lightness will be calculated.
*
* \note A synonym for this function is CIEL().
*/
sample Lightness( sample R, sample G, sample B ) const
{
XYZLab( R = m_data->CIEY( R, G, B ) );
return sample( (1.16 * R) - 0.16 );
}
// ### Deprecated - retained for compatibility -> suppress in PCL 2.x
sample Luminance( sample R, sample G, sample B ) const
{
return Lightness( R, G, B );
}
/*!
* A synonym for Lightness().
*/
sample CIEL( sample R, sample G, sample B ) const
{
return Lightness( R, G, B );
}
/*!
* Calculates the lightness component in the CIE L*a*b* space corresponding
* to a set of RGB components, and copies it to a specified variable \a K.
*
* \param[out] K Reference to an output variable for the calculated
* lightness component.
*
* \param R,G,B Input RGB components.
*/
void RGBToGray( sample& K, sample R, sample G, sample B ) const
{
K = Lightness( R, G, B );
}
/*!
* Returns the Value channel value in the HSV color ordering system,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* \note This is an utility function provided for convenience; HSV is not a
* color space, but a color ordering system not based on a RGBWS.
*/
sample Value( sample R, sample G, sample B ) const
{
return pcl::Max( pcl::Max( R, G ), B );
}
/*!
* Returns the Intensity channel value in the HSI color ordering system,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* \note HSI is more often known as HSL; however we reserve the L channel
* identifier exclusively for the CIE L* component (lightness) in PCL.
*
* \note This is an utility function provided for convenience; HSI is not a
* color space, but a color ordering system not based on a RGBWS.
*/
sample Intensity( sample R, sample G, sample B ) const
{
return sample( 0.5*( pcl::Min( pcl::Min( R, G ), B )
+ pcl::Max( pcl::Max( R, G ), B ) ) );
}
/*!
* Returns the Hue channel value in the HSV and HSI color ordering systems,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* The returned value is a <em>normalized hue</em>, which is the hue angle
* rescaled to the normalized [0,1[ range. A normalized hue value of 1
* corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* \note This is an utility function, provided for convenience. HSV and HSI
* are not color spaces, but color ordering systems not based on any RGBWS.
*/
sample Hue( sample R, sample G, sample B ) const
{
sample max = pcl::Max( pcl::Max( R, G ), B );
sample delta = max - pcl::Min( pcl::Min( R, G ), B );
if ( delta != 0 )
{
sample H;
if ( R == max )
H = (G - B)/delta; // between yellow & magenta
else if ( G == max )
H = 2 + (B - R)/delta; // between cyan & yellow
else
H = 4 + (R - G)/delta; // between magenta & cyan
H /= 6;
if ( H < 0 )
H += 1;
return H;
}
// Achromatic case: R = G = B
// Hue is undefined (H is set to 0 conventionally)
return 0;
}
/*!
* Returns the Saturation channel value in the HSV color ordering system,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* \note This is an utility function provided for convenience; HSV is not a
* color space, but a color ordering system not based on a RGBWS.
*/
sample HSVSaturation( sample R, sample G, sample B ) const
{
sample max = pcl::Max( pcl::Max( R, G ), B );
sample delta = max - pcl::Min( pcl::Min( R, G ), B );
return sample( (1.0 + max != 1.0) ? delta/max : 0.0 );
}
/*!
* Returns the Saturation channel value in the HSI color ordering system,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* \note HSI is more often known as HSL; however we reserve the L channel
* identifier exclusively for the CIE L* component (lightness) in PCL.
*
* \note This is an utility function provided for convenience; HSI is not a
* color space, but a color ordering system not based on a RGBWS.
*/
sample HSISaturation( sample R, sample G, sample B ) const
{
sample min = pcl::Min( pcl::Min( R, G ), B );
sample max = pcl::Max( pcl::Max( R, G ), B );
sample delta = max - min;
if ( delta != 0 )
{
sample sum = min + max;
return delta/((sum <= 1) ? sum : 2-sum);
}
return sample( 0 );
}
/*!
* Returns the Saturation channel value in the HSV color ordering system,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* This function is a synonym for HSVSaturation().
*/
sample Saturation( sample R, sample G, sample B ) const
{
return HSVSaturation( R, G, B );
}
/*!
* Converts a set of RGB components to the corresponding channel values in
* the HSV (Hue, Saturation, Value) color ordering system.
*
* \param[out] H,S,V References to the variables where output HSV channel
* values will be copied.
*
* \param R,G,B Input RGB components.
*
* The output \a H value is a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* \note This is a static function provided for convenience; HSV is not a
* color space, but a color ordering system not based on a RGBWS.
*/
static void RGBToHSV( sample& H, sample& S, sample& V, sample R, sample G, sample B )
{
V = pcl::Max( pcl::Max( R, G ), B ); // V = Value( R, G, B );
sample delta = V - pcl::Min( pcl::Min( R, G ), B );
if ( delta != 0 )
{
S = delta/V;
if ( R == V )
H = (G - B)/delta; // between yellow & magenta
else if ( G == V )
H = 2 + (B - R)/delta; // between cyan & yellow
else
H = 4 + (R - G)/delta; // between magenta & cyan
H /= 6;
if ( H < 0 )
H += 1;
}
else
{
// Achromatic case: R = G = B
// S = 0, and H is undefined (H is set to 0 conventionally)
S = H = 0;
}
}
/*!
* Converts a set of RGB components to the corresponding channel values in
* the HSI (Hue, Saturation, Intensity) color ordering system.
*
* \param[out] H,S,I References to the variables where output HSI channel
* values will be copied.
*
* \param R,G,B Input RGB components.
*
* The output \a H value is a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* \note HSI is more often known as HSL; however we reserve the L channel
* identifier exclusively for the CIE L* component (lightness) in PCL.
*
* \note This is a static function provided for convenience; HSV is not a
* color space, but a color ordering system not based on a RGBWS.
*/
static void RGBToHSI( sample& H, sample& S, sample& I, sample R, sample G, sample B )
{
sample min = pcl::Min( pcl::Min( R, G ), B );
sample max = pcl::Max( pcl::Max( R, G ), B );
sample delta = max - min;
sample sum = min + max;
I = 0.5*sum;
if ( delta != 0 )
{
S = delta/((sum <= 1) ? sum : 2-sum);
if ( R == max )
H = (G - B)/delta; // between yellow & magenta
else if ( G == max )
H = 2 + (B - R)/delta; // between cyan & yellow
else
H = 4 + (R - G)/delta; // between magenta & cyan
H /= 6;
if ( H < 0 )
H += 1;
}
else
{
// Achromatic case: R = G = B
// S = 0, and H is undefined (H is set to 0 conventionally)
S = H = 0;
}
}
/*!
* Converts a set of RGB components to the corresponding channel values in
* the HSV (Hue, Saturation, Value) color ordering system, plus the
* L* component (lightness) in the CIE L*a*b* space.
*
* \param[out] H,S,V References to the variables where output HSV channel
* values will be copied.
*
* \param[out] L Reference to a variable where the output CIE L*
* component will be copied.
*
* \param R,G,B Input RGB components.
*
* The output \a H value is a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*/
void RGBToHSVL( sample& H, sample& S, sample& V, sample& L, sample R, sample G, sample B ) const
{
RGBToHSV( H, S, V, R, G, B );
L = Lightness( R, G, B );
}
/*!
* Converts a set of RGB components to the corresponding channel values in
* the HSI (Hue, Saturation, Intensity) color ordering system, plus the
* L* component (lightness) in the CIE L*a*b* space.
*
* \param[out] H,S,I References to the variables where output HSI channel
* values will be copied.
*
* \param[out] L Reference to a variable where the output CIE L*
* component will be copied.
*
* \param R,G,B Input RGB components.
*
* The output \a H value is a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* \note HSI is more often known as HSL; however we reserve the L channel
* identifier exclusively for the CIE L* component (lightness) in PCL.
*/
void RGBToHSIL( sample& H, sample& S, sample& I, sample& L, sample R, sample G, sample B ) const
{
RGBToHSI( H, S, I, R, G, B );
L = Lightness( R, G, B );
}
/*!
* Conversion from the RGB color space to the CIE XYZ color space.
*
* \param[out] X,Y,Z References to the variables where output CIE XYZ
* components will be copied.
*
* \param R,G,B Input RGB components.
*/
void RGBToCIEXYZ( sample& X, sample& Y, sample& Z, sample R, sample G, sample B ) const
{
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
}
/*!
* Calculates the chrominance components in the CIE XYZ color space
* corresponding to a specified set of RGB components.
*
* \param[out] X,Z References to the variables where output CIE X and Z
* components will be copied.
*
* \param R,G,B Input RGB components.
*
* This function avoids the calculation of the CIE Y component, which saves
* computing time when only the chrominance components are required.
*/
void RGBToCIEXZ( sample& X, sample& Z, sample R, sample G, sample B ) const
{
m_data->RGBToCIEXZ( X, Z, R, G, B );
}
/*!
* CIE X component from RGB components.
*
* \param R,G,B Input RGB components.
*/
sample CIEX( sample R, sample G, sample B ) const
{
return m_data->CIEX( R, G, B );
}
/*!
* CIE Y component from RGB components.
*
* \param R,G,B Input RGB components.
*/
sample CIEY( sample R, sample G, sample B ) const
{
return m_data->CIEY( R, G, B );
}
/*!
* CIE Z component from RGB components.
*
* \param R,G,B Input RGB components.
*/
sample CIEZ( sample R, sample G, sample B ) const
{
return m_data->CIEZ( R, G, B );
}
/*!
* CIE L* component from CIE Y.
*
* \param Y Source CIE Y component.
*/
sample CIEYToCIEL( sample Y ) const
{
XYZLab( Y ); return 1.16*Y - 0.16;
}
/*!
* CIE Y component from CIE L*.
*
* \param L Source CIE L* component.
*/
sample CIELToCIEY( sample L ) const
{
LabXYZ( L = (L + 0.16)/1.16 ); return L;
}
/*!
* Conversion from the RGB color space to the CIE L*a*b* color space.
*
* \param[out] L,a,b References to the variables where output CIE L*a*b*
* components will be copied.
*
* \param R,G,B Input RGB components.
*/
void RGBToCIELab( sample& L, sample& a, sample& b, sample R, sample G, sample B ) const
{
sample X, Y, Z;
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
CIEXYZToCIELab( L, a, b, X, Y, Z );
}
/*!
* Calculates the chrominance components in the CIE L*a*b* color space
* corresponding to a specified set of RGB components.
*
* \param[out] a,b References to the variables where output CIE a* and b*
* components will be copied.
*
* \param R,G,B Input RGB components.
*
* This function avoids the calculation of the CIE L* component, which saves
* computing time when only the chrominance components are required.
*/
void RGBToCIEab( sample& a, sample& b, sample R, sample G, sample B ) const
{
sample X, Y, Z;
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
XYZLab( X ); XYZLab( Y ); XYZLab( Z );
a = (5*(X - Y) + m_data->abOffset)/m_data->abDelta;
b = (2*(Y - Z) + m_data->abOffset)/m_data->abDelta;
}
/*!
* Returns the a* chrominance component in the CIE L*a*b* color space,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* This function avoids calculation of the L* and b* components, which
* saves computing time when only the a* component is required.
*/
sample CIEa( sample R, sample G, sample B ) const
{
sample X, Y;
m_data->RGBToCIEXY( X, Y, R, G, B );
XYZLab( X ); XYZLab( Y );
return (5*(X - Y) + m_data->abOffset)/m_data->abDelta;
}
/*!
* Returns the b* chrominance component in the CIE L*a*b* color space,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* This function avoids the calculation of the L* and a* channels, which
* saves computing time when only the b* chrominance component is required.
*/
sample CIEb( sample R, sample G, sample B ) const
{
sample Y, Z;
m_data->RGBToCIEYZ( Y, Z, R, G, B );
XYZLab( Y ); XYZLab( Z );
return (2*(Y - Z) + m_data->abOffset)/m_data->abDelta;
}
/*!
* Returns the c* chrominance component in the CIE L*c*h* color space,
* corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* This function avoids the calculation of the L* and h* channels, which
* saves computing time when only the c* chrominance component is required.
*/
sample CIEc( sample R, sample G, sample B ) const
{
sample X, Y, Z;
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
XYZLab( X ); XYZLab( Y ); XYZLab( Z );
sample a = 5*(X - Y);
sample b = 2*(Y - Z);
return pcl::Sqrt( a*a + b*b )/m_data->cDelta;
}
/*!
* Returns the normalized h* chrominance component in the CIE L*c*h* color
* space, corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* This function avoids the calculation of the L* and c* channels, which
* saves computing time when only the h* chrominance component is required.
*
* The returned value is the hue angle normalized to the range [0,1[, where
* 0 corresponds to a hue angle of zero degrees, and 1 to an angle of 360
* degrees (2*pi radians).
*/
sample CIEh( sample R, sample G, sample B ) const
{
return CIEhr( R, G, B )/Const<sample>::_2pi();
}
/*!
* Returns the h* chrominance component in the CIE L*c*h* color space,
* expressed in radians, corresponding to a specified set of RGB components.
*
* \param R,G,B Input RGB components.
*
* This function avoids the calculation of the L* and c* channels, which
* saves computing time when only the h* chrominance component is required.
*
* The returned value is the hue angle in radians, in the range [0,2pi[.
*/
sample CIEhr( sample R, sample G, sample B ) const
{
sample X, Y, Z;
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
XYZLab( X ); XYZLab( Y ); XYZLab( Z );
sample h = ArcTan( 2*(Y - Z), 5*(X - Y) );
if ( h < 0 )
h += Const<sample>::_2pi();
return h;
}
/*!
* Conversion from the RGB color space to the CIE L*c*h* color space.
*
* \param[out] L,c,h References to the variables where output CIE L*c*h*
* components will be copied.
*
* \param R,G,B Input RGB components.
*/
void RGBToCIELch( sample& L, sample& c, sample& h, sample R, sample G, sample B ) const
{
sample X, Y, Z;
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
XYZLab( X ); XYZLab( Y ); XYZLab( Z );
L = sample( (1.16 * Y) - 0.16 );
sample a = 5*(X - Y);
sample b = 2*(Y - Z);
c = pcl::Sqrt( a*a + b*b )/m_data->cDelta;
h = ArcTan( b, a );
if ( h < 0 )
h += Const<sample>::_2pi();
h /= Const<sample>::_2pi();
}
/*!
* Conversion from the RGB color space to the CIE L*a*b* color space, with
* optimized, on-the-fly calculation of the CIE c* component.
*
* \param[out] L,a,b References to the variables where output CIE L*a*b*
* components will be copied.
*
* \param[out] c Reference to a variable where the output CIE c*
* component will be copied.
*
* \param R,G,B Input RGB components.
*
* If only the CIE c* component is required besides CIE L*a*b* components,
* this function is much faster than performing two separate conversions to
* the CIE L*a*b* and L*c*h* color spaces.
*/
void RGBToCIELabc( sample& L, sample& a, sample& b, sample& c, sample R, sample G, sample B ) const
{
sample X, Y, Z;
m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
XYZLab( X ); XYZLab( Y ); XYZLab( Z );
L = sample( (1.16 * Y) - 0.16 );
a = ((X = 5*(X - Y)) + m_data->abOffset)/m_data->abDelta;
b = ((Z = 2*(Y - Z)) + m_data->abOffset)/m_data->abDelta;
c = pcl::Sqrt( X*X + Z*Z )/m_data->cDelta;
}
/*!
* Conversion from the HSV color ordering system to the RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param H,S,V Input HSV channel values.
*
* The input \a H value must be a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* \note This is a static function provided for convenience; HSV is not a
* color space, but a color ordering system not based on a RGBWS.
*/
static void HSVToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample V )
{
if ( S != 0 )
{
H *= 6; // degrees -> quadrant index
int i = TruncInt( Floor( H ) ); // i = sector 0 to 5
sample f = H - i; // f = fractional part of H
sample p = V*(1 - S);
sample q = V*(1 - S*f);
sample t = V*(1 - S*(1 - f));
switch( i )
{
case 0: R = V; G = t; B = p; break;
case 1: R = q; G = V; B = p; break;
case 2: R = p; G = V; B = t; break;
case 3: R = p; G = q; B = V; break;
case 4: R = t; G = p; B = V; break;
case 5: R = V; G = p; B = q; break;
default: R = G = B = V; break; // out-of-range H argument
}
}
else
R = G = B = V; // achromatic
}
/*!
* Transformation from separate HSV chrominance and CIE L* components to the
* RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param H,S,V Input HSV channel values for chrominance.
*
* \param L Input CIE L* (lightness) component.
*
* The lightness implicitly defined by the input HSV values is discarded
* and replaced with the specified CIE L* component.
*
* Strictly speaking, the resulting chrominance is not colorimetrically
* defined, since it derives from HSV channel values. However, the
* chrominance is supposed to be expressed in the RGB working space
* referenced by this %RGBColorSystem instance.
*
* The input \a H value must be a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* The primary usefulness of this function is implementing accurate
* hue/saturation image transformations in the HSV system with full
* preservation of the CIE lightness component.
*/
void HSVLToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample V, sample L ) const
{
HSVToRGB( R, G, B, H, S, V );
#define a H
#define b S
RGBToCIEab( a, b, R, G, B );
CIELabToRGB( R, G, B, L, a, b );
#undef a
#undef b
}
/*!
* Conversion from the HSI color ordering system to the RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param H,S,I Input HSI channel values.
*
* The input \a H value must be a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* \note HSI is more often known as HSL; however we reserve the L channel
* identifier exclusively for the CIE L* component (lightness) in PCL.
*
* \note This is a static function provided for convenience; HSI is not a
* color space, but a color ordering system not based on a RGBWS.
*/
static void HSIToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample I )
{
if ( S != 0 )
{
sample v2 = (I < 0.5) ? I*(1 + S) : I + S - S*I;
sample v1 = I+I - v2;
R = HSIH2RGB( v1, v2, H+_1_3 );
G = HSIH2RGB( v1, v2, H );
B = HSIH2RGB( v1, v2, H-_1_3 );
}
else
R = G = B = I; // achromatic
}
/*!
* Transformation from separate HSI chrominance and CIE L* components to the
* RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param H,S,I Input HSI channel values for chrominance.
*
* \param L Input CIE L* component (lightness).
*
* The lightness implicitly defined by the input HSI values is discarded
* and replaced with the specified CIE L* component.
*
* Strictly speaking, the resulting chrominance is not colorimetrically
* defined, since it derives from HSI channel values. However, the
* chrominance is supposed to be expressed in the RGB working space
* referenced by this %RGBColorSystem instance.
*
* The input \a H value must be a <em>normalized hue</em>, which is the hue
* angle rescaled to the normalized [0,1] range. A normalized hue value of
* one corresponds to a hue angle of 2*pi radians, or 360 degrees.
*
* The primary usefulness of this function is implementing accurate
* hue/saturation image transformations in the HSI system with full
* preservation of the CIE lightness component.
*
* \note HSI is more often known as HSL; however we reserve the L channel
* identifier exclusively for the CIE L* component (lightness) in PCL.
*/
void HSILToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample I, sample L ) const
{
HSIToRGB( R, G, B, H, S, I );
#define a H
#define b S
RGBToCIEab( a, b, R, G, B );
CIELabToRGB( R, G, B, L, a, b );
#undef a
#undef b
}
/*!
* Conversion from the CIE XYZ color space to the RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param X,Y,Z Input CIE XYZ components.
*/
void CIEXYZToRGB( sample& R, sample& G, sample& B, sample X, sample Y, sample Z ) const
{
m_data->CIEXYZToRGB( R, G, B, X, Y, Z );
}
/*!
* Conversion from the CIE XYZ color space to the CIE L*a*b* color space.
*
* \param[out] L,a,b References to the variables where output CIE L*a*b*
* components will be copied.
*
* \param X,Y,Z Input CIE XYZ components.
*/
void CIEXYZToCIELab( sample& L, sample& a, sample& b, sample X, sample Y, sample Z ) const
{
XYZLab( X ); XYZLab( Y ); XYZLab( Z );
L = sample( (1.16 * Y) - 0.16 );
a = (5*(X - Y) + m_data->abOffset)/m_data->abDelta;
b = (2*(Y - Z) + m_data->abOffset)/m_data->abDelta;
}
/*!
* Conversion from the CIE L*a*b* color space to the RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param L,a,b Input CIE L*a*b* components.
*/
void CIELabToRGB( sample& R, sample& G, sample& B, sample L, sample a, sample b ) const
{
sample X, Y, Z;
CIELabToCIEXYZ( X, Y, Z, L, a, b );
m_data->CIEXYZToRGB( R, G, B, X, Y, Z );
}
/*!
* Conversion from the CIE L*a*b* color space to the CIE XYZ color space.
*
* \param[out] X,Y,Z References to the variables where output CIE XYZ
* components will be copied.
*
* \param L,a,b Input CIE L*a*b* components.
*/
void CIELabToCIEXYZ( sample& X, sample& Y, sample& Z, sample L, sample a, sample b ) const
{
Y = sample( (L + 0.16)/1.16 );
X = (m_data->abDelta*a - m_data->abOffset)/5 + Y;
Z = Y - (m_data->abDelta*b - m_data->abOffset)/2;
LabXYZ( X ); LabXYZ( Y ); LabXYZ( Z );
}
/*!
* Conversion from the CIE L*a*b* color space to the CIE L*c*h* color space.
*
* \param[out] L,c,h References to the variables where output CIE L*c*h*
* components will be copied.
*
* \param L0,a,b Input CIE L*a*b* components.
*
* \note The output L component is always identical to the input L0
* component; the L parameter of this function has been included for the
* sake of coherence in function signatures.
*/
void CIELabToCIELch( sample& L, sample& c, sample& h, sample L0, sample a, sample b ) const
{
L = L0;
a = m_data->abDelta*a - m_data->abOffset;
b = m_data->abDelta*b - m_data->abOffset;
c = Sqrt( a*a + b*b )/m_data->cDelta;
h = ArcTan( b, a )/Const<sample>::pi();
if ( h < 0 )
h += 1;
}
/*!
* Conversion from the CIE L*c*h* color space to the RGB color space.
*
* \param[out] R,G,B References to the variables where output RGB
* components will be copied.
*
* \param L,c,h Input CIE L*c*h* components.
*/
void CIELchToRGB( sample& R, sample& G, sample& B, sample L, sample c, sample h ) const
{
sample a, b;
SinCos( h*Const<sample>::_2pi(), b, a );
c *= m_data->cDelta;
a *= c;
b *= c;
sample Y = sample( (L + 0.16)/1.16 );
sample X = a/5 + Y;
sample Z = Y - b/2;
LabXYZ( X ); LabXYZ( Y ); LabXYZ( Z );
m_data->CIEXYZToRGB( R, G, B, X, Y, Z );
}
/*!
* Conversion from the CIE L*c*h* color space to the CIE L*a*b* color space.
*
* \param[out] L,a,b References to the variables where output CIE L*a*b*
* components will be copied.
*
* \param L0,c,h Input CIE L*c*h* components.
*
* \note The output L component is always identical to the input L0
* component; the L parameter of this function has been included for the
* sake of coherence in function signatures.
*/
void CIELchToCIELab( sample& L, sample& a, sample& b, sample L0, sample c, sample h ) const
{
L = L0;
SinCos( h*Const<sample>::_2pi(), b, a );
c *= m_data->cDelta;
a = (c*a + m_data->abOffset)/m_data->abDelta;
b = (c*b + m_data->abOffset)/m_data->abDelta;
}
protected:
struct Data : public ReferenceCounter
{
float gamma, gammaInv; // gamma, 1/gamma
bool issRGB; // true if sRGB gamma function is being used
bool isLinear; // true if gamma=1.0 and is not sRGB, for optimization
/*
* Chromaticity coordinates w.r.t. the D50 reference white
*/
FVector x, y;
/*
* Luminance coefficients w.r.t. the D50 reference white
*/
FVector Y;
/*
* RGB <-> CIE XYZ transformation matrices
*/
Vector M; // RGB -> CIE XYZ
Vector M_; // CIE XYZ -> RGB
/*
* Normalization coefficients for X and Z coordinates
*/
sample mX, mZ;
/*
* Normalization of CIE a*, b* and c* components
*/
sample abOffset;
sample abDelta;
sample cDelta;
Data( float, bool, const FVector&, const FVector&, const FVector& );
Data( float, bool, const float*, const float*, const float* );
Data( const Data& ) = default;
void Initialize();
bool operator ==( const RGBColorSystem::Data& data ) const
{
return gamma == data.gamma &&
issRGB == data.issRGB &&
Y == data.Y &&
x == data.x && y == data.y;
}
/*
* Primary gamma functions
*/
void LinearRGB( sample& x ) const
{
x = issRGB ?
((x > sRGBEpsilon) ?
Pow( sample( (x + 0.055)/1.055 ), sample( sRGBGamma ) ) : sample( x/12.92 )) :
Pow( x, sample( gamma ) );
}
void GammaRGB( sample& x ) const
{
x = issRGB ?
((x > sRGBEpsilonInv) ?
sample( 1.055*Pow( x, sample( sRGBGammaInv ) ) - 0.055 ) : sample( 12.92*x )) :
Pow( x, sample( gammaInv ) );
}
/*
* Primary linear transformations
*/
void RGBToCIEXYZ( sample& X, sample& Y, sample& Z, sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
X = Range( (R*M[0] + G*M[1] + B*M[2])/mX );
Y = Range( R*M[3] + G*M[4] + B*M[5] );
Z = Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
}
void CIEXYZToRGB( sample& R, sample& G, sample& B, sample X, sample Y, sample Z ) const
{
X *= mX;
Z *= mZ;
R = Range( X*M_[0] + Y*M_[1] + Z*M_[2] );
G = Range( X*M_[3] + Y*M_[4] + Z*M_[5] );
B = Range( X*M_[6] + Y*M_[7] + Z*M_[8] );
if ( !isLinear )
{
GammaRGB( R );
GammaRGB( G );
GammaRGB( B );
}
}
/*
* Utility functions for fast partial luminance/chrominance calculations
*/
sample CIEX( sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
return Range( (R*M[0] + G*M[1] + B*M[2])/mX );
}
sample CIEY( sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
return Range( R*M[3] + G*M[4] + B*M[5] );
}
sample CIEZ( sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
return Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
}
void RGBToCIEXY( sample& X, sample& Y, sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
X = Range( (R*M[0] + G*M[1] + B*M[2])/mX );
Y = Range( R*M[3] + G*M[4] + B*M[5] );
}
void RGBToCIEYZ( sample& Y, sample& Z, sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
Y = Range( R*M[3] + G*M[4] + B*M[5] );
Z = Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
}
void RGBToCIEXZ( sample& X, sample& Z, sample R, sample G, sample B ) const
{
if ( !isLinear )
{
LinearRGB( R );
LinearRGB( G );
LinearRGB( B );
}
X = Range( (R*M[0] + G*M[1] + B*M[2])/mX );
Z = Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
}
static bool ValidateParameters( const FVector& x, const FVector& y, const FVector& Y );
static sample Range( const sample& x ) // Ensure nominal [0,1] range
{
return pcl::Range( x, sample( 0 ), sample( 1 ) );
}
};
Data* m_data = nullptr;
void DetachFromData()
{
if ( !m_data->Detach() )
delete m_data;
}
static void XYZLab( sample& x )
{
x = (x > CIEEpsilon) ? Pow( x, sample( _1_3 ) ) : sample( CIEKappa116*x + _16_116 );
}
static void LabXYZ( sample& x )
{
sample x3 = x*x*x;
x = (x3 > CIEEpsilon) ? x3 : sample( (x - _16_116)/CIEKappa116 );
}
static sample HSIH2RGB( sample v1, sample v2, sample H )
{
if ( H < 0 )
H += 1;
else if ( H > 1 )
H -= 1;
if ( H < _1_6 )
return v1 + 6*H*(v2 - v1);
if ( H < 0.5 )
return v2;
if ( H < _2_3 )
return v1 + 6*(_2_3 - H)*(v2 - v1);
return v1;
}
friend struct Data;
public:
/*!
* sRGB x chromaticity coordinates (D50).
*/
static const float sRGB_x[ 3 ];
/*!
* sRGB y chromaticity coordinates (D50).
*/
static const float sRGB_y[ 3 ];
/*!
* sRGB luminance coefficients (D50).
*/
static const float sRGB_Y[ 3 ];
/*!
* The sRGB (D50) working space.
*/
static const RGBColorSystem sRGB;
#ifdef __PCL_WITH_STANDARD_RGB_WORKING_SPACES
/*
* Adobe RGB 1998
*/
static const float AdobeRGB1998_x[ 3 ];
static const float AdobeRGB1998_y[ 3 ];
static const float AdobeRGB1998_Y[ 3 ];
/*
* Apple RGB
*/
static const float AppleRGB_x[ 3 ];
static const float AppleRGB_y[ 3 ];
static const float AppleRGB_Y[ 3 ];
/*
* Best RGB
*/
static const float BestRGB_x[ 3 ];
static const float BestRGB_y[ 3 ];
static const float BestRGB_Y[ 3 ];
/*
* Beta RGB (by Bruce Lindbloom)
*/
static const float BetaRGB_x[ 3 ];
static const float BetaRGB_y[ 3 ];
static const float BetaRGB_Y[ 3 ];
/*
* Bruce RGB
*/
static const float BruceRGB_x[ 3 ];
static const float BruceRGB_y[ 3 ];
static const float BruceRGB_Y[ 3 ];
/*
* CIE RGB
*/
static const float CIERGB_x[ 3 ];
static const float CIERGB_y[ 3 ];
static const float CIERGB_Y[ 3 ];
/*
* Color Match RGB
*/
static const float ColorMatchRGB_x[ 3 ];
static const float ColorMatchRGB_y[ 3 ];
static const float ColorMatchRGB_Y[ 3 ];
/*
* NTSC RGB
*/
static const float NTSCRGB_x[ 3 ];
static const float NTSCRGB_y[ 3 ];
static const float NTSCRGB_Y[ 3 ];
/*
* PAL/SECAM RGB
*/
static const float PALSECAMRGB_x[ 3 ];
static const float PALSECAMRGB_y[ 3 ];
static const float PALSECAMRGB_Y[ 3 ];
/*
* ProPhoto RGB
*/
static const float ProPhotoRGB_x[ 3 ];
static const float ProPhotoRGB_y[ 3 ];
static const float ProPhotoRGB_Y[ 3 ];
/*
* SMPTE-C RGB
*/
static const float SMPTECRGB_x[ 3 ];
static const float SMPTECRGB_y[ 3 ];
static const float SMPTECRGB_Y[ 3 ];
/*
* Wide Gamut RGB
*/
static const float WideGamutRGB_x[ 3 ];
static const float WideGamutRGB_y[ 3 ];
static const float WideGamutRGB_Y[ 3 ];
#endif // __PCL_WITH_STANDARD_RGB_WORKING_SPACES
};
// ----------------------------------------------------------------------------
} // pcl
#undef _1_3
#undef _1_6
#undef _2_3
#undef _16_116
#undef CIEEpsilon
#undef CIEKappa
#undef CIEKappa116
#undef sRGBEpsilon
#undef sRGBEpsilonInv
#undef sRGBGamma
#undef sRGBGammaInv
#endif // __PCL_RGBColorSystem_h
// ----------------------------------------------------------------------------
// EOF pcl/RGBColorSystem.h - Released 2022-03-12T18:59:29Z