How to convert RGB image pixels to L*a*b*?
You can do it with PIL/Pillow using the built-in Colour Management System and building a transform like this:
#!/usr/local/bin/python3
import numpy as np
from PIL import Image, ImageCms
# Open image and discard alpha channel which makes wheel round rather than square
im = Image.open('colorwheel.png').convert('RGB')
# Convert to Lab colourspace
srgb_p = ImageCms.createProfile("sRGB")
lab_p = ImageCms.createProfile("LAB")
rgb2lab = ImageCms.buildTransformFromOpenProfiles(srgb_p, lab_p, "RGB", "LAB")
Lab = ImageCms.applyTransform(im, rgb2lab)
And Lab
is now your image in Lab colourspace. If you carry on and add the following lines to the end of the above code, you can split the Lab image into its constituent channels and save them each as greyscale images for checking.
# Split into constituent channels so we can save 3 separate greyscales
L, a, b = Lab.split()
L.save('L.png')
a.save('a.png')
b.save('b.png')
So, if you start with this image:
you will get this as the L
channel:
this as the a
channel:
and this the b
channel:
Being non-scientific for a moment, the a
channel should be negative/low where the image is green and should be high/positive where the image is magenta so it looks correct. And the b
channel should be negative/low where the image is blue and high/positive where it is yellow, so that looks pretty good to me! As regards the L
channel, the RGB to greyscale formula is (off the top of my head) something like:
L = 0.2*R + 0.7*G + 0.1*B
So you would expect the L
channel to be much brighter where the image is green, and darkest where it is blue.
Alternatively, you can do it with the scikit-image module, maybe even more simply like this:
import numpy as np
from skimage import color, io
# Open image and make Numpy arrays 'rgb' and 'Lab'
rgb = io.imread('image.png')
Lab = color.rgb2lab(rgb)
I am not 100% sure of the scaling, but I suspect the L
channel is a float in range 0..100, and that a
and b
are also floats in range -128..+128, though I may be wrong!
With my colour wheel image above I got the following minima/maxima for each channel:
Lab[:,:,0].min() # L min
32.29567256501352
Lab[:,:,0].max() # L max
97.13950703971322
Lab[:,:,1].min() # a min
-86.18302974439501
Lab[:,:,1].max() # a max
98.23305386311316
Lab[:,:,2].min() # b min
-107.85730020669489
Lab[:,:,2].max() # b max
94.47812227647823
from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color
def rgb_to_cielab(a):
"""
a is a pixel with RGB coloring
"""
a1,a2,a3 = a/255
color1_rgb = sRGBColor(a1, a2, a3);
color1_lab = convert_color(color1_rgb, LabColor);
return color1_lab
rgb_to_cielab(np.array([255,0,255]))
Output: LabColor(lab_l=60.32364943499053,lab_a=98.23532017664644,lab_b=-60.83501679458592)