PCL
RGBColorSystem.h
Go to the documentation of this file.
1 // ____ ______ __
2 // / __ \ / ____// /
3 // / /_/ // / / /
4 // / ____// /___ / /___ PixInsight Class Library
5 // /_/ \____//_____/ PCL 2.7.0
6 // ----------------------------------------------------------------------------
7 // pcl/RGBColorSystem.h - Released 2024-06-18T15:48:54Z
8 // ----------------------------------------------------------------------------
9 // This file is part of the PixInsight Class Library (PCL).
10 // PCL is a multiplatform C++ framework for development of PixInsight modules.
11 //
12 // Copyright (c) 2003-2024 Pleiades Astrophoto S.L. All Rights Reserved.
13 //
14 // Redistribution and use in both source and binary forms, with or without
15 // modification, is permitted provided that the following conditions are met:
16 //
17 // 1. All redistributions of source code must retain the above copyright
18 // notice, this list of conditions and the following disclaimer.
19 //
20 // 2. All redistributions in binary form must reproduce the above copyright
21 // notice, this list of conditions and the following disclaimer in the
22 // documentation and/or other materials provided with the distribution.
23 //
24 // 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
25 // of their contributors, may be used to endorse or promote products derived
26 // from this software without specific prior written permission. For written
27 // permission, please contact info@pixinsight.com.
28 //
29 // 4. All products derived from this software, in any form whatsoever, must
30 // reproduce the following acknowledgment in the end-user documentation
31 // and/or other materials provided with the product:
32 //
33 // "This product is based on software from the PixInsight project, developed
34 // by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
35 //
36 // Alternatively, if that is where third-party acknowledgments normally
37 // appear, this acknowledgment must be reproduced in the product itself.
38 //
39 // THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
40 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
43 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
44 // EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
45 // INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
46 // DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49 // POSSIBILITY OF SUCH DAMAGE.
50 // ----------------------------------------------------------------------------
51 
52 #ifndef __PCL_RGBColorSystem_h
53 #define __PCL_RGBColorSystem_h
54 
56 
57 #include <pcl/Defs.h>
58 
59 #include <pcl/Math.h>
60 #include <pcl/ReferenceCounter.h>
61 #include <pcl/Vector.h>
62 
63 #define _1_3 3.333333333333333e-01 // 1/3
64 #define _1_6 1.666666666666667e-01 // 1/6
65 #define _2_3 6.666666666666667e-01 // 2/3
66 #define _16_116 1.379310344827586e-01 // 16/116
67 #define CIEEpsilon 8.856451679035631e-03 // 216/24389
68 #define CIEKappa 9.032962962962963e+02 // 24389/27
69 #define CIEKappa116 7.787037037037037e+00 // CIEKappa/116
70 #define CIED501931XR 0.96422 // D50 (1931) mX reference white (ASTM E308-01)
71 #define CIED501931ZR 0.82521 // D50 (1931) mZ reference white (ASTM E308-01)
72 #define CIED501964XR 0.96720 // D50 (1964) mX reference white (ASTM E308-01)
73 #define CIED501964ZR 0.81427 // D50 (1964) mZ reference white (ASTM E308-01)
74 
75 namespace pcl
76 {
77 
78 // ----------------------------------------------------------------------------
79 
101 class PCL_CLASS RGBColorSystem
102 {
103 public:
104 
109  using sample = double;
110 
117  : m_data( RGBColorSystem::sRGB.m_data )
118  {
119  if ( m_data != nullptr )
120  m_data->Attach();
121  else // should not happen!
122  m_data = new Data( 2.2F/*gamma*/, true/*issRGB*/, sRGB_x, sRGB_y, sRGB_Y );
123  }
124 
129  : m_data( s.m_data )
130  {
131  m_data->Attach();
132  }
133 
154  RGBColorSystem( float gamma, bool issRGB, const FVector& x, const FVector& y, const FVector& Y = FVector() )
155  {
156  m_data = new Data( gamma, issRGB, x, y, Y );
157  }
158 
179  RGBColorSystem( float gamma, bool issRGB, const float* x, const float* y, const float* Y = nullptr )
180  {
181  m_data = new Data( gamma, issRGB, FVector( x, 3 ), FVector( y, 3 ), (Y != nullptr) ? FVector( Y, 3 ) : FVector() );
182  }
183 
187  virtual ~RGBColorSystem()
188  {
189  if ( m_data != nullptr )
190  {
191  DetachFromData();
192  m_data = nullptr;
193  }
194  }
195 
200  bool IsUnique() const
201  {
202  return m_data->IsUnique();
203  }
204 
212  bool IsAliasOf( const RGBColorSystem& s ) const
213  {
214  return m_data == s.m_data;
215  }
216 
226  {
227  if ( !IsUnique() )
228  {
229  Data* newData = new Data( *m_data );
230  DetachFromData();
231  m_data = newData;
232  }
233  }
234 
243  float Gamma() const
244  {
245  return m_data->gamma;
246  }
247 
251  bool IsSRGB() const
252  {
253  return m_data->issRGB;
254  }
255 
261  bool IsLinear() const
262  {
263  return m_data->isLinear;
264  }
265 
277  const Vector& RGBToXYZMatrix() const
278  {
279  return m_data->M;
280  }
281 
293  const Vector& XYZToRGBMatrix() const
294  {
295  return m_data->M_;
296  }
297 
308  double CIEXNormalizationFactor() const
309  {
310  return m_data->mX;
311  }
312 
323  double CIEZNormalizationFactor() const
324  {
325  return m_data->mZ;
326  }
327 
332  double CIEaNormalizationOffset() const
333  {
334  return m_data->zA;
335  }
336 
341  double CIEaNormalizationFactor() const
342  {
343  return m_data->mA;
344  }
345 
350  double CIEbNormalizationOffset() const
351  {
352  return m_data->zB;
353  }
354 
359  double CIEbNormalizationFactor() const
360  {
361  return m_data->mB;
362  }
363 
368  double CIEcNormalizationFactor() const
369  {
370  return m_data->mC;
371  }
372 
381  {
382  return m_data->x;
383  }
384 
393  {
394  return m_data->y;
395  }
396 
405  {
406  return m_data->Y;
407  }
408 
414  friend bool operator ==( const RGBColorSystem& S1, const RGBColorSystem& S2 )
415  {
416  return S1.IsAliasOf( S2 ) || *S1.m_data == *S2.m_data;
417  }
418 
427  void Assign( const RGBColorSystem& rgbws )
428  {
429  rgbws.m_data->Attach();
430  DetachFromData();
431  m_data = rgbws.m_data;
432  }
433 
439  RGBColorSystem& operator =( const RGBColorSystem& rgbws )
440  {
441  Assign( rgbws );
442  return *this;
443  }
444 
454  {
455  XYZLab( R = m_data->CIEY( R, G, B ) );
456  return sample( (1.16 * R) - 0.16 );
457  }
458 
459  // ### Deprecated - retained for compatibility -> suppress in PCL 2.x
460  sample Luminance( sample R, sample G, sample B ) const
461  {
462  return Lightness( R, G, B );
463  }
464 
468  sample CIEL( sample R, sample G, sample B ) const
469  {
470  return Lightness( R, G, B );
471  }
472 
484  void RGBToGray( sample& K, sample R, sample G, sample B ) const
485  {
486  K = Lightness( R, G, B );
487  }
488 
498  sample Value( sample R, sample G, sample B ) const
499  {
500  return pcl::Max( pcl::Max( R, G ), B );
501  }
502 
516  {
517  return sample( 0.5*( pcl::Min( pcl::Min( R, G ), B )
518  + pcl::Max( pcl::Max( R, G ), B ) ) );
519  }
520 
534  sample Hue( sample R, sample G, sample B ) const
535  {
536  sample max = pcl::Max( pcl::Max( R, G ), B );
537  sample delta = max - pcl::Min( pcl::Min( R, G ), B );
538  if ( delta != 0 )
539  {
540  sample H;
541  if ( R == max )
542  H = (G - B)/delta; // between yellow & magenta
543  else if ( G == max )
544  H = 2 + (B - R)/delta; // between cyan & yellow
545  else
546  H = 4 + (R - G)/delta; // between magenta & cyan
547 
548  H /= 6;
549  if ( H < 0 )
550  H += 1;
551  return H;
552  }
553 
554  // Achromatic case: R = G = B
555  // Hue is undefined (H is set to 0 conventionally)
556  return 0;
557  }
558 
569  {
570  sample max = pcl::Max( pcl::Max( R, G ), B );
571  sample delta = max - pcl::Min( pcl::Min( R, G ), B );
572  return sample( (1.0 + max != 1.0) ? delta/max : 0.0 );
573  }
574 
588  {
589  sample min = pcl::Min( pcl::Min( R, G ), B );
590  sample max = pcl::Max( pcl::Max( R, G ), B );
591  sample delta = max - min;
592  if ( delta != 0 )
593  {
594  sample sum = min + max;
595  return delta/((sum <= 1) ? sum : 2-sum);
596  }
597  return sample( 0 );
598  }
599 
609  {
610  return HSVSaturation( R, G, B );
611  }
612 
629  static void RGBToHSV( sample& H, sample& S, sample& V, sample R, sample G, sample B )
630  {
631  V = pcl::Max( pcl::Max( R, G ), B ); // V = Value( R, G, B );
632 
633  sample delta = V - pcl::Min( pcl::Min( R, G ), B );
634  if ( delta != 0 )
635  {
636  S = delta/V;
637 
638  if ( R == V )
639  H = (G - B)/delta; // between yellow & magenta
640  else if ( G == V )
641  H = 2 + (B - R)/delta; // between cyan & yellow
642  else
643  H = 4 + (R - G)/delta; // between magenta & cyan
644 
645  H /= 6;
646  if ( H < 0 )
647  H += 1;
648  }
649  else
650  {
651  // Achromatic case: R = G = B
652  // S = 0, and H is undefined (H is set to 0 conventionally)
653  S = H = 0;
654  }
655  }
656 
676  static void RGBToHSI( sample& H, sample& S, sample& I, sample R, sample G, sample B )
677  {
678  sample min = pcl::Min( pcl::Min( R, G ), B );
679  sample max = pcl::Max( pcl::Max( R, G ), B );
680  sample delta = max - min;
681  sample sum = min + max;
682 
683  I = 0.5*sum;
684 
685  if ( delta != 0 )
686  {
687  S = delta/((sum <= 1) ? sum : 2-sum);
688 
689  if ( R == max )
690  H = (G - B)/delta; // between yellow & magenta
691  else if ( G == max )
692  H = 2 + (B - R)/delta; // between cyan & yellow
693  else
694  H = 4 + (R - G)/delta; // between magenta & cyan
695 
696  H /= 6;
697  if ( H < 0 )
698  H += 1;
699  }
700  else
701  {
702  // Achromatic case: R = G = B
703  // S = 0, and H is undefined (H is set to 0 conventionally)
704  S = H = 0;
705  }
706  }
707 
725  void RGBToHSVL( sample& H, sample& S, sample& V, sample& L, sample R, sample G, sample B ) const
726  {
727  RGBToHSV( H, S, V, R, G, B );
728  L = Lightness( R, G, B );
729  }
730 
751  void RGBToHSIL( sample& H, sample& S, sample& I, sample& L, sample R, sample G, sample B ) const
752  {
753  RGBToHSI( H, S, I, R, G, B );
754  L = Lightness( R, G, B );
755  }
756 
765  void RGBToCIEXYZ( sample& X, sample& Y, sample& Z, sample R, sample G, sample B ) const
766  {
767  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
768  }
769 
782  void RGBToCIEXZ( sample& X, sample& Z, sample R, sample G, sample B ) const
783  {
784  m_data->RGBToCIEXZ( X, Z, R, G, B );
785  }
786 
792  sample CIEX( sample R, sample G, sample B ) const
793  {
794  return m_data->CIEX( R, G, B );
795  }
796 
802  sample CIEY( sample R, sample G, sample B ) const
803  {
804  return m_data->CIEY( R, G, B );
805  }
806 
812  sample CIEZ( sample R, sample G, sample B ) const
813  {
814  return m_data->CIEZ( R, G, B );
815  }
816 
823  {
824  XYZLab( Y );
825  return 1.16*Y - 0.16;
826  }
827 
834  {
835  L = (L + 0.16)/1.16;
836  LabXYZ( L );
837  return L;
838  }
839 
848  void RGBToCIELab( sample& L, sample& a, sample& b, sample R, sample G, sample B ) const
849  {
850  sample X, Y, Z;
851  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
852  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
853  L = m_data->Range( sample( (1.16 * Y) - 0.16 ) );
854  a = m_data->Range( (5*(X - Y) + m_data->zA)/m_data->mA );
855  b = m_data->Range( (2*(Y - Z) + m_data->zB)/m_data->mB );
856  }
857 
867  void RGBToCIEab( sample& a, sample& b, sample R, sample G, sample B ) const
868  {
869  sample X, Y, Z;
870  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
871  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
872  a = m_data->Range( (5*(X - Y) + m_data->zA)/m_data->mA );
873  b = m_data->Range( (2*(Y - Z) + m_data->zB)/m_data->mB );
874  }
875 
882  sample CIEa( sample R, sample G, sample B ) const
883  {
884  sample X, Y;
885  m_data->RGBToCIEXY( X, Y, R, G, B );
886  XYZLab( X ); XYZLab( Y );
887  return m_data->Range( (5*(X - Y) + m_data->zA)/m_data->mA );
888  }
889 
899  sample CIEb( sample R, sample G, sample B ) const
900  {
901  sample Y, Z;
902  m_data->RGBToCIEYZ( Y, Z, R, G, B );
903  XYZLab( Y ); XYZLab( Z );
904  return m_data->Range( (2*(Y - Z) + m_data->zB)/m_data->mB );
905  }
906 
916  sample CIEc( sample R, sample G, sample B ) const
917  {
918  sample X, Y, Z;
919  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
920  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
921  sample a = 5*(X - Y);
922  sample b = 2*(Y - Z);
923  return m_data->Range( pcl::Sqrt( a*a + b*b )/m_data->mC );
924  }
925 
939  sample CIEh( sample R, sample G, sample B ) const
940  {
941  return CIEhr( R, G, B )/Const<sample>::_2pi();
942  }
943 
956  sample CIEhr( sample R, sample G, sample B ) const
957  {
958  sample X, Y, Z;
959  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
960  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
961  sample h = ArcTan( 2*(Y - Z), 5*(X - Y) );
962  if ( h < 0 )
963  h += Const<sample>::_2pi();
964  return h;
965  }
966 
975  void RGBToCIELch( sample& L, sample& c, sample& h, sample R, sample G, sample B ) const
976  {
977  sample X, Y, Z;
978  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
979  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
980  L = m_data->Range( sample( (1.16 * Y) - 0.16 ) );
981  sample a = 5*(X - Y);
982  sample b = 2*(Y - Z);
983  c = m_data->Range( pcl::Sqrt( a*a + b*b )/m_data->mC );
984  h = ArcTan( b, a );
985  if ( h < 0 )
986  h += Const<sample>::_2pi();
987  h /= Const<sample>::_2pi();
988  }
989 
1006  void RGBToCIELabc( sample& L, sample& a, sample& b, sample& c, sample R, sample G, sample B ) const
1007  {
1008  sample X, Y, Z;
1009  m_data->RGBToCIEXYZ( X, Y, Z, R, G, B );
1010  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
1011  L = m_data->Range( sample( (1.16 * Y) - 0.16 ) );
1012  a = m_data->Range( ((X = 5*(X - Y)) + m_data->zA)/m_data->mA );
1013  b = m_data->Range( ((Z = 2*(Y - Z)) + m_data->zB)/m_data->mB );
1014  c = m_data->Range( pcl::Sqrt( X*X + Z*Z )/m_data->mC );
1015  }
1016 
1032  static void HSVToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample V )
1033  {
1034  if ( S != 0 )
1035  {
1036  H *= 6; // degrees -> quadrant index
1037 
1038  int i = TruncInt( Floor( H ) ); // i = sector 0 to 5
1039  sample f = H - i; // f = fractional part of H
1040  sample p = V*(1 - S);
1041  sample q = V*(1 - S*f);
1042  sample t = V*(1 - S*(1 - f));
1043 
1044  switch( i )
1045  {
1046  case 0: R = V; G = t; B = p; break;
1047  case 1: R = q; G = V; B = p; break;
1048  case 2: R = p; G = V; B = t; break;
1049  case 3: R = p; G = q; B = V; break;
1050  case 4: R = t; G = p; B = V; break;
1051  case 5: R = V; G = p; B = q; break;
1052  default: R = G = B = V; break; // out-of-range H argument
1053  }
1054  }
1055  else
1056  R = G = B = V; // achromatic
1057  }
1058 
1087  void HSVLToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample V, sample L ) const
1088  {
1089  HSVToRGB( R, G, B, H, S, V );
1090 #define a H
1091 #define b S
1092  RGBToCIEab( a, b, R, G, B );
1093  CIELabToRGB( R, G, B, L, a, b );
1094 #undef a
1095 #undef b
1096  }
1097 
1116  static void HSIToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample I )
1117  {
1118  if ( S != 0 )
1119  {
1120  sample v2 = (I < 0.5) ? I*(1 + S) : I + S - S*I;
1121  sample v1 = I+I - v2;
1122  R = HSIH2RGB( v1, v2, H+_1_3 );
1123  G = HSIH2RGB( v1, v2, H );
1124  B = HSIH2RGB( v1, v2, H-_1_3 );
1125  }
1126  else
1127  R = G = B = I; // achromatic
1128  }
1129 
1161  void HSILToRGB( sample& R, sample& G, sample& B, sample H, sample S, sample I, sample L ) const
1162  {
1163  HSIToRGB( R, G, B, H, S, I );
1164 #define a H
1165 #define b S
1166  RGBToCIEab( a, b, R, G, B );
1167  CIELabToRGB( R, G, B, L, a, b );
1168 #undef a
1169 #undef b
1170  }
1171 
1180  void CIEXYZToRGB( sample& R, sample& G, sample& B, sample X, sample Y, sample Z ) const
1181  {
1182  m_data->CIEXYZToRGB( R, G, B, X, Y, Z );
1183  }
1184 
1193  void CIEXYZToCIELab( sample& L, sample& a, sample& b, sample X, sample Y, sample Z ) const
1194  {
1195  XYZLab( X ); XYZLab( Y ); XYZLab( Z );
1196  L = m_data->Range( sample( (1.16 * Y) - 0.16 ) );
1197  a = m_data->Range( (5*(X - Y) + m_data->zA)/m_data->mA );
1198  b = m_data->Range( (2*(Y - Z) + m_data->zB)/m_data->mB );
1199  }
1200 
1209  void CIELabToRGB( sample& R, sample& G, sample& B, sample L, sample a, sample b ) const
1210  {
1211  sample Y = sample( (L + 0.16)/1.16 );
1212  sample X = (m_data->mA*a - m_data->zA)/5 + Y;
1213  sample Z = Y - (m_data->mB*b - m_data->zB)/2;
1214  LabXYZ( X ); LabXYZ( Y ); LabXYZ( Z );
1215  m_data->CIEXYZToRGB( R, G, B, X, Y, Z );
1216  }
1217 
1226  void CIELabToCIEXYZ( sample& X, sample& Y, sample& Z, sample L, sample a, sample b ) const
1227  {
1228  Y = sample( (L + 0.16)/1.16 );
1229  X = (m_data->mA*a - m_data->zA)/5 + Y;
1230  Z = Y - (m_data->mB*b - m_data->zB)/2;
1231  LabXYZ( X ); LabXYZ( Y ); LabXYZ( Z );
1232  X = m_data->Range( X );
1233  Y = m_data->Range( Y );
1234  Z = m_data->Range( Z );
1235  }
1236 
1249  void CIELabToCIELch( sample& L, sample& c, sample& h, sample L0, sample a, sample b ) const
1250  {
1251  L = m_data->Range( L0 );
1252  a = m_data->mA*a - m_data->zA;
1253  b = m_data->mB*b - m_data->zB;
1254  c = m_data->Range( Sqrt( a*a + b*b )/m_data->mC );
1255  h = ArcTan( b, a )/Const<sample>::pi();
1256  if ( h < 0 )
1257  h += 1;
1258  }
1259 
1268  void CIELchToRGB( sample& R, sample& G, sample& B, sample L, sample c, sample h ) const
1269  {
1270  sample a, b;
1271  SinCos( h*Const<sample>::_2pi(), b, a );
1272  c *= m_data->mC;
1273  a *= c;
1274  b *= c;
1275  sample Y = sample( (L + 0.16)/1.16 );
1276  sample X = a/5 + Y;
1277  sample Z = Y - b/2;
1278  LabXYZ( X ); LabXYZ( Y ); LabXYZ( Z );
1279  m_data->CIEXYZToRGB( R, G, B, X, Y, Z );
1280  }
1281 
1294  void CIELchToCIELab( sample& L, sample& a, sample& b, sample L0, sample c, sample h ) const
1295  {
1296  L = m_data->Range( L0 );
1297  SinCos( h*Const<sample>::_2pi(), b, a );
1298  c *= m_data->mC;
1299  a = m_data->Range( (c*a + m_data->zA)/m_data->mA );
1300  b = m_data->Range( (c*b + m_data->zB)/m_data->mB );
1301  }
1302 
1308  static double SRGBToLinear( double x )
1309  {
1310  PCL_PRECONDITION( x >= 0 && x <= 1 )
1311  return (x > 0.04045) ? Pow( (x + 0.055)/1.055, 2.4 ) : x/12.92;
1312  }
1313 
1319  static double LinearToSRGB( double x )
1320  {
1321  PCL_PRECONDITION( x >= 0 && x <= 1 )
1322  return (x > 0.0031308) ? 1.055*Pow( x, sample( 1/2.4 ) ) - 0.055 : 12.92*x;
1323  }
1324 
1325 protected:
1326 
1327  struct Data : public ReferenceCounter
1328  {
1329  float gamma, gammaInv; // gamma, 1/gamma; ignored if issRGB=true
1330  bool issRGB; // true if sRGB gamma function is being used
1331  bool isLinear; // true if gamma=1.0 and issRGB=false, for optimization
1332 
1333  /*
1334  * Chromaticity coordinates w.r.t. the D50 reference white.
1335  */
1336  FVector x, y;
1337 
1338  /*
1339  * Luminance coefficients w.r.t. the D50 reference white.
1340  */
1341  FVector Y;
1342 
1343  /*
1344  * RGB <-> CIE XYZ transformation matrices.
1345  */
1346  Vector M; // RGB -> CIE XYZ
1347  Vector M_; // CIE XYZ -> RGB
1348 
1349  /*
1350  * Normalization of CIE X and Z components.
1351  */
1352  double mX, mZ; // scale factors
1353 
1354  /*
1355  * Normalization of CIE L*, a*, b* and c* components.
1356  */
1357  double mA, mB, mC; // scale factors
1358  double zA, zB; // zero offsets
1359 
1360  Data( float, bool, const FVector&, const FVector&, const FVector& );
1361  Data( float, bool, const float*, const float*, const float* );
1362  Data( const Data& ) = default;
1363 
1364  void Initialize();
1365 
1366  bool operator ==( const RGBColorSystem::Data& data ) const
1367  {
1368  return gamma == data.gamma &&
1369  issRGB == data.issRGB &&
1370  Y == data.Y &&
1371  x == data.x && y == data.y;
1372  }
1373 
1374  /*
1375  * Primary gamma functions.
1376  */
1377 
1378  void LinearRGB( sample& x ) const
1379  {
1380  x = sample( issRGB ? SRGBToLinear( double( x ) ) : Pow( double( x ), double( gamma ) ) );
1381  }
1382 
1383  void GammaRGB( sample& x ) const
1384  {
1385  x = sample( issRGB ? LinearToSRGB( double( x ) ) : Pow( double( x ), double( gammaInv ) ) );
1386  }
1387 
1388  /*
1389  * Primary linear transformations.
1390  */
1391 
1392  void RGBToCIEXYZ( sample& X, sample& Y, sample& Z, sample R, sample G, sample B ) const
1393  {
1394  if ( !isLinear )
1395  {
1396  LinearRGB( R );
1397  LinearRGB( G );
1398  LinearRGB( B );
1399  }
1400 
1401  X = Range( (R*M[0] + G*M[1] + B*M[2])/mX );
1402  Y = Range( R*M[3] + G*M[4] + B*M[5] );
1403  Z = Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
1404  }
1405 
1406  void CIEXYZToRGB( sample& R, sample& G, sample& B, sample X, sample Y, sample Z ) const
1407  {
1408  X *= mX;
1409  Z *= mZ;
1410  R = Range( X*M_[0] + Y*M_[1] + Z*M_[2] );
1411  G = Range( X*M_[3] + Y*M_[4] + Z*M_[5] );
1412  B = Range( X*M_[6] + Y*M_[7] + Z*M_[8] );
1413 
1414  if ( !isLinear )
1415  {
1416  GammaRGB( R );
1417  GammaRGB( G );
1418  GammaRGB( B );
1419  }
1420  }
1421 
1422  /*
1423  * Partial transformations for fast luminance/chrominance calculations.
1424  */
1425 
1426  sample CIEX( sample R, sample G, sample B ) const
1427  {
1428  if ( !isLinear )
1429  {
1430  LinearRGB( R );
1431  LinearRGB( G );
1432  LinearRGB( B );
1433  }
1434 
1435  return Range( (R*M[0] + G*M[1] + B*M[2])/mX );
1436  }
1437 
1438  sample CIEY( sample R, sample G, sample B ) const
1439  {
1440  if ( !isLinear )
1441  {
1442  LinearRGB( R );
1443  LinearRGB( G );
1444  LinearRGB( B );
1445  }
1446 
1447  return Range( R*M[3] + G*M[4] + B*M[5] );
1448  }
1449 
1450  sample CIEZ( sample R, sample G, sample B ) const
1451  {
1452  if ( !isLinear )
1453  {
1454  LinearRGB( R );
1455  LinearRGB( G );
1456  LinearRGB( B );
1457  }
1458 
1459  return Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
1460  }
1461 
1462  void RGBToCIEXY( sample& X, sample& Y, sample R, sample G, sample B ) const
1463  {
1464  if ( !isLinear )
1465  {
1466  LinearRGB( R );
1467  LinearRGB( G );
1468  LinearRGB( B );
1469  }
1470 
1471  X = Range( (R*M[0] + G*M[1] + B*M[2])/mX );
1472  Y = Range( R*M[3] + G*M[4] + B*M[5] );
1473  }
1474 
1475  void RGBToCIEYZ( sample& Y, sample& Z, sample R, sample G, sample B ) const
1476  {
1477  if ( !isLinear )
1478  {
1479  LinearRGB( R );
1480  LinearRGB( G );
1481  LinearRGB( B );
1482  }
1483 
1484  Y = Range( R*M[3] + G*M[4] + B*M[5] );
1485  Z = Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
1486  }
1487 
1488  void RGBToCIEXZ( sample& X, sample& Z, sample R, sample G, sample B ) const
1489  {
1490  if ( !isLinear )
1491  {
1492  LinearRGB( R );
1493  LinearRGB( G );
1494  LinearRGB( B );
1495  }
1496 
1497  X = Range( (R*M[0] + G*M[1] + B*M[2])/mX );
1498  Z = Range( (R*M[6] + G*M[7] + B*M[8])/mZ );
1499  }
1500 
1501  static bool ValidateParameters( const FVector& x, const FVector& y, const FVector& Y );
1502 
1503  // Ensure output values stay within the nominal [0,1] range.
1504  static sample Range( const sample& x )
1505  {
1506  return pcl::Range( x, sample( 0 ), sample( 1 ) );
1507  }
1508  };
1509 
1510  Data* m_data = nullptr;
1511 
1512  void DetachFromData()
1513  {
1514  if ( !m_data->Detach() )
1515  delete m_data;
1516  }
1517 
1518  /*
1519  * CIE XYZ -> Lab component transformation function.
1520  */
1521  static void XYZLab( sample& x )
1522  {
1523  x = (x > CIEEpsilon) ? Pow( x, sample( _1_3 ) ) : sample( CIEKappa116*x + _16_116 );
1524  }
1525 
1526  /*
1527  * CIE Lab -> XYZ component transformation function.
1528  */
1529  static void LabXYZ( sample& x )
1530  {
1531  sample x3 = x*x*x;
1532  x = (x3 > CIEEpsilon) ? x3 : sample( (x - _16_116)/CIEKappa116 );
1533  }
1534 
1535  /*
1536  * HSI hue angle to RGB component conversion function.
1537  */
1538  static sample HSIH2RGB( sample v1, sample v2, sample H )
1539  {
1540  if ( H < 0 )
1541  H += 1;
1542  else if ( H > 1 )
1543  H -= 1;
1544 
1545  if ( H < _1_6 )
1546  return v1 + 6*H*(v2 - v1);
1547 
1548  if ( H < 0.5 )
1549  return v2;
1550 
1551  if ( H < _2_3 )
1552  return v1 + 6*(_2_3 - H)*(v2 - v1);
1553 
1554  return v1;
1555  }
1556 
1557  friend struct Data;
1558 
1559 public:
1560 
1564  static const float sRGB_x[ 3 ];
1565 
1569  static const float sRGB_y[ 3 ];
1570 
1574  static const float sRGB_Y[ 3 ];
1575 
1579  static const RGBColorSystem sRGB;
1580 
1581 #ifdef __PCL_WITH_STANDARD_RGB_WORKING_SPACES
1582 
1583  /*
1584  * Adobe RGB 1998
1585  */
1586  static const float AdobeRGB1998_x[ 3 ];
1587  static const float AdobeRGB1998_y[ 3 ];
1588  static const float AdobeRGB1998_Y[ 3 ];
1589 
1590  /*
1591  * Apple RGB
1592  */
1593  static const float AppleRGB_x[ 3 ];
1594  static const float AppleRGB_y[ 3 ];
1595  static const float AppleRGB_Y[ 3 ];
1596 
1597  /*
1598  * Best RGB
1599  */
1600  static const float BestRGB_x[ 3 ];
1601  static const float BestRGB_y[ 3 ];
1602  static const float BestRGB_Y[ 3 ];
1603 
1604  /*
1605  * Beta RGB (by Bruce Lindbloom)
1606  */
1607  static const float BetaRGB_x[ 3 ];
1608  static const float BetaRGB_y[ 3 ];
1609  static const float BetaRGB_Y[ 3 ];
1610 
1611  /*
1612  * Bruce RGB
1613  */
1614  static const float BruceRGB_x[ 3 ];
1615  static const float BruceRGB_y[ 3 ];
1616  static const float BruceRGB_Y[ 3 ];
1617 
1618  /*
1619  * CIE RGB
1620  */
1621  static const float CIERGB_x[ 3 ];
1622  static const float CIERGB_y[ 3 ];
1623  static const float CIERGB_Y[ 3 ];
1624 
1625  /*
1626  * Color Match RGB
1627  */
1628  static const float ColorMatchRGB_x[ 3 ];
1629  static const float ColorMatchRGB_y[ 3 ];
1630  static const float ColorMatchRGB_Y[ 3 ];
1631 
1632  /*
1633  * NTSC RGB
1634  */
1635  static const float NTSCRGB_x[ 3 ];
1636  static const float NTSCRGB_y[ 3 ];
1637  static const float NTSCRGB_Y[ 3 ];
1638 
1639  /*
1640  * PAL/SECAM RGB
1641  */
1642  static const float PALSECAMRGB_x[ 3 ];
1643  static const float PALSECAMRGB_y[ 3 ];
1644  static const float PALSECAMRGB_Y[ 3 ];
1645 
1646  /*
1647  * ProPhoto RGB
1648  */
1649  static const float ProPhotoRGB_x[ 3 ];
1650  static const float ProPhotoRGB_y[ 3 ];
1651  static const float ProPhotoRGB_Y[ 3 ];
1652 
1653  /*
1654  * SMPTE-C RGB
1655  */
1656  static const float SMPTECRGB_x[ 3 ];
1657  static const float SMPTECRGB_y[ 3 ];
1658  static const float SMPTECRGB_Y[ 3 ];
1659 
1660  /*
1661  * Wide Gamut RGB
1662  */
1663  static const float WideGamutRGB_x[ 3 ];
1664  static const float WideGamutRGB_y[ 3 ];
1665  static const float WideGamutRGB_Y[ 3 ];
1666 
1667 #endif // __PCL_WITH_STANDARD_RGB_WORKING_SPACES
1668 };
1669 
1670 // ----------------------------------------------------------------------------
1671 
1672 } // pcl
1673 
1674 #undef _1_3
1675 #undef _1_6
1676 #undef _2_3
1677 #undef _16_116
1678 #undef CIEEpsilon
1679 #undef CIEKappa
1680 #undef CIEKappa116
1681 
1682 #endif // __PCL_RGBColorSystem_h
1683 
1684 // ----------------------------------------------------------------------------
1685 // EOF pcl/RGBColorSystem.h - Released 2024-06-18T15:48:54Z
Fundamental numeric constants.
Definition: Constants.h:73
static constexpr T _2pi()
Definition: Constants.h:114
static constexpr T pi()
Definition: Constants.h:94
32-bit floating point real vector.
Generic vector of arbitrary length.
Definition: Vector.h:107
Colorimetrically defined RGB working color space.
sample CIEa(sample R, sample G, sample B) const
RGBColorSystem(float gamma, bool issRGB, const float *x, const float *y, const float *Y=nullptr)
sample Hue(sample R, sample G, sample B) const
static const RGBColorSystem sRGB
static double LinearToSRGB(double x)
sample HSISaturation(sample R, sample G, sample B) const
void CIEXYZToCIELab(sample &L, sample &a, sample &b, sample X, sample Y, sample Z) const
void RGBToHSIL(sample &H, sample &S, sample &I, sample &L, sample R, sample G, sample B) const
sample CIEb(sample R, sample G, sample B) const
double CIEXNormalizationFactor() const
const FVector & LuminanceCoefficients() const
sample Intensity(sample R, sample G, sample B) const
sample HSVSaturation(sample R, sample G, sample B) const
const FVector & ChromaticityXCoordinates() const
sample CIEYToCIEL(sample Y) const
const Vector & XYZToRGBMatrix() const
sample Lightness(sample R, sample G, sample B) const
double CIEaNormalizationFactor() const
const Vector & RGBToXYZMatrix() const
sample CIEL(sample R, sample G, sample B) const
void HSVLToRGB(sample &R, sample &G, sample &B, sample H, sample S, sample V, sample L) const
sample CIEhr(sample R, sample G, sample B) const
bool IsUnique() const
RGBColorSystem(float gamma, bool issRGB, const FVector &x, const FVector &y, const FVector &Y=FVector())
sample Value(sample R, sample G, sample B) const
double CIEZNormalizationFactor() const
sample CIEX(sample R, sample G, sample B) const
void CIELchToRGB(sample &R, sample &G, sample &B, sample L, sample c, sample h) const
void RGBToGray(sample &K, sample R, sample G, sample B) const
sample CIEY(sample R, sample G, sample B) const
void CIELabToCIEXYZ(sample &X, sample &Y, sample &Z, sample L, sample a, sample b) const
double CIEbNormalizationOffset() const
void CIELabToRGB(sample &R, sample &G, sample &B, sample L, sample a, sample b) const
bool IsLinear() const
static void RGBToHSI(sample &H, sample &S, sample &I, sample R, sample G, sample B)
void CIEXYZToRGB(sample &R, sample &G, sample &B, sample X, sample Y, sample Z) const
static void RGBToHSV(sample &H, sample &S, sample &V, sample R, sample G, sample B)
void RGBToCIEXZ(sample &X, sample &Z, sample R, sample G, sample B) const
bool IsAliasOf(const RGBColorSystem &s) const
const FVector & ChromaticityYCoordinates() const
sample CIEZ(sample R, sample G, sample B) const
double CIEaNormalizationOffset() const
static void HSIToRGB(sample &R, sample &G, sample &B, sample H, sample S, sample I)
sample CIEh(sample R, sample G, sample B) const
RGBColorSystem(const RGBColorSystem &s)
void RGBToCIELab(sample &L, sample &a, sample &b, sample R, sample G, sample B) const
void CIELchToCIELab(sample &L, sample &a, sample &b, sample L0, sample c, sample h) const
sample Saturation(sample R, sample G, sample B) const
void HSILToRGB(sample &R, sample &G, sample &B, sample H, sample S, sample I, sample L) const
sample CIEc(sample R, sample G, sample B) const
void RGBToCIELch(sample &L, sample &c, sample &h, sample R, sample G, sample B) const
static void HSVToRGB(sample &R, sample &G, sample &B, sample H, sample S, sample V)
void RGBToCIEXYZ(sample &X, sample &Y, sample &Z, sample R, sample G, sample B) const
void RGBToCIEab(sample &a, sample &b, sample R, sample G, sample B) const
void RGBToCIELabc(sample &L, sample &a, sample &b, sample &c, sample R, sample G, sample B) const
double CIEbNormalizationFactor() const
sample CIELToCIEY(sample L) const
float Gamma() const
static double SRGBToLinear(double x)
void CIELabToCIELch(sample &L, sample &c, sample &h, sample L0, sample a, sample b) const
void RGBToHSVL(sample &H, sample &S, sample &V, sample &L, sample R, sample G, sample B) const
double CIEcNormalizationFactor() const
void Assign(const RGBColorSystem &rgbws)
Thread-safe reference counter for copy-on-write data structures.
bool operator==(const Array< T, A > &x1, const Array< T, A > &x2) noexcept
Definition: Array.h:2267
Complex< T1 > Pow(const Complex< T1 > &c, T2 x) noexcept
Definition: Complex.h:747
Complex< T > Sqrt(const Complex< T > &c) noexcept
Definition: Complex.h:674
void SinCos(T x, T &sx, T &cx) noexcept
Definition: Math.h:1030
constexpr T ArcTan(T x) noexcept
Definition: Math.h:526
int TruncInt(T x) noexcept
Definition: Math.h:1132
constexpr T Floor(T x) noexcept
Definition: Math.h:628
constexpr const T & Min(const T &a, const T &b) noexcept
Definition: Utility.h:90
constexpr const T & Range(const T &x, const T &a, const T &b) noexcept
Definition: Utility.h:190
constexpr const T & Max(const T &a, const T &b) noexcept
Definition: Utility.h:119
PCL root namespace.
Definition: AbstractImage.h:77