As seen in the Colorimetry page, it's important to understand the difference between Absolute (or device-independent) Color Space (.e.g. CIEXYZ, CIExyY, CIELAB) and device-dependent colors spaces (e.g. RGB, HSL, HSB, HSV).
For example, it makes sense to convert from a device-dependent RGB space to a HSL space since, even though they are both device-dependent, they are defined in the same "dependent space".
Also, it makes sense to convert from 2 device-independent spaces like CIEXYZ and CIELAB.
Most importantly, the conversions between device-dependent color spaces and device-independent color spaces must always be accompanied by a Color Profile that appropriately describes the dependence to the device.
Contents
Device-Dependent Color Space Conversions
Here, we will list the different conversions between device-dependent color spaces.
RGB / HSL
(Source: http://www.easyrgb.com)
RGB → HSL
- Input: RGB in [0,1]
- Output: HSL in [0,1]
var_Min = min( R, G, B ) // Min. value of RGB var_Max = max( R, G, B ) // Max. value of RGB del_Max = var_Max - var_Min // Delta RGB value L = ( var_Max + var_Min ) / 2 if ( del_Max == 0 ) // This is a gray, no chroma... { H = 0 // HSL results from 0 to 1 S = 0 } else // Chromatic data... { if ( L < 0.5 ) S = del_Max / ( var_Max + var_Min ) else S = del_Max / ( 2 - var_Max - var_Min ) del_R = ( ( ( var_Max - var_R ) / 6 ) + ( del_Max / 2 ) ) / del_Max del_G = ( ( ( var_Max - var_G ) / 6 ) + ( del_Max / 2 ) ) / del_Max del_B = ( ( ( var_Max - var_B ) / 6 ) + ( del_Max / 2 ) ) / del_Max if ( var_R == var_Max ) H = del_B - del_G else if ( var_G == var_Max ) H = ( 1 / 3 ) + del_R - del_B else if ( var_B == var_Max ) H = ( 2 / 3 ) + del_G - del_R if ( H < 0 ) H += 1 if ( H > 1 ) H -= 1 }
HSL → RGB
- Input: HSL in [0,1]
- Output: RGB in [0,1]
if ( S == 0 ) { (R,G,B) = L; } else { if ( L < 0.5 ) var_2 = L * ( 1 + S ) else var_2 = ( L + S ) - ( S * L ) var_1 = 2 * L - var_2 R = Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) ) G = Hue_2_RGB( var_1, var_2, H ) B = Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) ) } Hue_2_RGB( v1, v2, vH ) { if ( vH < 0 ) vH += 1 if ( vH > 1 ) vH -= 1 if ( ( 6 * vH ) < 1 ) return ( v1 + ( v2 - v1 ) * 6 * vH ) if ( ( 2 * vH ) < 1 ) return ( v2 ) if ( ( 3 * vH ) < 2 ) return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6 ) return ( v1 ) }
RGB / HSV
(Source: http://www.easyrgb.com)
RGB → HSV
- Input: RGB in [0,1]
- Output: HSV in [0,1]
var_Min = min( R, G, B ) // Min. value of RGB var_Max = max( R, G, B ) // Max. value of RGB del_Max = var_Max - var_Min // Delta RGB value V = var_Max if ( del_Max == 0 ) // This is a gray, no chroma... { H = 0 // HSV results from 0 to 1 S = 0 } else // Chromatic data... { S = del_Max / var_Max del_R = ( ( ( var_Max - var_R ) / 6 ) + ( del_Max / 2 ) ) / del_Max del_G = ( ( ( var_Max - var_G ) / 6 ) + ( del_Max / 2 ) ) / del_Max del_B = ( ( ( var_Max - var_B ) / 6 ) + ( del_Max / 2 ) ) / del_Max if ( var_R == var_Max ) H = del_B - del_G else if ( var_G == var_Max ) H = ( 1 / 3 ) + del_R - del_B else if ( var_B == var_Max ) H = ( 2 / 3 ) + del_G - del_R if ( H < 0 ) H += 1 if ( H > 1 ) H -= 1 }
HSV → RGB
- Input: HSV in [0,1]
- Output: RGB in [0,1]
if ( S == 0 ) // HSV from 0 to 1 { (R,G,B) = V } else { var_h = H * 6 if ( var_h == 6 ) var_h = 0 // H must be < 1 var_i = int( var_h ) // Or ... var_i = floor( var_h ) var_1 = V * ( 1 - S ) var_2 = V * ( 1 - S * ( var_h - var_i ) ) var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) ) if ( var_i == 0 ) { R = V ; G = var_3 ; B = var_1 } else if ( var_i == 1 ) { R = var_2 ; G = V ; B = var_1 } else if ( var_i == 2 ) { R = var_1 ; G = V ; B = var_3 } else if ( var_i == 3 ) { R = var_1 ; G = var_2 ; B = V } else if ( var_i == 4 ) { R = var_3 ; G = var_1 ; B = V } else { R = V ; G = var_1 ; B = var_2 } }
Device-Independent Color Space Conversions
Here, we will list the different conversions between device-independent color spaces.
XYZ / xyY
(Source: http://www.easyrgb.com)
XYZ → xyY
- Input: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
- Output: xyY in [0,1]
Y = Y x = X / ( X + Y + Z ) y = Y / ( X + Y + Z )
xyY → XYZ
- Input: xyY in [0,1]
- Output: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
X = x * ( Y / y ) Y = Y Z = ( 1 - x - y ) * ( Y / y )
XYZ / Lab
(Source: http://www.easyrgb.com)
Remember that CIE L*a*b* is device-independent but needs a white point reference nevertheless.
Here, the D65 illuminant is used.
XYZ → L*a*b*
- Input: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
- Output:
- L* in [0,100]
- a*, b* in [0,?]
var_X = X / ref_X // ref_X = 0.95047 Observer= 2°, Illuminant= D65 var_Y = Y / ref_Y // ref_Y = 1.000 var_Z = Z / ref_Z // ref_Z = 1.08883 if ( var_X > 0.008856 ) var_X = var_X ^ ( 1/3 ) else var_X = ( 7.787 * var_X ) + ( 16 / 116 ) if ( var_Y > 0.008856 ) var_Y = var_Y ^ ( 1/3 ) else var_Y = ( 7.787 * var_Y ) + ( 16 / 116 ) if ( var_Z > 0.008856 ) var_Z = var_Z ^ ( 1/3 ) else var_Z = ( 7.787 * var_Z ) + ( 16 / 116 ) CIE-L* = ( 116 * var_Y ) - 16 CIE-a* = 500 * ( var_X - var_Y ) CIE-b* = 200 * ( var_Y - var_Z )
L*a*b* → XYZ
- Input:
- L* in [0,100]
- a*, b* in [0,?]
- Output: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
var_Y = ( CIE-L* + 16 ) / 116 var_X = CIE-a* / 500 + var_Y var_Z = var_Y - CIE-b* / 200 if ( var_Y^3 > 0.008856 ) var_Y = var_Y^3 else var_Y = ( var_Y - 16 / 116 ) / 7.787 if ( var_X^3 > 0.008856 ) var_X = var_X^3 else var_X = ( var_X - 16 / 116 ) / 7.787 if ( var_Z^3 > 0.008856 ) var_Z = var_Z^3 else var_Z = ( var_Z - 16 / 116 ) / 7.787 X = ref_X * var_X // ref_X = 0.95047 Observer= 2°, Illuminant= D65 Y = ref_Y * var_Y // ref_Y = 1.00000 Z = ref_Z * var_Z // ref_Z = 1.08883
Device-dependent / Device-independent Color Space Conversions
RGB (in sRGB) / XYZ
(Source: http://www.easyrgb.com)
Please refer to the sRGB color profile specification to understand the pseudo-gamma correction in the following routines.
RGB → XYZ
- Input: RGB in [0,1] with sRGB gamma profile
- Output: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
// Apply gamma correction (i.e. conversion to linear-space) if ( R > 0.04045 ) R = ( ( R + 0.055 ) / 1.055 ) ^ 2.4 else R = R / 12.92 if ( G > 0.04045 ) G = ( ( G + 0.055 ) / 1.055 ) ^ 2.4 else G = G / 12.92 if ( B > 0.04045 ) B = ( ( B + 0.055 ) / 1.055 ) ^ 2.4 else B = B / 12.92 // Observer. = 2°, Illuminant = D65 X = R * 0.4124 + G * 0.3576 + B * 0.1805 Y = R * 0.2126 + G * 0.7152 + B * 0.0722 Z = R * 0.0193 + G * 0.1192 + B * 0.9505
XYZ → RGB
- Input: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
- Output: RGB in [0,1] with sRGB gamma profile
R = X * 3.2406 + Y * -1.5372 + Z * -0.4986 G = X * -0.9689 + Y * 1.8758 + Z * 0.0415 B = X * 0.0557 + Y * -0.2040 + Z * 1.0570 if ( R > 0.0031308 ) R = 1.055 * ( R ^ ( 1 / 2.4 ) ) - 0.055 else R = 12.92 * R if ( G > 0.0031308 ) G = 1.055 * ( G ^ ( 1 / 2.4 ) ) - 0.055 else G = 12.92 * G if ( B > 0.0031308 ) B = 1.055 * ( B ^ ( 1 / 2.4 ) ) - 0.055 else B = 12.92 * B
RGB (in Adobe RGB) / XYZ
(Source: http://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf)
RGB → XYZ
- Input: RGB in [0,1] with Adobe RGB gamma profile
- Output: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
// Gamma correction of ~2.2 R = R ^ 2.19921875 G = G ^ 2.19921875 B = B ^ 2.19921875 // Observer. = 2°, Illuminant = D65 X = 0.57667 * R + 0.18556 * G + 0.18823 * B Y = 0.29734 * R + 0.62736 * G + 0.07529 * B Z = 0.02703 * R + 0.07069 * G + 0.99134 * B
XYZ → RGB
- Input: (Observer. = 2°, Illuminant = D65)
- X in [0, 0.95047]
- Y in [0, 1.00000]
- Z in [0, 1.08883]
- Output: RGB in [0,1] with Adobe RGB gamma profile
R = 2.04159 * X - 0.56501 * Y - 0.34473 * Z G = -0.96924 * X + 1.87597 * Y + 0.04156 * Z B = 0.01344 * X - 0.11836 * Y + 1.01517 * Z // Gamma correction R = R ^ (1.0 / 2.19921875) G = G ^ (1.0 / 2.19921875) B = B ^ (1.0 / 2.19921875)