im_color.h

Go to the documentation of this file.
00001 /** \file
00002  * \brief Color Manipulation
00003  *
00004  * See Copyright Notice in im_lib.h
00005  */
00006 
00007 #ifndef __IM_COLOR_H
00008 #define __IM_COLOR_H
00009 
00010 #include "im_math.h"
00011 
00012 /** \defgroup color Color Manipulation
00013  *
00014  * \par
00015  * Functions to convert from one color space to another, 
00016  * and color gammut utilities.
00017  * \par
00018  * See \ref im_color.h
00019  *
00020  * \section s1 Some Color Science
00021  * \par
00022  * Y is luminance, a linear-light quantity. 
00023  * It is directly proportional to physical intensity
00024  * weighted by the spectral sensitivity of human vision.
00025  * \par
00026  * L* is lightness, a nonlinear luminance
00027  * that aproximates the perception of brightness. 
00028  * It is nearly perceptual uniform.
00029  * It has a range of 0 to 100.
00030  * \par
00031  * Y' is luma, a nonlinear luminance that aproximates lightness.
00032  * \par
00033  * Brightness is a visual sensation according to which an area
00034  * apears to exhibit more or less light. 
00035  * It is a subjective quantity and can not be measured.
00036  * \par
00037  * One unit of euclidian distante in CIE L*u*v* or CIE L*a*b* corresponds
00038  * roughly to a just-noticeable difference (JND) of color.
00039  * \par
00040 \verbatim
00041  ChromaUV = sqrt(u*u + v*v)       
00042  HueUV = atan2(v, u)
00043  SaturationUV = ChromaUV / L      (called psychometric saturation) 
00044  (the same can be calculated for Lab)
00045 \endverbatim
00046  * \par
00047  * IEC 61966-2.1 Default RGB colour space - sRGB
00048  * \li ITU-R Recommendation BT.709 (D65 white point).
00049  * \li D65 White Point (X,Y,Z) = (0.9505 1.0000 1.0890)
00050  * \par
00051  * Documentation extracted from  Charles Poynton - Digital Video and HDTV - Morgan Kaufmann - 2003.
00052  *
00053  * \section Links
00054  * \li www.color.org - ICC
00055  * \li www.srgb.com - sRGB
00056  * \li www.poynton.com - Charles Poynton
00057  * \li www.littlecms.com - A free Color Management System (use this if you need precise color conversions)
00058  *
00059  * \section cci Color Component Intervals
00060  * \par
00061  * All the color components are stored in the 0-max interval, even the signed ones. \n
00062  * Here are the pre-defined intervals for each data type. These values are used for standard color conversion.
00063  * You should normalize data before converting betwwen color spaces.
00064  * \par
00065 \verbatim
00066  byte   [0,255]      or [-128,+127]          (1 byte)
00067  ushort [0,65535]    or [-32768,+32767]      (2 bytes)
00068  int    [0,16777215] or [-8388608,+8388607]  (3 bytes)
00069  float  [0,1]        or [-0.5,+0.5]          (4 bytes)
00070 \endverbatim
00071  * \ingroup util */
00072 
00073 /** Returns the zero value for color conversion porpouses. \n
00074  * This is a value to be compensated when the data_type is unsigned and component is signed. \n
00075  * \ingroup color */
00076 inline float imColorZero(int data_type)
00077 {
00078   float zero[] = {128.0f, 32768.0f, 8388608.0f, 0.5f};
00079   return zero[data_type];
00080 }
00081 
00082 /** Returns the maximum value for color conversion porpouses. \n
00083  * \ingroup color */
00084 inline int imColorMax(int data_type)
00085 {
00086   int max[] = {255, 65535, 16777215, 1};
00087   return max[data_type];
00088 }
00089 
00090 /** Quantize r=0-1 values into q=0-max.
00091  * max is the maximum value.
00092  * max and the returned value are usually integers,
00093  * but the dummy quantizer uses real values.
00094  * See also \ref math.
00095  * \ingroup color */
00096 template <class T> 
00097 inline T imColorQuantize(const float& value, const T& max)
00098 {
00099   if (max == 1) return (T)value; // to allow a dummy quantizer
00100   if (value >= 1) return max;
00101   if (value <= 0) return 0;
00102   /* return (T)imRound(value*(max + 1) - 0.5f);  not necessary since all values are positive */
00103   return (T)(value*(max + 1));
00104 }                               
00105 
00106 /** Reconstruct 0-max values into 0-1. \n
00107  * max is the maximum value.
00108  * max and the given value are usually integers,
00109  * but the dummy reconstructor uses real values.
00110  * See also \ref math.
00111  * \ingroup color */
00112 template <class T> 
00113 inline float imColorReconstruct(const T& value, const T& max)
00114 {
00115   if (max == 1) return (float)value;  // to allow a dummy reconstructor
00116   if (value <= 0) return 0;
00117   if (value >= max) return 1;
00118   return (((float)value + 0.5f)/((float)max + 1.0f));
00119 }
00120 
00121 /** Converts Y'CbCr to R'G'B' (all nonlinear). \n
00122  * ITU-R Recommendation 601-1 with no headroom/footroom.
00123 \verbatim
00124  0 <= Y <= 1 ; -0.5 <= CbCr <= 0.5 ; 0 <= RGB <= 1 
00125 
00126  R'= Y' + 0.000 *Cb + 1.402 *Cr
00127  G'= Y' - 0.344 *Cb - 0.714 *Cr
00128  B'= Y' + 1.772 *Cb + 0.000 *Cr
00129 \endverbatim
00130  * \ingroup color */
00131 template <class T> 
00132 inline void imColorYCbCr2RGB(const T Y, const T Cb, const T Cr, 
00133                              T& R, T& G, T& B,
00134                              const T& zero, const T& max)
00135 {
00136   float r = float(Y                        + 1.402f * (Cr - zero));
00137   float g = float(Y - 0.344f * (Cb - zero) - 0.714f * (Cr - zero));
00138   float b = float(Y + 1.772f * (Cb - zero));
00139 
00140   // now we should enforce 0<= rgb <= max
00141 
00142   R = (T)IM_CROPMAX(r, max);
00143   G = (T)IM_CROPMAX(g, max);
00144   B = (T)IM_CROPMAX(b, max);
00145 }
00146 
00147 /** Converts R'G'B' to Y'CbCr (all nonlinear). \n
00148  * ITU-R Recommendation 601-1 with no headroom/footroom.
00149 \verbatim
00150  0 <= Y <= 1 ; -0.5 <= CbCr <= 0.5 ; 0 <= RGB <= 1 
00151 
00152  Y' =  0.299 *R' + 0.587 *G' + 0.114 *B'
00153  Cb = -0.169 *R' - 0.331 *G' + 0.500 *B'
00154  Cr =  0.500 *R' - 0.419 *G' - 0.081 *B'
00155 \endverbatim
00156  * \ingroup color */
00157 template <class T> 
00158 inline void imColorRGB2YCbCr(const T R, const T G, const T B, 
00159                              T& Y, T& Cb, T& Cr,
00160                              const T& zero)
00161 {
00162   Y  = (T)( 0.299f *R + 0.587f *G + 0.114f *B);
00163   Cb = (T)(-0.169f *R - 0.331f *G + 0.500f *B + (float)zero);
00164   Cr = (T)( 0.500f *R - 0.419f *G - 0.081f *B + (float)zero);
00165 
00166   // there is no need for cropping here, YCrCr is already at the limits
00167 }
00168 
00169 /** Converts C'M'Y'K' to R'G'B' (all nonlinear). \n
00170  * This is a poor conversion that works for a simple visualization.
00171 \verbatim
00172   0 <= CMYK <= 1 ; 0 <= RGB <= 1 
00173 
00174   R = (1 - K) * (1 - C)
00175   G = (1 - K) * (1 - M)
00176   B = (1 - K) * (1 - Y)
00177 \endverbatim
00178  * \ingroup color */
00179 template <class T>
00180 inline void imColorCMYK2RGB(const T C, const T M, const T Y, const T K, 
00181                             T& R, T& G, T& B, const T& max)
00182 {
00183   T W = max - K;
00184   R = (T)((W * (max - C)) / max);
00185   G = (T)((W * (max - M)) / max);
00186   B = (T)((W * (max - Y)) / max);
00187 
00188   // there is no need for cropping here, RGB is already at the limits
00189 }
00190 
00191 /** Converts CIE XYZ to Rec 709 RGB (all linear). \n
00192  * ITU-R Recommendation BT.709 (D65 white point). \n
00193 \verbatim
00194   0 <= XYZ <= 1 ; 0 <= RGB <= 1    
00195 
00196   R =  3.2406 *X - 1.5372 *Y - 0.4986 *Z
00197   G = -0.9689 *X + 1.8758 *Y + 0.0415 *Z
00198   B =  0.0557 *X - 0.2040 *Y + 1.0570 *Z
00199 \endverbatim
00200  * \ingroup color */
00201 template <class T>
00202 inline void imColorXYZ2RGB(const T X, const T Y, const T Z, 
00203                            T& R, T& G, T& B, const T& max)
00204 {
00205   float r =  3.2406f *X - 1.5372f *Y - 0.4986f *Z;
00206   float g = -0.9689f *X + 1.8758f *Y + 0.0415f *Z;
00207   float b =  0.0557f *X - 0.2040f *Y + 1.0570f *Z;
00208 
00209   // we need to crop because not all XYZ colors are visible
00210 
00211   R = (T)IM_CROPMAX(r, max);
00212   G = (T)IM_CROPMAX(g, max);
00213   B = (T)IM_CROPMAX(b, max);
00214 }
00215 
00216 /** Converts Rec 709 RGB to CIE XYZ (all linear). \n
00217  * ITU-R Recommendation BT.709 (D65 white point). \n
00218 \verbatim
00219   0 <= XYZ <= 1 ; 0 <= RGB <= 1    
00220 
00221   X = 0.4124 *R + 0.3576 *G + 0.1805 *B
00222   Y = 0.2126 *R + 0.7152 *G + 0.0722 *B
00223   Z = 0.0193 *R + 0.1192 *G + 0.9505 *B
00224 \endverbatim
00225  * \ingroup color */
00226 template <class T>
00227 inline void imColorRGB2XYZ(const T R, const T G, const T B, 
00228                            T& X, T& Y, T& Z)
00229 {
00230   X = (T)(0.4124f *R + 0.3576f *G + 0.1805f *B);
00231   Y = (T)(0.2126f *R + 0.7152f *G + 0.0722f *B);
00232   Z = (T)(0.0193f *R + 0.1192f *G + 0.9505f *B);
00233 
00234   // there is no need for cropping here, XYZ is already at the limits
00235 }
00236 
00237 #define IM_FWLAB(_w) (_w > 0.008856f?               \
00238                         powf(_w, 1.0f/3.0f):        \
00239                         7.787f * _w + 0.16f/1.16f)
00240 
00241 /** Converts CIE XYZ (linear) to CIE L*a*b* (nonlinear). \n
00242  * The white point is D65. \n
00243 \verbatim
00244   0 <= L <= 1 ; -0.5 <= ab <= +0.5 ; 0 <= XYZ <= 1 
00245 
00246   if (t > 0.008856)
00247     f(t) = pow(t, 1/3)
00248   else
00249     f(t) = 7.787*t + 16/116
00250 
00251   fX = f(X / Xn)      fY = f(Y / Yn)      fZ = f(Z / Zn)
00252 
00253   L = 1.16 * fY - 0.16
00254   a = 2.5 * (fX - fY)
00255   b = (fY - fZ)
00256 
00257 \endverbatim
00258  * \ingroup color */
00259 inline void imColorXYZ2Lab(const float X, const float Y, const float Z, 
00260                            float& L, float& a, float& b)
00261 {
00262   float fX = X / 0.9505f;  // white point D65
00263   float fY = Y / 1.0f;
00264   float fZ = Z / 1.0890f;
00265 
00266   fX = IM_FWLAB(fX);
00267   fY = IM_FWLAB(fY);
00268   fZ = IM_FWLAB(fZ);
00269 
00270   L = 1.16f * fY - 0.16f;
00271   a = 2.5f * (fX - fY);
00272   b = (fY - fZ);
00273 }
00274 
00275 #define IM_GWLAB(_w)  (_w > 0.20689f?                     \
00276                          powf(_w, 3.0f):                  \
00277                          0.1284f * (_w - 0.16f/1.16f))
00278 
00279 /** Converts CIE L*a*b* (nonlinear) to CIE XYZ (linear). \n
00280  * The white point is D65. \n
00281  * 0 <= L <= 1 ; -0.5 <= ab <= +0.5 ; 0 <= XYZ <= 1 
00282  * \ingroup color */
00283 inline void imColorLab2XYZ(const float L, const float a, const float b, 
00284                            float& X, float& Y, float& Z)
00285 
00286 {
00287   float fY = (L + 0.16f) / 1.16f;
00288   float gY = IM_GWLAB(fY);
00289 
00290   float fgY = IM_FWLAB(gY);
00291   float gX = fgY + a / 2.5f;
00292   float gZ = fgY - b;
00293   gX = IM_GWLAB(gX);
00294   gZ = IM_GWLAB(gZ);
00295 
00296   X = gX * 0.9505f;     // white point D65
00297   Y = gY * 1.0f;
00298   Z = gZ * 1.0890f;
00299 }
00300 
00301 /** Converts CIE XYZ (linear) to CIE L*u*v* (nonlinear). \n
00302  * The white point is D65. \n
00303 \verbatim
00304   0 <= L <= 1 ; -1 <= uv <= +1 ; 0 <= XYZ <= 1
00305 
00306   Y = Y / 1.0      (for D65)
00307   if (Y > 0.008856)
00308     fY = pow(Y, 1/3)
00309   else
00310     fY = 7.787 * Y + 0.16/1.16
00311   L = 1.16 * fY - 0.16
00312 
00313   U(x, y, z) = (4 * x)/(x + 15 * y + 3 * z)
00314   V(x, y, z) = (9 * x)/(x + 15 * y + 3 * z)
00315   un = U(Xn, Yn, Zn) = 0.1978      (for D65)
00316   vn = V(Xn, Yn, Zn) = 0.4683      (for D65)
00317   fu = U(X, Y, Z) 
00318   fv = V(X, Y, Z) 
00319 
00320   u = 13 * L * (fu - un)
00321   v = 13 * L * (fv - vn)
00322 \endverbatim
00323  * \ingroup color */
00324 inline void imColorXYZ2Luv(const float X, const float Y, const float Z, 
00325                            float& L, float& u, float& v)
00326 {
00327   float XYZ = (float)(X + 15 * Y + 3 * Z);
00328   float fY = Y / 1.0f;
00329 
00330   if (XYZ != 0)
00331   {
00332     L = 1.16f * IM_FWLAB(fY) - 0.16f;
00333     u = 6.5f * L * ((4 * X)/XYZ - 0.1978f);
00334     v = 6.5f * L * ((9 * Y)/XYZ - 0.4683f);
00335   }
00336   else
00337   {
00338     L = u = v = 0;
00339   }
00340 }
00341 
00342 /** Converts CIE L*u*v* (nonlinear) to CIE XYZ (linear). \n
00343  * The white point is D65.
00344  * 0 <= L <= 1 ; -0.5 <= uv <= +0.5 ; 0 <= XYZ <= 1 \n
00345  * \ingroup color */
00346 inline void imColorLuv2XYZ(const float L, const float u, const float v, 
00347                            float& X, float& Y, float& Z)
00348 
00349 {
00350   float fY = (L + 0.16f) / 1.16f;
00351   Y = IM_GWLAB(fY) * 1.0f;
00352 
00353   float ul = 0.1978f, vl = 0.4683f;
00354   if (L != 0)
00355   {
00356     ul = u / (6.5f * L) + 0.1978f;
00357     vl = v / (6.5f * L) + 0.4683f;
00358   }
00359 
00360   X = ((9 * ul) / (4 * vl)) * Y;
00361   Z = ((12 - 3 * ul - 20 * vl) / (4 * vl)) * Y;
00362 }
00363 
00364 /** Converts nonlinear values to linear values. \n
00365  * We use the sRGB transfer function. sRGB uses ITU-R 709 primaries and D65 white point. \n
00366 \verbatim
00367   0 <= l <= 1 ; 0 <= v <= 1 
00368 
00369   if (v < 0.03928)
00370     l = v / 12.92
00371   else
00372     l = pow((v + 0.055) / 1.055, 2.4)
00373 \endverbatim
00374  * \ingroup color */                           
00375 inline float imColorTransfer2Linear(const float& nonlinear_value)
00376 {
00377   if (nonlinear_value < 0.03928f)
00378     return nonlinear_value / 12.92f;
00379   else
00380     return powf((nonlinear_value + 0.055f) / 1.055f, 2.4f);
00381 }
00382 
00383 /** Converts linear values to nonlinear values. \n
00384  * We use the sRGB transfer function. sRGB uses ITU-R 709 primaries and D65 white point. \n
00385 \verbatim
00386   0 <= l <= 1 ; 0 <= v <= 1 
00387 
00388   if (l < 0.0031308)
00389     v = 12.92 * l
00390   else
00391     v = 1.055 * pow(l, 1/2.4) - 0.055
00392 \endverbatim
00393  * \ingroup color */                           
00394 inline float imColorTransfer2Nonlinear(const float& value)
00395 {
00396   if (value < 0.0031308f)
00397     return 12.92f * value;
00398   else
00399     return 1.055f * powf(value, 1.0f/2.4f) - 0.055f;
00400 }
00401 
00402 /** Converts RGB (linear) to R'G'B' (nonlinear).
00403  * \ingroup color */
00404 inline void imColorRGB2RGBNonlinear(const float RL, const float GL, const float BL,
00405                                     float& R, float& G, float& B)
00406 {
00407   R = imColorTransfer2Nonlinear(RL);
00408   G = imColorTransfer2Nonlinear(GL);
00409   B = imColorTransfer2Nonlinear(BL);
00410 }
00411 
00412 /** Converts R'G'B' to Y' (all nonlinear). \n
00413 \verbatim
00414  Y'  =  0.299 *R' + 0.587 *G' + 0.114 *B'
00415 \endverbatim
00416  * \ingroup color */
00417 template <class T> 
00418 inline T imColorRGB2Luma(const T R, const T G, const T B)
00419 {
00420   return (T)((299 * R + 587 * G + 114 * B) / 1000);
00421 }
00422 
00423 /** Converts Luminance (CIE Y) to Lightness (CIE L*) (all linear). \n
00424  * The white point is D65.
00425 \verbatim
00426   0 <= Y <= 1 ; 0 <= L* <= 1
00427 
00428   Y = Y / 1.0      (for D65)
00429   if (Y > 0.008856)
00430     fY = pow(Y, 1/3)
00431   else
00432     fY = 7.787 * Y + 0.16/1.16
00433   L = 1.16 * fY - 0.16
00434 \endverbatim
00435  * \ingroup color */
00436 inline float imColorLuminance2Lightness(const float& Y)
00437 {
00438   return 1.16f * IM_FWLAB(Y) - 0.16f;
00439 }
00440 
00441 /** Converts Lightness (CIE L*) to Luminance (CIE Y) (all linear). \n
00442  * The white point is D65.
00443 \verbatim
00444   0 <= Y <= 1 ; 0 <= L* <= 1
00445 
00446   fY = (L + 0.16)/1.16
00447   if (fY > 0.20689)
00448     Y = pow(fY, 3)
00449   else
00450     Y = 0.1284 * (fY - 0.16/1.16)
00451   Y = Y * 1.0      (for D65)
00452 \endverbatim
00453  * \ingroup color */
00454 inline float imColorLightness2Luminance(const float& L)
00455 {
00456   float fY = (L + 0.16f) / 1.16f;
00457   return IM_GWLAB(fY);
00458 }
00459 
00460 #undef IM_FWLAB
00461 #undef IM_GWLAB
00462 #undef IM_CROPL
00463 #undef IM_CROPC
00464 
00465 #endif

Generated on Thu Oct 1 11:40:01 2009 for IM by  doxygen 1.6.1