
May 1st, 2014, 01:16 AM
#1
Thread Starter
Fanatic Member
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
#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 reapplying 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
#3
Thread Starter
Fanatic Member
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 reapplying 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 preprocess 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 asis (no preprocessing) as inputs to the transformation matrix.
Last edited by Ben321; May 1st, 2014 at 02:37 PM.

May 1st, 2014, 02:55 PM
#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 lookup tables into RAM, let alone the cost of shipping them with your project. The formulas I shared could probably be optimized further, but asis they're fast enough for realtime conversion on screensized images. (I use them to render a green screen effect in realtime, and I haven't had any speed problems on modern PCs.)
Originally Posted by Ben321
Also, why do you preprocess 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 asis (no preprocessing) 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
#5
Hyperactive Member
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
Last edited by reexre; May 7th, 2014 at 04:37 PM.

May 7th, 2014, 08:21 PM
#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
when you quote a post could you please do it via the "Reply With Quote" button or if it multiple post click the "''+" button then "Reply With Quote" button.
If this thread is finished with please mark it "Resolved" by selecting "Mark thread resolved" from the "Thread tools" dropdown menu.
Please consider giving me some rep points if I help you a lot.
Please rate my post if you find it helpful!
Technology is a dangerous thing in the hands of an idiot! I am that idiot.
Posting Permissions
 You may not post new threads
 You may not post replies
 You may not post attachments
 You may not edit your posts

Forum Rules

Click Here to Expand Forum to Full Width
