 # RGB, XYZ, and Lab conversions

Printable View

• May 1st, 2014, 01:16 AM
Ben321
RGB, XYZ, and Lab conversions
This program converts images between RGB, XYZ, and Lab formats. It's quite a large program (due to the 2 huge lookup tables for RGB/Lab conversion), so instead of posting it as an attachment, I've uploaded it to Mediafire, and have posted the download link here.

https://www.mediafire.com/?vdx3uy1z31g21as
• May 1st, 2014, 08:54 AM
Tanner_H
Re: RGB, XYZ, and Lab conversions
Hi Ben. Thanks for sharing. You weren't joking about the download file being huge!

There is a much easier way to do RGB / XYZ / Lab conversions. Code below (adopted from easyRGB.com). Note that certain assumptions have to be made when converting between RGB (which is just a color model) and an actual color space, like Lab. In these functions, sRGB and 6500k color temperature are assumed, as noted in the comments.

Code:

```'This function is just a thin wrapper to RGBtoXYZ and XYZtoLAB.  There is no direct conversion from RGB to CieLAB. Public Sub RGBtoLAB(ByVal r As Long, ByVal g As Long, ByVal b As Long, ByRef labL As Double, ByRef labA As Double, ByRef labB As Double)     Dim x As Double, y As Double, z As Double     RGBtoXYZ r, g, b, x, y, z     XYZtoLab x, y, z, labL, labA, labB End Sub 'Convert RGB to XYZ space, using an sRGB conversion and the assumption of a D65 (e.g. color temperature of 6500k) illuminant ' Formula adopted from http://www.easyrgb.com/index.php?X=MATH&H=02#text2 Public Sub RGBtoXYZ(ByVal r As Long, ByVal g As Long, ByVal b As Long, ByRef x As Double, ByRef y As Double, ByRef z As Double)     'Normalize RGB to [0, 1]     Dim rFloat As Double, gFloat As Double, bFloat As Double     rFloat = r / 255     gFloat = g / 255     bFloat = b / 255         'Convert RGB values to the sRGB color space     If rFloat > 0.04045 Then         rFloat = ((rFloat + 0.055) / (1.055)) ^ 2.2     Else         rFloat = rFloat / 12.92     End If         If gFloat > 0.04045 Then         gFloat = ((gFloat + 0.055) / (1.055)) ^ 2.2     Else         gFloat = gFloat / 12.92     End If         If bFloat > 0.04045 Then         bFloat = ((bFloat + 0.055) / (1.055)) ^ 2.2     Else         bFloat = bFloat / 12.92     End If         'Calculate XYZ using D65 correction     x = rFloat * 0.4124 + gFloat * 0.3576 + bFloat * 0.1805     y = rFloat * 0.2126 + gFloat * 0.7152 + bFloat * 0.0722     z = rFloat * 0.0193 + gFloat * 0.1192 + bFloat * 0.9505     End Sub 'Convert an XYZ color to CIELab.  As with the original XYZ calculation, D65 is assumed. ' Formula adopted from http://www.easyrgb.com/index.php?X=MATH&H=07#text7, with minor changes by me (not re-applying D65 values until after '  fXYZ has been calculated) Public Sub XYZtoLab(ByVal x As Double, ByVal y As Double, ByVal z As Double, ByRef l As Double, ByRef a As Double, ByRef b As Double)     l = 116 * fXYZ(y) - 16     a = 500 * (fXYZ(x / 0.9505) - fXYZ(y))     b = 200 * (fXYZ(y) - fXYZ(z / 1.089)) End Sub Private Function fXYZ(ByVal t As Double) As Double     If t > 0.008856 Then         fXYZ = t ^ (1 / 3)     Else         fXYZ = (7.787 * t) + (16 / 116)     End If End Function```
• May 1st, 2014, 02:29 PM
Ben321
Re: RGB, XYZ, and Lab conversions
Quote:

Hi Ben. Thanks for sharing. You weren't joking about the download file being huge!

There is a much easier way to do RGB / XYZ / Lab conversions. Code below (adopted from easyRGB.com). Note that certain assumptions have to be made when converting between RGB (which is just a color model) and an actual color space, like Lab. In these functions, sRGB and 6500k color temperature are assumed, as noted in the comments.

Code:

```'This function is just a thin wrapper to RGBtoXYZ and XYZtoLAB.  There is no direct conversion from RGB to CieLAB. Public Sub RGBtoLAB(ByVal r As Long, ByVal g As Long, ByVal b As Long, ByRef labL As Double, ByRef labA As Double, ByRef labB As Double)     Dim x As Double, y As Double, z As Double     RGBtoXYZ r, g, b, x, y, z     XYZtoLab x, y, z, labL, labA, labB End Sub 'Convert RGB to XYZ space, using an sRGB conversion and the assumption of a D65 (e.g. color temperature of 6500k) illuminant ' Formula adopted from http://www.easyrgb.com/index.php?X=MATH&H=02#text2 Public Sub RGBtoXYZ(ByVal r As Long, ByVal g As Long, ByVal b As Long, ByRef x As Double, ByRef y As Double, ByRef z As Double)     'Normalize RGB to [0, 1]     Dim rFloat As Double, gFloat As Double, bFloat As Double     rFloat = r / 255     gFloat = g / 255     bFloat = b / 255         'Convert RGB values to the sRGB color space     If rFloat > 0.04045 Then         rFloat = ((rFloat + 0.055) / (1.055)) ^ 2.2     Else         rFloat = rFloat / 12.92     End If         If gFloat > 0.04045 Then         gFloat = ((gFloat + 0.055) / (1.055)) ^ 2.2     Else         gFloat = gFloat / 12.92     End If         If bFloat > 0.04045 Then         bFloat = ((bFloat + 0.055) / (1.055)) ^ 2.2     Else         bFloat = bFloat / 12.92     End If         'Calculate XYZ using D65 correction     x = rFloat * 0.4124 + gFloat * 0.3576 + bFloat * 0.1805     y = rFloat * 0.2126 + gFloat * 0.7152 + bFloat * 0.0722     z = rFloat * 0.0193 + gFloat * 0.1192 + bFloat * 0.9505     End Sub 'Convert an XYZ color to CIELab.  As with the original XYZ calculation, D65 is assumed. ' Formula adopted from http://www.easyrgb.com/index.php?X=MATH&H=07#text7, with minor changes by me (not re-applying D65 values until after '  fXYZ has been calculated) Public Sub XYZtoLab(ByVal x As Double, ByVal y As Double, ByVal z As Double, ByRef l As Double, ByRef a As Double, ByRef b As Double)     l = 116 * fXYZ(y) - 16     a = 500 * (fXYZ(x / 0.9505) - fXYZ(y))     b = 200 * (fXYZ(y) - fXYZ(z / 1.089)) End Sub Private Function fXYZ(ByVal t As Double) As Double     If t > 0.008856 Then         fXYZ = t ^ (1 / 3)     Else         fXYZ = (7.787 * t) + (16 / 116)     End If End Function```

Thanks for sharing that. I haven't tried it yet though.
By the way, you are missing the inverse transformations. Also, is this actually faster than the techniques I used in my program, when used to convert an entire image? It seems that it would be slower, since there are a lot more operations being done in your program, than in the one I wrote. Also, why do you pre-process the RGB channels before applying the RGB to XYZ transformation matrix? I'm guessing it has something to do with linearity of the RGB values when compared to actual physical brightness levels of the pixels, and compensating for a lack of linearity. However for the sake of simplifying the operation in my program when converting between RGB and XYZ, I assume that the RGB values are already representing linear brightnesses of the 3 color channels and use them as-is (no pre-processing) as inputs to the transformation matrix.
• May 1st, 2014, 02:55 PM
Tanner_H
Re: RGB, XYZ, and Lab conversions
Quote:

By the way, you are missing the inverse transformations.

Yep, I don't have those on hand, though they're trivially created from the formulas at this link.

Quote:

Also, is this actually faster than the techniques I used in my program, when used to convert an entire image? It seems that it would be slower, since there are a lot more operations being done in your program, than in the one I wrote.

You'd have to test, but I wouldn't underestimate the overhead of loading those massive look-up tables into RAM, let alone the cost of shipping them with your project. The formulas I shared could probably be optimized further, but as-is they're fast enough for real-time conversion on screen-sized images. (I use them to render a green screen effect in real-time, and I haven't had any speed problems on modern PCs.)

Quote:

Also, why do you pre-process the RGB channels before applying the RGB to XYZ transformation matrix? I'm guessing it has something to do with linearity of the RGB values when compared to actual physical brightness levels of the pixels, and compensating for a lack of linearity. However for the sake of simplifying the operation in my program when converting between RGB and XYZ, I assume that the RGB values are already representing linear brightnesses of the 3 color channels and use them as-is (no pre-processing) as inputs to the transformation matrix.

As you probably know, RGB is just a color model, not a color space. A color like RGB(255, 0, 0) will look completely different on different devices.

Because CieLAB is an actual color space (that absolutely describes color), to get any use from it, you first have to convert your RGB data into an absolute RGB color space. Which RGB color space is up to you; sRGB is simple and straightforward, and your original RGB data was mostly likely designed against sRGB anyway. (More about sRGB here.) Adobe RGB or ProPhoto RGB are other common RGB spaces, but they're a lot more work to use.

Since the primary purpose of the CieLAB space is accurate representation of color, it's probably not a good idea to convert raw 24bpp RGB data directly through XYZ into Lab, without first performing a proper RGB color space conversion. You could save some processing time, certainly, but your Lab values wouldn't be accurate... which defeats the reason for using Lab in the first place, I think! ;)
• May 7th, 2014, 03:54 PM
reexre
Re: RGB, XYZ, and Lab conversions
hi
I use this:
Code:

```Option Explicit 'http://www.easyrgb.com/index.php?X=MATH&H=02#text2 'MULTIPLICATION IS FASTER THAN DIVISION, 'SO, SETUP SOME CONSTANTS: Private Const INV255 As Double = 1 / 255 Private Const INV1p055 As Double = 1 / 1.055 Private Const INV12p92 As Double = 1 / 12.92 Private Const INV3 As Double = 1 / 3 Private Const kX As Double = 100 / 95.047 Private Const kY As Double = 100 / 100 Private Const kZ As Double = 100 / 108.883 Private Const INVkX As Double = 95.047 / 100 Private Const INVkY As Double = 100 / 100 Private Const INVkZ As Double = 108.883 / 100 Private Const c16d116 As Double = 16 / 116 Private Const INV7p787 As Double = 1 / 7.787 Private Const INV2p4 As Double = 1 / 2.4 Private Const INV116 As Double = 1 / 116 Private Const INV200 As Double = 1 / 200 Private Const INV500 As Double = 1 / 500 Private Const INV2p55 As Double = 1 / 2.55 Private Const INV1p275 As Double = 1 / 1.275 Public Sub RGB2lab(R As Double, G As Double, B As Double, _                   ByRef cL As Double, ByRef cA As Double, ByRef cB As Double)     Dim RR    As Double     Dim GG    As Double     Dim BB    As Double     Dim X      As Double     Dim Y      As Double     Dim Z      As Double     RR = R * INV255     GG = G * INV255     BB = B * INV255     If (RR > 0.04045) Then         RR = ((RR + 0.055) * INV1p055) ^ 2.4     Else         RR = RR * INV12p92     End If     If (GG > 0.04045) Then         GG = ((GG + 0.055) * INV1p055) ^ 2.4     Else         GG = GG * INV12p92     End If     If (BB > 0.04045) Then         BB = ((BB + 0.055) * INV1p055) ^ 2.4     Else         BB = BB * INV12p92     End If     'RR = RR * 100     'GG = GG * 100     'BB = BB * 100     '//Observer. = 2°, Illuminant = D65     X = RR * 0.4124 + GG * 0.3576 + BB * 0.1805     Y = RR * 0.2126 + GG * 0.7152 + BB * 0.0722     Z = RR * 0.0193 + GG * 0.1192 + BB * 0.9505     '-----------------------------------------------------------     'X = X / ref_X          '//ref_X =  95.047  Observer= 2°, Illuminant= D65     'Y = Y / ref_Y          '//ref_Y = 100.000     'Z = Z / ref_Z          '//ref_Z = 108.883     X = X * kX     Y = Y * kY     Z = Z * kZ     If (X > 0.008856) Then X = X ^ (INV3) Else: X = (7.787 * X) + (c16d116)     If (Y > 0.008856) Then Y = Y ^ (INV3) Else: Y = (7.787 * Y) + (c16d116)     If (Z > 0.008856) Then Z = Z ^ (INV3) Else: Z = (7.787 * Z) + (c16d116)     cL = (116 * Y) - 16                '0 to 100     cA = 500 * (X - Y)                'Circa -100 a 100     cB = 200 * (Y - Z)     'Debug.Print cb     'Transfrom output Ranges (initialy from 0 to 100  &  -100 to 100 )     'to [from 0 to 255]     cL = cL * 2.55     cA = 127.5 + cA * 1.275     cB = 127.5 + cB * 1.275     '-----------------------     If cL > 255 Then cL = 255     If cL < 0 Then cL = 0     If cA > 255 Then cA = 255     If cB > 255 Then cB = 255     If cB < 0 Then cB = 0     If cA < 0 Then cA = 0 End Sub Public Sub LAB2RGB(ByVal cL As Double, ByVal cA As Double, ByVal cB As Double, _                   ByRef R As Double, ByRef G As Double, ByRef B As Double)     Dim X      As Double     Dim Y      As Double     Dim Z      As Double     Dim X3    As Double     Dim Y3    As Double     Dim Z3    As Double         'Check input Range     'If cL > 255 Then Stop: cL = 255     'If cL < 0 Then Stop: cL = 0     'If cA > 255 Then Stop: cA = 255     'If cA < 0 Then Stop: cA = 0     'If cb > 255 Then Stop: cb = 255     'If cb < 0 Then Stop: cb = 0     'Reset input ranges     'from 0 to 255     'to [from 0 to 100] & [from -100 to 100]     cL = cL * INV2p55    '/2.55     cA = (cA - 127.5) * INV1p275    '/ 1.275     cB = (cB - 127.5) * INV1p275    '/ 1.275     '-------     Y = (cL + 16) * INV116     X = cA * INV500 + Y     Z = Y - cB * INV200     X3 = X * X * X     Y3 = Y * Y * Y     Z3 = Z * Z * Z     If (Y3 > 0.008856) Then Y = Y3 Else: Y = (Y - c16d116) * INV7p787     If (X3 > 0.008856) Then X = X3 Else: X = (X - c16d116) * INV7p787     If (Z3 > 0.008856) Then Z = Z3 Else: Z = (Z - c16d116) * INV7p787     'X = ref_X * x    //ref_X =  95.047    Observer= 2°, Illuminant= D65     'Y = ref_Y * y    //ref_Y = 100.000     'Z = ref_Z * z    //ref_Z = 108.883     X = X * INVkX     Y = Y * INVkY     Z = Z * INVkZ     '--------------------------------------------------------------------------     'x,y,z 0 to 100     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.204 + Z * 1.057     If (R > 0.0031308) Then R = 1.055 * (R ^ INV2p4) - 0.055 Else: R = 12.92 * R     If (G > 0.0031308) Then G = 1.055 * (G ^ INV2p4) - 0.055 Else: G = 12.92 * G     If (B > 0.0031308) Then B = 1.055 * (B ^ INV2p4) - 0.055 Else: B = 12.92 * B     R = R * 255     G = G * 255     B = B * 255     If R > 255 Then R = 255     If G > 255 Then G = 255     If B > 255 Then B = 255     If R < 0 Then R = 0     If G < 0 Then G = 0     If B < 0 Then B = 0 End Sub```

this should / could be useful
• May 7th, 2014, 08:21 PM
Nightwalker83
Re: RGB, XYZ, and Lab conversions
Quote:

Private Const INVkX As Double = 95.047 / 100
Private Const INVkY As Double = 100 / 100
Private Const INVkZ As Double = 108.883 / 100

Instead of that why not just?

Code:

```Private Const INVkX As Double = .95047 Private Const INVkY As Double = 1 Private Const INVkZ As Double = 1.08883```