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