Color Manipulation
[Utilities]
Detailed Description
- Functions to convert from one color space to another, and color gammut utilities.
- See im_color.h
Some Color Science
- Y is luminance, a linear-light quantity. It is directly proportional to physical intensity weighted by the spectral sensitivity of human vision.
- L* is lightness, a nonlinear luminance that aproximates the perception of brightness. It is nearly perceptual uniform. It has a range of 0 to 100.
- Y' is luma, a nonlinear luminance that aproximates lightness.
- Brightness is a visual sensation according to which an area apears to exhibit more or less light. It is a subjective quantity and can not be measured.
- One unit of euclidian distante in CIE L*u*v* or CIE L*a*b* corresponds roughly to a just-noticeable difference (JND) of color.
ChromaUV = sqrt(u*u + v*v) HueUV = atan2(v, u) SaturationUV = ChromaUV / L (called psychometric saturation) (the same can be calculated for Lab)
- IEC 61966-2.1 Default RGB colour space - sRGB
- ITU-R Recommendation BT.709 (D65 white point).
- D65 White Point (X,Y,Z) = (0.9505 1.0000 1.0890)
- Documentation extracted from Charles Poynton - Digital Video and HDTV - Morgan Kaufmann - 2003.
Links
- www.color.org - ICC
- www.srgb.com - sRGB
- www.poynton.com - Charles Poynton
- www.littlecms.com - A free Color Management System (use this if you need precise color conversions)
Color Component Intervals
- All the color components are stored in the 0-max interval, even the signed ones.
Here are the pre-defined intervals for each data type. These values are used for standard color conversion. You should normalize data before converting betwwen color spaces.
byte [0,255] or [-128,+127] (1 byte) ushort [0,65535] or [-32768,+32767] (2 bytes) int [0,16777215] or [-8388608,+8388607] (3 bytes) float [0,1] or [-0.5,+0.5] (4 bytes)
Modules | |
group | HSI Color Coordinate System Conversions |
Functions | |
float | imColorZero (int data_type) |
int | imColorMax (int data_type) |
template<class T> | |
T | imColorQuantize (const float &value, const T &max) |
template<class T> | |
float | imColorReconstruct (const T &value, const T &max) |
template<class T> | |
void | imColorYCbCr2RGB (const T Y, const T Cb, const T Cr, T &R, T &G, T &B, const T &zero, const T &max) |
template<class T> | |
void | imColorRGB2YCbCr (const T R, const T G, const T B, T &Y, T &Cb, T &Cr, const T &zero) |
template<class T> | |
void | imColorCMYK2RGB (const T C, const T M, const T Y, const T K, T &R, T &G, T &B, const T &max) |
template<class T> | |
void | imColorXYZ2RGB (const T X, const T Y, const T Z, T &R, T &G, T &B, const T &max) |
template<class T> | |
void | imColorRGB2XYZ (const T R, const T G, const T B, T &X, T &Y, T &Z) |
void | imColorXYZ2Lab (const float X, const float Y, const float Z, float &L, float &a, float &b) |
void | imColorLab2XYZ (const float L, const float a, const float b, float &X, float &Y, float &Z) |
void | imColorXYZ2Luv (const float X, const float Y, const float Z, float &L, float &u, float &v) |
void | imColorLuv2XYZ (const float L, const float u, const float v, float &X, float &Y, float &Z) |
float | imColorTransfer2Linear (const float &nonlinear_value) |
float | imColorTransfer2Nonlinear (const float &value) |
void | imColorRGB2RGBNonlinear (const float RL, const float GL, const float BL, float &R, float &G, float &B) |
template<class T> | |
T | imColorRGB2Luma (const T R, const T G, const T B) |
float | imColorLuminance2Lightness (const float &Y) |
float | imColorLightness2Luminance (const float &L) |
Function Documentation
|
Returns the zero value for color conversion porpouses. 00078 { 00079 float zero[] = {128.0f, 32768.0f, 8388608.0f, 0.5f}; 00080 return zero[data_type]; 00081 }
|
|
Returns the maximum value for color conversion porpouses. 00086 { 00087 int max[] = {255, 65535, 16777215, 1}; 00088 return max[data_type]; 00089 }
|
|
Quantize 0-1 values into 0-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 }
|
|
Reconstruct 0-max values into 0-1. 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 }
|
|
Converts Y'CbCr to R'G'B' (all nonlinear). 0 <= Y <= 1 ; -0.5 <= CbCr <= 0.5 ; 0 <= RGB <= 1 R'= Y' + 0.000 *Cb + 1.402 *Cr G'= Y' - 0.344 *Cb - 0.714 *Cr B'= Y' + 1.772 *Cb + 0.000 *Cr 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 }
|
|
Converts R'G'B' to Y'CbCr (all nonlinear). 0 <= Y <= 1 ; -0.5 <= CbCr <= 0.5 ; 0 <= RGB <= 1 Y' = 0.299 *R' + 0.587 *G' + 0.114 *B' Cb = -0.169 *R' - 0.331 *G' + 0.500 *B' Cr = 0.500 *R' - 0.419 *G' - 0.081 *B' 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 }
|
|
Converts C'M'Y'K' to R'G'B' (all nonlinear). 0 <= CMYK <= 1 ; 0 <= RGB <= 1 R = (1 - K) * (1 - C) G = (1 - K) * (1 - M) B = (1 - K) * (1 - Y) 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 }
|
|
Converts CIE XYZ to Rec 709 RGB (all linear). 0 <= XYZ <= 1 ; 0 <= RGB <= 1 R = 3.2406 *X - 1.5372 *Y - 0.4986 *Z G = -0.9689 *X + 1.8758 *Y + 0.0415 *Z B = 0.0557 *X - 0.2040 *Y + 1.0570 *Z 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 }
|
|
Converts Rec 709 RGB to CIE XYZ (all linear). 0 <= XYZ <= 1 ; 0 <= RGB <= 1 X = 0.4124 *R + 0.3576 *G + 0.1805 *B Y = 0.2126 *R + 0.7152 *G + 0.0722 *B Z = 0.0193 *R + 0.1192 *G + 0.9505 *B 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 }
|
|
Converts CIE XYZ (linear) to CIE L*a*b* (nonlinear). 0 <= L <= 1 ; -0.5 <= ab <= +0.5 ; 0 <= XYZ <= 1 if (t > 0.008856) f(t) = pow(t, 1/3) else f(t) = 7.787*t + 16/116 fX = f(X / Xn) fY = f(Y / Yn) fZ = f(Z / Zn) L = 1.16 * fY - 0.16 a = 2.5 * (fX - fY) b = (fY - fZ) 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 }
|
|
Converts CIE L*a*b* (nonlinear) to CIE XYZ (linear). 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 }
|
|
Converts CIE XYZ (linear) to CIE L*u*v* (nonlinear). 0 <= L <= 1 ; -1 <= uv <= +1 ; 0 <= XYZ <= 1 Y = Y / 1.0 (for D65) if (Y > 0.008856) fY = pow(Y, 1/3) else fY = 7.787 * Y + 0.16/1.16 L = 1.16 * fY - 0.16 U(x, y, z) = (4 * x)/(x + 15 * y + 3 * z) V(x, y, z) = (9 * x)/(x + 15 * y + 3 * z) un = U(Xn, Yn, Zn) = 0.1978 (for D65) vn = V(Xn, Yn, Zn) = 0.4683 (for D65) fu = U(X, Y, Z) fv = V(X, Y, Z) u = 13 * L * (fu - un) v = 13 * L * (fv - vn) 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 }
|
|
Converts CIE L*u*v* (nonlinear) to CIE XYZ (linear). 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 }
|
|
Converts nonlinear values to linear values. 0 <= l <= 1 ; 0 <= v <= 1 if (v < 0.03928) l = v / 12.92 else l = pow((v + 0.055) / 1.055, 2.4) 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 }
|
|
Converts linear values to nonlinear values. 0 <= l <= 1 ; 0 <= v <= 1 if (l < 0.0031308) v = 12.92 * l else v = 1.055 * pow(l, 1/2.4) - 0.055 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 }
|
|
Converts RGB (linear) to R'G'B' (nonlinear). 00405 { 00406 R = imColorTransfer2Nonlinear(RL); 00407 G = imColorTransfer2Nonlinear(GL); 00408 B = imColorTransfer2Nonlinear(BL); 00409 }
|
|
Converts R'G'B' to Y' (all nonlinear). Y' = 0.299 *R' + 0.587 *G' + 0.114 *B' 00418 {
00419 return (T)((299 * R + 587 * G + 114 * B) / 1000);
00420 }
|
|
Converts Luminance (CIE Y) to Lightness (CIE L*) (all linear). 0 <= Y <= 1 ; 0 <= L* <= 1 Y = Y / 1.0 (for D65) if (Y > 0.008856) fY = pow(Y, 1/3) else fY = 7.787 * Y + 0.16/1.16 L = 1.16 * fY - 0.16 00436 {
00437 return 1.16f * IM_FWLAB(Y) - 0.16f;
00438 }
|
|
Converts Lightness (CIE L*) to Luminance (CIE Y) (all linear). 0 <= Y <= 1 ; 0 <= L* <= 1 fY = (L + 0.16)/1.16 if (fY > 0.20689) Y = pow(fY, 3) else Y = 0.1284 * (fY - 0.16/1.16) Y = Y * 1.0 (for D65) 00454 { 00455 float fY = (L + 0.16f) / 1.16f; 00456 return IM_GWLAB(fY); 00457 }
|