# Getting list of colours from image in lab format

I need to write a bit of code that will get a list of the RGB colours in an image and then translate them into a NumPy array of lab colours. I have managed to do this, but I would like to learn how I can do it more efficiently.

from skimage.color import rgb2lab
import numpy as np
from PIL import Image  # @UnresolvedImport

def get_histogram (img):

#histogram = plt.hist(img.flatten(), bins=100, facecolor='green', alpha=0.75)
w, h = img.size
colours = img.getcolors(w*h)  #Returns a list [(pixel_count, (R, G, B))]
num, colours_rgb = zip(*colours)
r,g,b = zip(*colours_rgb)

num_of_colours = len(r)
w2,h2 = 1,num_of_colours
data = np.zeros( (w2,h2,3), dtype=np.uint8)
print(data.shape)
data[0,:,0] = r
data[0,:,1] = g
data[0,:,2] = b
print(data)
colours_lab = rgb2lab(data)


1. There's no docstring for get_histogram. What does this function do? What kind of object should I pass for the img argument?

2. You import skimage.io.imread and PIL.Image but you don't use either of them.

3. This comment doesn't seem relevant:

#histogram = plt.hist(img.flatten(), bins=100, facecolor='green', alpha=0.75)

4. There are two useless print statements. I presume that these are left over from a debugging session and you forgot to remove them. You might find it useful to learn to use the Python debugger which would avoid the need to add print statements to the code.

5. There are two pieces of functionality here: (i) extracting the colour data from an image into a Numpy array; (ii) converting colour data from RGB to the Lab colour space. It would make sense to split these up (especially as piece (ii) is so simple).

6. The function is poorly named: it does not actually get a histogram. (Because you discard the colour counts in the num array.) This makes me wonder what you are using this function for.

7. You use the spelling "colour" but the APIs you are calling use the spelling "color". Even if you prefer the "colour" spelling, it's better to be consistent. That way there's less chance of forgetting and making a mistake.

8. Creating a Numpy array from an a Python list is usually straightforward if you pass the list to the numpy.array function. There's no need to mess about with numpy.zeros and assigning column-wise.

So I'd write the following:

import numpy as np

def distinct_colors(img):
"""Return the distinct colors found in img.
img must be an Image object (from the Python Imaging Library).
The result is a Numpy array with three columns containing the
red, green and blue values for each distinct color.

"""
width, height = img.size
colors = [rgb for _, rgb in img.getcolors(width * height)]
return np.array(colors, dtype=np.uint8)


The conversion to Lab colour space is so simple that I don't think it needs a function.

### Update

There's one minor difficulty: the skimage.color functions all demand an array with 3 or 4 dimensions, and so only support 2- and 3- dimensional images. Your array of distinct colours is a 1-dimensional image, so it's rejected. But you can easily use numpy.reshape to turn it into a 2-dimensional image whose first dimension is 1 before passing it to rgb2lab, like this:

colors = distinct_colors(img)
rgb2lab(colors.reshape((1, -1, 3)))


Or if you prefer the second dimension to be 1, use .reshape((-1, 1, 3)).

(Personally I think the skimage.color behaviour is absurd. Why does it care about the number of dimensions? There doesn't seem to be any obvious reason in the code. Maybe it would be worth filing a bug report?)

• The one comment that you posted wrt to coding efficiency (number 8), I have a question about. When I just do an assign of the list to a numpy array, it is not the right size, shape for the lab conversion process and throws an error (ValueError: the input array must be have a shape == (.., ..,[ ..,] 3)), got (253, 3)). Which is why I did the numpy zeroes thing. But I am hoping there is a more efficient way to do than the way I did it. I am new to python. – sleepingbeauty Jan 30 '14 at 19:06
• See updated answer: it's easy to add a dimension using numpy.reshape. – Gareth Rees Jan 30 '14 at 20:27