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.
In computing, indexed color is a technique to manage digital images' colors in a limited fashion, in order to save computer memory and file storage, while speeding up display refresh and file transfers.
The pixels in the image contain index numbers that point to the RGB value in the color lookup table. The RGB values are the ones used by the display system. Indexed color images can look nearly identical to their 24-bit originals, because the 256 most frequently used colors are identified.
Indexed colorspaces are a very useful way of reducing the amount of memory and space needed when they only have a certain number of repeating colour values. You define a limited set number of colour values (usually 2,4, 16 or 256) and define a color value for each.
An indexed image uses direct mapping of pixel values to colormap values. The color of each image pixel is determined by using the corresponding value of X as an index into map . A colormap is often stored with an indexed image and is automatically loaded with the image when you use the imread function.
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:
>> 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
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With