IM: im_color.h Source File
From IM - An Imaging Tool
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 0-1 values into 0-max. \n 00091 * q = r * (max + 1) \n 00092 * Divide by the size of each interval 1/(max+1), 00093 * then the value is rounded down in the typecast. \n 00094 * But 0 is mapped to 0, and 1 is mapped to max. 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 quantize 00100 if (value >= 1) return max; 00101 if (value <= 0) return 0; 00102 return (T)(value*(max + 1)); 00103 } 00104 00105 /** Reconstruct 0-max values into 0-1. \n 00106 * r = (q + 0.5)/(max + 1) \n 00107 * Add 0.5 to set the same origin, then multiply by the size of each interval 1/(max+1). \n 00108 * But 0 is mapped to 0, and max is mapped to 1. 00109 * \ingroup color */ 00110 template <class T> 00111 inline float imColorReconstruct(const T& value, const T& max) 00112 { 00113 if (max == 1) return (float)value; // to allow a dummy reconstruct 00114 if (value <= 0) return 0; 00115 if (value >= max) return 1; 00116 return (((float)value + 0.5f)/((float)max + 1.0f)); 00117 } 00118 00119 /** Converts Y'CbCr to R'G'B' (all nonlinear). \n 00120 * ITU-R Recommendation 601-1 with no headroom/footroom. 00121 \verbatim 00122 0 <= Y <= 1 ; -0.5 <= CbCr <= 0.5 ; 0 <= RGB <= 1 00123 00124 R'= Y' + 0.000 *Cb + 1.402 *Cr 00125 G'= Y' - 0.344 *Cb - 0.714 *Cr 00126 B'= Y' + 1.772 *Cb + 0.000 *Cr 00127 \endverbatim 00128 * \ingroup color */ 00129 template <class T> 00130 inline void imColorYCbCr2RGB(const T Y, const T Cb, const T Cr, 00131 T& R, T& G, T& B, 00132 const T& zero, const T& max) 00133 { 00134 float r = float(Y + 1.402f * (Cr - zero)); 00135 float g = float(Y - 0.344f * (Cb - zero) - 0.714f * (Cr - zero)); 00136 float b = float(Y + 1.772f * (Cb - zero)); 00137 00138 // now we should enforce 0<= rgb <= max 00139 00140 R = (T)IM_CROPMAX(r, max); 00141 G = (T)IM_CROPMAX(g, max); 00142 B = (T)IM_CROPMAX(b, max); 00143 } 00144 00145 /** Converts R'G'B' to Y'CbCr (all nonlinear). \n 00146 * ITU-R Recommendation 601-1 with no headroom/footroom. 00147 \verbatim 00148 0 <= Y <= 1 ; -0.5 <= CbCr <= 0.5 ; 0 <= RGB <= 1 00149 00150 Y' = 0.299 *R' + 0.587 *G' + 0.114 *B' 00151 Cb = -0.169 *R' - 0.331 *G' + 0.500 *B' 00152 Cr = 0.500 *R' - 0.419 *G' - 0.081 *B' 00153 \endverbatim 00154 * \ingroup color */ 00155 template <class T> 00156 inline void imColorRGB2YCbCr(const T R, const T G, const T B, 00157 T& Y, T& Cb, T& Cr, 00158 const T& zero) 00159 { 00160 Y = (T)( 0.299f *R + 0.587f *G + 0.114f *B); 00161 Cb = (T)(-0.169f *R - 0.331f *G + 0.500f *B + (float)zero); 00162 Cr = (T)( 0.500f *R - 0.419f *G - 0.081f *B + (float)zero); 00163 00164 // there is no need for cropping here, YCrCr is already at the limits 00165 } 00166 00167 /** Converts C'M'Y'K' to R'G'B' (all nonlinear). \n 00168 * This is a poor conversion that works for a simple visualization. 00169 \verbatim 00170 0 <= CMYK <= 1 ; 0 <= RGB <= 1 00171 00172 R = (1 - K) * (1 - C) 00173 G = (1 - K) * (1 - M) 00174 B = (1 - K) * (1 - Y) 00175 \endverbatim 00176 * \ingroup color */ 00177 template <class T> 00178 inline void imColorCMYK2RGB(const T C, const T M, const T Y, const T K, 00179 T& R, T& G, T& B, const T& max) 00180 { 00181 T W = max - K; 00182 R = (T)((W * (max - C)) / max); 00183 G = (T)((W * (max - M)) / max); 00184 B = (T)((W * (max - Y)) / max); 00185 00186 // there is no need for cropping here, RGB is already at the limits 00187 } 00188 00189 /** Converts CIE XYZ to Rec 709 RGB (all linear). \n 00190 * ITU-R Recommendation BT.709 (D65 white point). \n 00191 \verbatim 00192 0 <= XYZ <= 1 ; 0 <= RGB <= 1 00193 00194 R = 3.2406 *X - 1.5372 *Y - 0.4986 *Z 00195 G = -0.9689 *X + 1.8758 *Y + 0.0415 *Z 00196 B = 0.0557 *X - 0.2040 *Y + 1.0570 *Z 00197 \endverbatim 00198 * \ingroup color */ 00199 template <class T> 00200 inline void imColorXYZ2RGB(const T X, const T Y, const T Z, 00201 T& R, T& G, T& B, const T& max) 00202 { 00203 float r = 3.2406f *X - 1.5372f *Y - 0.4986f *Z; 00204 float g = -0.9689f *X + 1.8758f *Y + 0.0415f *Z; 00205 float b = 0.0557f *X - 0.2040f *Y + 1.0570f *Z; 00206 00207 // we need to crop because not all XYZ colors are visible 00208 00209 R = (T)IM_CROPMAX(r, max); 00210 G = (T)IM_CROPMAX(g, max); 00211 B = (T)IM_CROPMAX(b, max); 00212 } 00213 00214 /** Converts Rec 709 RGB to CIE XYZ (all linear). \n 00215 * ITU-R Recommendation BT.709 (D65 white point). \n 00216 \verbatim 00217 0 <= XYZ <= 1 ; 0 <= RGB <= 1 00218 00219 X = 0.4124 *R + 0.3576 *G + 0.1805 *B 00220 Y = 0.2126 *R + 0.7152 *G + 0.0722 *B 00221 Z = 0.0193 *R + 0.1192 *G + 0.9505 *B 00222 \endverbatim 00223 * \ingroup color */ 00224 template <class T> 00225 inline void imColorRGB2XYZ(const T R, const T G, const T B, 00226 T& X, T& Y, T& Z) 00227 { 00228 X = (T)(0.4124f *R + 0.3576f *G + 0.1805f *B); 00229 Y = (T)(0.2126f *R + 0.7152f *G + 0.0722f *B); 00230 Z = (T)(0.0193f *R + 0.1192f *G + 0.9505f *B); 00231 00232 // there is no need for cropping here, XYZ is already at the limits 00233 } 00234 00235 #define IM_FWLAB(_w) (_w > 0.008856f? \ 00236 powf(_w, 1.0f/3.0f): \ 00237 7.787f * _w + 0.16f/1.16f) 00238 00239 /** Converts CIE XYZ (linear) to CIE L*a*b* (nonlinear). \n 00240 * The white point is D65. \n 00241 \verbatim 00242 0 <= L <= 1 ; -0.5 <= ab <= +0.5 ; 0 <= XYZ <= 1 00243 00244 if (t > 0.008856) 00245 f(t) = pow(t, 1/3) 00246 else 00247 f(t) = 7.787*t + 16/116 00248 00249 fX = f(X / Xn) fY = f(Y / Yn) fZ = f(Z / Zn) 00250 00251 L = 1.16 * fY - 0.16 00252 a = 2.5 * (fX - fY) 00253 b = (fY - fZ) 00254 00255 \endverbatim 00256 * \ingroup color */ 00257 inline void imColorXYZ2Lab(const float X, const float Y, const float Z, 00258 float& L, float& a, float& b) 00259 { 00260 float fX = X / 0.9505f; // white point D65 00261 float fY = Y / 1.0f; 00262 float fZ = Z / 1.0890f; 00263 00264 fX = IM_FWLAB(fX); 00265 fY = IM_FWLAB(fY); 00266 fZ = IM_FWLAB(fZ); 00267 00268 L = 1.16f * fY - 0.16f; 00269 a = 2.5f * (fX - fY); 00270 b = (fY - fZ); 00271 } 00272 00273 #define IM_GWLAB(_w) (_w > 0.20689f? \ 00274 powf(_w, 3.0f): \ 00275 0.1284f * (_w - 0.16f/1.16f)) 00276 00277 /** Converts CIE L*a*b* (nonlinear) to CIE XYZ (linear). \n 00278 * The white point is D65. \n 00279 * 0 <= L <= 1 ; -0.5 <= ab <= +0.5 ; 0 <= XYZ <= 1 00280 * \ingroup color */ 00281 inline void imColorLab2XYZ(const float L, const float a, const float b, 00282 float& X, float& Y, float& Z) 00283 00284 { 00285 float fY = (L + 0.16f) / 1.16f; 00286 float gY = IM_GWLAB(fY); 00287 00288 float fgY = IM_FWLAB(gY); 00289 float gX = fgY + a / 2.5f; 00290 float gZ = fgY - b; 00291 gX = IM_GWLAB(gX); 00292 gZ = IM_GWLAB(gZ); 00293 00294 X = gX * 0.9505f; // white point D65 00295 Y = gY * 1.0f; 00296 Z = gZ * 1.0890f; 00297 } 00298 00299 /** Converts CIE XYZ (linear) to CIE L*u*v* (nonlinear). \n 00300 * The white point is D65. \n 00301 \verbatim 00302 0 <= L <= 1 ; -1 <= uv <= +1 ; 0 <= XYZ <= 1 00303 00304 Y = Y / 1.0 (for D65) 00305 if (Y > 0.008856) 00306 fY = pow(Y, 1/3) 00307 else 00308 fY = 7.787 * Y + 0.16/1.16 00309 L = 1.16 * fY - 0.16 00310 00311 U(x, y, z) = (4 * x)/(x + 15 * y + 3 * z) 00312 V(x, y, z) = (9 * x)/(x + 15 * y + 3 * z) 00313 un = U(Xn, Yn, Zn) = 0.1978 (for D65) 00314 vn = V(Xn, Yn, Zn) = 0.4683 (for D65) 00315 fu = U(X, Y, Z) 00316 fv = V(X, Y, Z) 00317 00318 u = 13 * L * (fu - un) 00319 v = 13 * L * (fv - vn) 00320 \endverbatim 00321 * \ingroup color */ 00322 inline void imColorXYZ2Luv(const float X, const float Y, const float Z, 00323 float& L, float& u, float& v) 00324 { 00325 float XYZ = (float)(X + 15 * Y + 3 * Z); 00326 float fY = Y / 1.0f; 00327 00328 if (XYZ != 0) 00329 { 00330 L = 1.16f * IM_FWLAB(fY) - 0.16f; 00331 u = 6.5f * L * ((4 * X)/XYZ - 0.1978f); 00332 v = 6.5f * L * ((9 * Y)/XYZ - 0.4683f); 00333 } 00334 else 00335 { 00336 L = u = v = 0; 00337 } 00338 } 00339 00340 /** Converts CIE L*u*v* (nonlinear) to CIE XYZ (linear). \n 00341 * The white point is D65. 00342 * 0 <= L <= 1 ; -0.5 <= uv <= +0.5 ; 0 <= XYZ <= 1 \n 00343 * \ingroup color */ 00344 inline void imColorLuv2XYZ(const float L, const float u, const float v, 00345 float& X, float& Y, float& Z) 00346 00347 { 00348 float fY = (L + 0.16f) / 1.16f; 00349 Y = IM_GWLAB(fY) * 1.0f; 00350 00351 float ul = 0.1978f, vl = 0.4683f; 00352 if (L != 0) 00353 { 00354 ul = u / (6.5f * L) + 0.1978f; 00355 vl = v / (6.5f * L) + 0.4683f; 00356 } 00357 00358 X = ((9 * ul) / (4 * vl)) * Y; 00359 Z = ((12 - 3 * ul - 20 * vl) / (4 * vl)) * Y; 00360 } 00361 00362 /** Converts nonlinear values to linear values. \n 00363 * We use the sRGB transfer function. sRGB uses ITU-R 709 primaries and D65 white point. \n 00364 \verbatim 00365 0 <= l <= 1 ; 0 <= v <= 1 00366 00367 if (v < 0.03928) 00368 l = v / 12.92 00369 else 00370 l = pow((v + 0.055) / 1.055, 2.4) 00371 \endverbatim 00372 * \ingroup color */ 00373 inline float imColorTransfer2Linear(const float& nonlinear_value) 00374 { 00375 if (nonlinear_value < 0.03928f) 00376 return nonlinear_value / 12.92f; 00377 else 00378 return powf((nonlinear_value + 0.055f) / 1.055f, 2.4f); 00379 } 00380 00381 /** Converts linear values to nonlinear values. \n 00382 * We use the sRGB transfer function. sRGB uses ITU-R 709 primaries and D65 white point. \n 00383 \verbatim 00384 0 <= l <= 1 ; 0 <= v <= 1 00385 00386 if (l < 0.0031308) 00387 v = 12.92 * l 00388 else 00389 v = 1.055 * pow(l, 1/2.4) - 0.055 00390 \endverbatim 00391 * \ingroup color */ 00392 inline float imColorTransfer2Nonlinear(const float& value) 00393 { 00394 if (value < 0.0031308f) 00395 return 12.92f * value; 00396 else 00397 return 1.055f * powf(value, 1.0f/2.4f) - 0.055f; 00398 } 00399 00400 /** Converts RGB (linear) to R'G'B' (nonlinear). 00401 * \ingroup color */ 00402 inline void imColorRGB2RGBNonlinear(const float RL, const float GL, const float BL, 00403 float& R, float& G, float& B) 00404 { 00405 R = imColorTransfer2Nonlinear(RL); 00406 G = imColorTransfer2Nonlinear(GL); 00407 B = imColorTransfer2Nonlinear(BL); 00408 } 00409 00410 /** Converts R'G'B' to Y' (all nonlinear). \n 00411 \verbatim 00412 Y' = 0.299 *R' + 0.587 *G' + 0.114 *B' 00413 \endverbatim 00414 * \ingroup color */ 00415 template <class T> 00416 inline T imColorRGB2Luma(const T R, const T G, const T B) 00417 { 00418 return (T)((299 * R + 587 * G + 114 * B) / 1000); 00419 } 00420 00421 /** Converts Luminance (CIE Y) to Lightness (CIE L*) (all linear). \n 00422 * The white point is D65. 00423 \verbatim 00424 0 <= Y <= 1 ; 0 <= L* <= 1 00425 00426 Y = Y / 1.0 (for D65) 00427 if (Y > 0.008856) 00428 fY = pow(Y, 1/3) 00429 else 00430 fY = 7.787 * Y + 0.16/1.16 00431 L = 1.16 * fY - 0.16 00432 \endverbatim 00433 * \ingroup color */ 00434 inline float imColorLuminance2Lightness(const float& Y) 00435 { 00436 return 1.16f * IM_FWLAB(Y) - 0.16f; 00437 } 00438 00439 /** Converts Lightness (CIE L*) to Luminance (CIE Y) (all linear). \n 00440 * The white point is D65. 00441 \verbatim 00442 0 <= Y <= 1 ; 0 <= L* <= 1 00443 00444 fY = (L + 0.16)/1.16 00445 if (fY > 0.20689) 00446 Y = pow(fY, 3) 00447 else 00448 Y = 0.1284 * (fY - 0.16/1.16) 00449 Y = Y * 1.0 (for D65) 00450 \endverbatim 00451 * \ingroup color */ 00452 inline float imColorLightness2Luminance(const float& L) 00453 { 00454 float fY = (L + 0.16f) / 1.16f; 00455 return IM_GWLAB(fY); 00456 } 00457 00458 #undef IM_FWLAB 00459 #undef IM_GWLAB 00460 #undef IM_CROPL 00461 #undef IM_CROPC 00462 00463 #endif