Reputation: 3138
I have .tiff files containing color-indexed images, i.e. the image itself (1024x1024) contains an index per pixel (in my case 0, 1) and within the tiff file is a colormap (256x3) that maps these codes to colors
code R G B
_________________
0 = 0 0 0
1 = 140 215 115
2 = 255 255 255 ... (other codes are irrelevant for me)
I want to read the indexed image with Python. I'm using OpenCV and following the docs I tried this (not using the -1 flag gives an RGB image):
img = cv2.imread(file, cv2.IMREAD_UNCHANGED) # cv2.IMREAD_UNCHANGED = -1
I would expect the unchanged indexed-color image, i.e. an image 1024x1024
containing values 0
or 1
. However I'm getting an 1024x1024
image with values 0
or 181
.
I'm puzzled where the 181
is coming from (not an average of the corresponding color value; (140 + 215 + 115) / 3 = 157
), also I don't want to manually change these values. Is there a way to read color indexed tiff-files with Python OpenCV (or if need be other libs) to [a] get the index-image and (optional) [b] even get the color map?
An example file is here. Reading this data with MATLAB works as expected:
img = imread(file); % returns img: (1024, 1024) with values [0, 1]
[img, cmap] = imread(file); % returns img: (1024, 1024) with values [0, 1], cmap (256x3)
Upvotes: 3
Views: 3084
Reputation: 207465
Updated Answer
It seems Pillow
can also extract the palette from your TIFF. I am no Python expert, but I wrote this and it appears to work:
from PIL import Image
image = Image.open('indexed.tif')
print(image.getpalette()[:12])
Output
[0, 0, 0, 140, 215, 115, 255, 255, 255, 0, 0, 0]
Original Answer
You can do what you ask very easily at the command-line with ImageMagick which is installed on most Linux distros and is available for macOS and Windows.
Answering the last part first, you can extract the palette with this command:
identify -verbose indexed.tif
Sample Output
Image: indexed.tif
Format: TIFF (Tagged Image File Format)
Mime type: image/tiff
Class: PseudoClass
Geometry: 1024x1024+0+0
Resolution: 72x72
Print size: 14.2222x14.2222
Units: PixelsPerInch
Colorspace: sRGB
Type: Palette
Endianess: LSB
Depth: 8-bit
Channel depth:
Red: 8-bit
Green: 8-bit
Blue: 8-bit
Channel statistics:
Pixels: 1048576
Red:
min: 0 (0)
max: 140 (0.54902)
mean: 24.9271 (0.0977535)
standard deviation: 53.5578 (0.210031)
kurtosis: 0.832982
skewness: 1.68315
entropy: 0.675795
Green:
min: 0 (0)
max: 215 (0.843137)
mean: 38.281 (0.150121)
standard deviation: 82.2495 (0.322547)
kurtosis: 0.832982
skewness: 1.68315
entropy: 0.675795
Blue:
min: 0 (0)
max: 115 (0.45098)
mean: 20.4759 (0.0802975)
standard deviation: 43.9939 (0.172525)
kurtosis: 0.832982
skewness: 1.68315
entropy: 0.675795
Image statistics:
Overall:
min: 0 (0)
max: 215 (0.843137)
mean: 27.8947 (0.109391)
standard deviation: 59.9337 (0.235034)
kurtosis: 2.61693
skewness: 2.01681
entropy: 0.675795
Colors: 2
Histogram:
861876: ( 0, 0, 0) #000000 black
186700: (140,215,115) #8CD773 srgb(140,215,115)
Colormap entries: 256
Colormap:
0: ( 0, 0, 0,255) #000000FF black
1: (140,215,115,255) #8CD773FF srgba(140,215,115,1)
2: (255,255,255,255) #FFFFFFFF white
3: ( 0, 0, 0,255) #000000FF black
4: ( 0, 0, 0,255) #000000FF black
5: ( 0, 0, 0,255) #000000FF black
6: ( 0, 0, 0,255) #000000FF black
7: ( 0, 0, 0,255) #000000FF black
8: ( 0, 0, 0,255) #000000FF black
...
...
You can see your palette (colormap) in the last 8 lines above. A more convenient command is:
identify -verbose indexed.tif | grep -A8 "Colormap:"
Colormap:
0: ( 0, 0, 0,255) #000000FF black
1: (140,215,115,255) #8CD773FF srgba(140,215,115,1)
2: (255,255,255,255) #FFFFFFFF white
3: ( 0, 0, 0,255) #000000FF black
4: ( 0, 0, 0,255) #000000FF black
5: ( 0, 0, 0,255) #000000FF black
6: ( 0, 0, 0,255) #000000FF black
7: ( 0, 0, 0,255) #000000FF black
Now for the first part of your question. The easiest way to get an image of zeroes for blacks and ones for the other colours is to set the fill colour to one (ImageMagick calls that gray(1)
) and then make everything that is not black, into that colour. Lastly, save as a PGM (Portable GreyMap) file which OpenCV can read without any libraries and which cannot contain a palette so its has no scope for messing you around:
convert indexed.tif -fill "gray(1)" +opaque black indices.pgm
That is the end of the actual answer. All that follows is just extra information.
Note that will be a very dark, low-contrast image as it only contains 0
or 1
on a scale of 0-255, so you will need to normalise it or contrast-stretch it if you want to see anything:
convert indices.pgm -normalize result.jpg
Note that if you use ImageMagick v7+, identify
becomes magick identify
and convert
becomes magick
in the commands above.
Upvotes: 1