3
votes

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)
1
Did my answer sort out your problem? If so, please consider accepting it as your answer - by clicking the hollow tick/checkmark beside the vote count. If not, please say what didn't work so that I, or someone else, can assist you further. Thanks. meta.stackexchange.com/questions/5234/…Mark Setchell

1 Answers

1
votes

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

enter image description here


Note that if you use ImageMagick v7+, identify becomes magick identify and convert becomes magick in the commands above.