 # Thread: RGB, XYZ, and Lab conversions

1. ## 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  Reply With Quote

2. ## 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```  Reply With Quote

3. ## Re: RGB, XYZ, and Lab conversions Originally Posted by Tanner_H 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.  Reply With Quote

4. ## Re: RGB, XYZ, and Lab conversions Originally Posted by Ben321 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. Originally Posted by Ben321 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.) Originally Posted by Ben321 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!   Reply With Quote

5. ## 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  Reply With Quote

6. ## Re: RGB, XYZ, and Lab conversions Originally Posted by reexre 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```  Reply With Quote

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•

Featured

Click Here to Expand Forum to Full Width