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
from skimage.io import imread
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)
    data[0,:,0] = r 
    data[0,:,1] = g 
    data[0,:,2] = b
    colours_lab = rgb2lab(data)

1 Answer 1

  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.


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?)

  • \$\begingroup\$ 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. \$\endgroup\$ Jan 30, 2014 at 19:06
  • \$\begingroup\$ See updated answer: it's easy to add a dimension using numpy.reshape. \$\endgroup\$ Jan 30, 2014 at 20:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.