Python – Read in an indexed color image in Python

imageimage processingMATLABpython

An indexed color image is an image with pixels that are integers (1,2, .. N), and for each integer, an associated color maps to this pixel from a given color map. In MATLAB, reading in an indexed color image can be done in the following way:

[im, colormap] = imread('indexed.png');

How can I do the same thing in Python? I have tried OpenCV, scikit-image but they all convert to RGB automatically.

Best Answer

After some research, this is what I came up with. You can use the Python Imaging Library - specifically the Pillow fork: https://python-pillow.github.io/

Install the package, then you can use the Image.open method from the Image class to open up the image. If your image has a colour map, then the image will automatically load in as an indexed image. To make this usable, use NumPy and use the numpy.array constructor. I'm assuming you can use NumPy because scikit-image and OpenCV both use NumPy as the fundamental backbone to image manipulations:

from PIL import Image
import numpy as np
im = Image.open("image.png") # Replace with your image name here
indexed = np.array(im) # Convert to NumPy array to easier access

Finally, if you want the colour map / palette that was actually used for the image, use the Image.getpalette method that's part of the Image class. However, this will give you a num_colours x 3 element list. Therefore, to determine how many colours you have, simply divide the length of this list by 3. However, the colour map that is loaded in MATLAB is normalized whereas getpalette does not normalize this and defaults to the type of the image loaded in. As such, you'll have to infer on what the image type is by looking at the converted NumPy image version, then use this to normalize your colour map:

As such:

# Get the colour palette
palette = im.getpalette()

# Determine the total number of colours
num_colours = len(palette)/3

# Determine maximum value of the image data type
max_val = float(np.iinfo(indexed.dtype).max)

# Create a colour map matrix
map = np.array(palette).reshape(num_colours, 3) / max_val

To demonstrate that we have this correct, here's an indexed image from a question that I helped solve a while ago:

Using MATLAB to load in this image:

[indexed, map] = imread('http://i.stack.imgur.com/OxFwB.png');

I get this when I focus on rows 280 to 290 and columns 400 to 410 in the indexed image as a double check:

>> indexed(280:290, 400:410)

ans =

   59   60   61   62   65   64   59   56   56   53   49
   61   61   64   65   65   60   60   57   58   53   53
   67   62   67   56   60   62   60   61   51   59   55
   65   60   62   61   58   58   53   55   57   55   54
   66   58   56   59   56   56   52   55   52   55   52
   68   68   61   61   61   56   56   55   55   57   59
   66   59   59   66   68   62   62   60   60   60   53
   70   68   64   58   61   63   67   61   67   56   59
   69   67   63   64   62   65   63   68   67   64   58
   61   68   68   72   71   73   70   66   63   64   64
   68   67   70   71   71   69   64   64   65   64   58

Here's what I get in Python when I run the equivalent code to get the indexed image. Note that I physically downloaded the image onto my computer and loaded it in from disk. Take note that NumPy starts indexing at 0 and not 1, so I had to subtract the ranges by 1. Also note that the end of the range operator is exclusive:

In [29]: indexed[279:290, 399:410]
Out[29]: 
array([[59, 60, 61, 62, 65, 64, 59, 56, 56, 53, 49],
       [61, 61, 64, 65, 65, 60, 60, 57, 58, 53, 53],
       [67, 62, 67, 56, 60, 62, 60, 61, 51, 59, 55],
       [65, 60, 62, 61, 58, 58, 53, 55, 57, 55, 54],
       [66, 58, 56, 59, 56, 56, 52, 55, 52, 55, 52],
       [68, 68, 61, 61, 61, 56, 56, 55, 55, 57, 59],
       [66, 59, 59, 66, 68, 62, 62, 60, 60, 60, 53],
       [70, 68, 64, 58, 61, 63, 67, 61, 67, 56, 59],
       [69, 67, 63, 64, 62, 65, 63, 68, 67, 64, 58],
       [61, 68, 68, 72, 71, 73, 70, 66, 63, 64, 64],
       [68, 67, 70, 71, 71, 69, 64, 64, 65, 64, 58]], dtype=uint8)

That matches... now what about the colour maps? Let's take a look at the first 10 rows of the colour maps between MATLAB and Python:

MATLAB

>> format long g;
>> map(1:10,:)

ans =

                         0                         0                         0
        0.0156862745098039       0.00392156862745098        0.0274509803921569
        0.0313725490196078       0.00784313725490196        0.0588235294117647
        0.0470588235294118        0.0117647058823529        0.0901960784313725
        0.0627450980392157        0.0156862745098039          0.12156862745098
        0.0784313725490196        0.0196078431372549         0.152941176470588
        0.0941176470588235        0.0235294117647059         0.184313725490196
         0.109803921568627        0.0274509803921569         0.215686274509804
         0.125490196078431        0.0313725490196078         0.247058823529412
         0.141176470588235        0.0352941176470588          0.27843137254902

Python

In [30]: map[:10,:]
Out[30]: 
array([[ 0.        ,  0.        ,  0.        ],
       [ 0.01568627,  0.00392157,  0.02745098],
       [ 0.03137255,  0.00784314,  0.05882353],
       [ 0.04705882,  0.01176471,  0.09019608],
       [ 0.0627451 ,  0.01568627,  0.12156863],
       [ 0.07843137,  0.01960784,  0.15294118],
       [ 0.09411765,  0.02352941,  0.18431373],
       [ 0.10980392,  0.02745098,  0.21568627],
       [ 0.1254902 ,  0.03137255,  0.24705882],
       [ 0.14117647,  0.03529412,  0.27843137]])

... looks like it matches!