Skip to content

Instantly share code, notes, and snippets.

@manojpandey
Created August 12, 2016 10:34
Show Gist options
  • Save manojpandey/f5ece715132c572c80421febebaf66ae to your computer and use it in GitHub Desktop.
Save manojpandey/f5ece715132c572c80421febebaf66ae to your computer and use it in GitHub Desktop.
RGB to CIELab color space conversion
# RGB to Lab conversion
# Step 1: RGB to XYZ
# http://www.easyrgb.com/index.php?X=MATH&H=02#text2
# Step 2: XYZ to Lab
# http://www.easyrgb.com/index.php?X=MATH&H=07#text7
def rgb2lab(inputColor):
num = 0
RGB = [0, 0, 0]
for value in inputColor:
value = float(value) / 255
if value > 0.04045:
value = ((value + 0.055) / 1.055) ** 2.4
else:
value = value / 12.92
RGB[num] = value * 100
num = num + 1
XYZ = [0, 0, 0, ]
X = RGB[0] * 0.4124 + RGB[1] * 0.3576 + RGB[2] * 0.1805
Y = RGB[0] * 0.2126 + RGB[1] * 0.7152 + RGB[2] * 0.0722
Z = RGB[0] * 0.0193 + RGB[1] * 0.1192 + RGB[2] * 0.9505
XYZ[0] = round(X, 4)
XYZ[1] = round(Y, 4)
XYZ[2] = round(Z, 4)
# Observer= 2°, Illuminant= D65
XYZ[0] = float(XYZ[0]) / 95.047 # ref_X = 95.047
XYZ[1] = float(XYZ[1]) / 100.0 # ref_Y = 100.000
XYZ[2] = float(XYZ[2]) / 108.883 # ref_Z = 108.883
num = 0
for value in XYZ:
if value > 0.008856:
value = value ** (0.3333333333333333)
else:
value = (7.787 * value) + (16 / 116)
XYZ[num] = value
num = num + 1
Lab = [0, 0, 0]
L = (116 * XYZ[1]) - 16
a = 500 * (XYZ[0] - XYZ[1])
b = 200 * (XYZ[1] - XYZ[2])
Lab[0] = round(L, 4)
Lab[1] = round(a, 4)
Lab[2] = round(b, 4)
return Lab
@manojpandey
Copy link
Author

Great. Any feedback @i-make-robots? :)

@i-make-robots
Copy link

Your work is appreciated and I thank you. :)

@roguealexander
Copy link

Super helpful stuff! Just used the alg and values (hard to find in a digestible way) in c#

@MeltyMooncakes
Copy link

im gonna convert it to javascript for a module im making

@manojpandey
Copy link
Author

@JayRizuri feel free to :D Thanks!

@timotheeg
Copy link

Thanks for that! In case this helps someone, I ported to JS as follow:

function rgb2lab_normalizeRgbChannel(channel) {
	channel /= 255;

	return 100 * (channel > 0.04045
		? Math.pow((channel + 0.055) / 1.055, 2.4)
		: channel / 12.92
	);
}

function rgb2lab_normalizeXyzChannel(channel) {
	return (channel > 0.008856)
		? Math.pow(channel, 1/3)
		: (7.787 * channel) + (16 / 116);
}

function rgb2lab([r, g, b]) {
	r = rgb2lab_normalizeRgbChannel(r);
	g = rgb2lab_normalizeRgbChannel(g);
	b = rgb2lab_normalizeRgbChannel(b);

	let X = r * 0.4124 + g * 0.3576 + b * 0.1805;
	let Y = r * 0.2126 + g * 0.7152 + b * 0.0722;
	let Z = r * 0.0193 + g * 0.1192 + b * 0.9505;

	// Observer= 2°, Illuminant= D65
	X = rgb2lab_normalizeXyzChannel(X / 95.0470);
	Y = rgb2lab_normalizeXyzChannel(Y / 100.0);
	Z = rgb2lab_normalizeXyzChannel(Z / 108.883);

	return [
		(116 * Y) - 16, // L
		500 * (X - Y),  // a
		200 * (Y - Z),  // b
	];
}

@TellAnAx
Copy link

TellAnAx commented Aug 4, 2022

This is the code written in R.
I added a tiny extension, making the maximum of the RGB color value range variable instead of hard-coding it.

https://gist.github.com/TellAnAx/06ace666a92d0849d6b774de1790cc7a

rgb2lab <- function(inputColor, maxColorValue = 255){

    num <- 1
    RGB <- c(0, 0, 0)

    for(value in inputColor){    
      value <- value / maxColorValue

        if(value > 0.04045){
          value <- ((value + 0.055) / 1.055)^2.4
        } else{
          value <- value / 12.92
        }
          

        RGB[num] <- value * 100
        num <- num + 1
    }

    XYZ <- c(0, 0, 0)

    X = RGB[1] * 0.4124 + RGB[2] * 0.3576 + RGB[3] * 0.1805
    Y = RGB[1] * 0.2126 + RGB[2] * 0.7152 + RGB[3] * 0.0722
    Z = RGB[1] * 0.0193 + RGB[2] * 0.1192 + RGB[3] * 0.9505
    XYZ[1] = round(X, 4)
    XYZ[2] = round(Y, 4)
    XYZ[3] = round(Z, 4)

    # Observer= 2°, Illuminant= D65
    XYZ[1] = XYZ[1] / 95.047         # ref_X =  95.047
    XYZ[2] = XYZ[2] / 100.0          # ref_Y = 100.000
    XYZ[3] = XYZ[3] / 108.883        # ref_Z = 108.883

    num = 1
    for(value in XYZ){
            if(value > 0.008856){
            value = value ** (0.3333333333333333)  
            } else{
              value = (7.787 * value) + (16 / 116)
            }
      
        XYZ[num] <- value
        num <- num + 1  
    }



    Lab <- c(0, 0, 0)

    L = (116 * XYZ[2]) - 16
    a = 500 * (XYZ[1] - XYZ[2])
    b = 200 * (XYZ[2] - XYZ[3])

    Lab[1] = round(L, 4)
    Lab[2] = round(a, 4)
    Lab[3] = round(b, 4)

    return(Lab)
}

@i-make-robots
Copy link

@rakesh5283
Copy link

Anyone have the numpy conversion for this code.. this takes too much time

@werdl
Copy link

werdl commented Nov 5, 2023

This is really useful, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment