7
votes

Well, I'm working with image processing to identify the color variation of an image and to be able to plot that data in a histogram. For this, I use images of skin spots in the RGB color space. The code below I can get the colors of each pixel and convert to HSV using color.rgb2lab. But as I want to convert to L*a*b*, because it is closer to human vision, in the python library there is no conversion to L*a*b*. With this, through the separated pixels of RGB, how do I transform these pixels into LAB colors?

import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
import colorsys
from PIL import Image

# (1) Import the file to be analyzed!
img_file = Image.open("IMD006.png")
img = img_file.load()

# (2) Get image width & height in pixels
[xs, ys] = img_file.size
max_intensity = 100
hues = {}

# (3) Examine each pixel in the image file
for x in xrange(0, xs):
  for y in xrange(0, ys):
    # (4)  Get the RGB color of the pixel
    [r, g, b] = img[x, y]

# (5)  Normalize pixel color values
r /= 255.0
g /= 255.0
b /= 255.0

# (6)  Convert RGB color to HSV
[h, s, v] = colorsys.rgb_to_hsv(r, g, b)

# (7)  Marginalize s; count how many pixels have matching (h, v)
if h not in hues:
  hues[h] = {}
if v not in hues[h]:
  hues[h][v] = 1
else:
  if hues[h][v] < max_intensity:
    hues[h][v] += 1
4
You're going to need a two part conversion, first to XYZ, then to LAB, if I remember correctly from when I was doing this. You'll have to pick a color space for your XYZ transformationuser3483203
you can use these modules for RGB to L*A*B conversion: skimage scikit-image.org/docs/0.14.x/api/skimage.color.html#rgb2lab or opencv docs.opencv.org/3.4.3/d7/d1b/…deadvoid
Poynton's Color FAQ is the very best resource for how to convert from one color space to another. It will just give you equations, you'll have to implement those yourself if you can't find them in any library.Cris Luengo
Read that Color FAQ @Cris pointed you to. It'll help you focus on your questions. (L*a*b only gets mentioned in §35, but do yourself a favor and start at the top.)Jongware

4 Answers

8
votes

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:

enter image description here

you will get this as the L channel:

enter image description here

this as the a channel:

enter image description here

and this the b channel:

enter image description here

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
3
votes
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)

1
votes

Using cv2 you can easily implement this conversion. RGB->LAB, LAB->RGB.

import numpy as np
import cv2

img = cv2.imread('1.jpg')
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

cv2.imwrite('L.png', LAB[:,:,0])
cv2.imwrite('a.png', LAB[:,:,1])
cv2.imwrite('b.png', LAB[:,:,2])

BGR = cv2.cvtColor(LAB, cv2.COLOR_LAB2BGR)
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imwrite('new.png', BGR)
1
votes

I've seen this problem like you 'bout for 3 months and here is my solution for this

import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
import colorsys
from PIL import Image
from past.builtins import xrange
img_file = Image.open("F:/coding/Project/FDD/neo5.png")
img = img_file.load()
[xs, ys] = img_file.size
max_intensity = 100
hues = {}
for x in xrange(0, xs):
   for y in xrange(0, ys):

[r, g, b] = img[x, y]
r /= 255.0
g /= 255.0
b /= 255.0
[h, s, v] = colorsys.rgb_to_hsv(r, g, b)
if h not in hues:
   hues[h] = {}
if v not in hues[h]:
   hues[h][v] = 1
else:
   if hues[h][v] < max_intensity:
      hues[h][v] += 1

h_ = []
v_ = []
i = []
colours = []
for h in hues:
   for v in hues[h]:
      h_.append(h)
      v_.append(v)
      i.append(hues[h][v])
      [r, g, b] = colorsys.hsv_to_rgb(h, 1, v)
      colours.append([r, g, b])

fig = plt.figure()
ax = p3.Axes3D(fig)
ax.scatter(h_, v_, i, s=5, c=colours, lw=0)
ax.set_xlabel('Hue')
ax.set_ylabel('Value')
ax.set_zlabel('Intensity')
fig.add_axes(ax)
plt.show()