3

enter image description here

As show in the above image, when I read it with pillow:

from PIL import Image
label = Image.open('example.png')
print(np.unique(array(label)))

The number are within range of [0, 34], which is correct. However, when I read with cv2:

import cv2
label = cv2.imread('example.png')
print(np.unique(label))

The number are with [0, 255] which is not correct in my application. How could I align the behavior of cv2 and pil please ?

Also when I checked the matlab example code parsing this image, it is written like this:

[labels, color_mappings] = imread('example.png')

It seems that the png file has two data fields, one is the fields with values ranges from 0 to 34, and the other is the color pixels, how could I parse it with cv2?

CoinCheung
  • 85
  • 10
  • Your image is palettised. OpenCV deals with palette images by expanding them to RGB - it is all explained here https://stackoverflow.com/a/52307690/2836621 – Mark Setchell May 22 '20 at 11:21
  • More information here too https://stackoverflow.com/a/59839834/2836621 – Mark Setchell May 22 '20 at 11:25
  • @MarkSetchell Thanks, how could I read a indexed image with opencv or write an image as indexed image ? – CoinCheung May 22 '20 at 11:47
  • I don't think you can. I would read it with PIL and convert to a Numpy array that I could process with OpenCV. – Mark Setchell May 22 '20 at 11:48
  • Does that mean that I cannot process that indexed image with c++ ? – CoinCheung May 22 '20 at 11:50
  • I prefer opencv because it also provides c++ api, I can process images with python when I do experiments, but I will have to use c++ in the product environment, any method to do it? – CoinCheung May 22 '20 at 11:55
  • "I cannot process that indexed image with c++ ?" It's a PNG, so use libPNG to load the paletted image without the conversion happening, and then do the rest of the processing with OpenCV as you need. If you need to write back a paletted image, you'd use libPNG directly again (as opposed to relying on the convenience wrappers that OpenCV provides). – Dan Mašek May 22 '20 at 12:52

1 Answers1

1

I think Dan has the right answer, but if you want to do some "quick and dirty" testing, you could use the following code to:

  • convert your palette image into a single channel greyscale PGM image of the palette indices that OpenCV can read without any extra libraries, and a separate palette file that you can apply back afterwards

  • load back a PGM file of the indices that OpenCV may have altered, and reapply the saved palette


#!/usr/bin/env python3

import numpy as np
from PIL import Image

# Open palette image and remove pointless alpha channel
im = Image.open('image.png').convert('P')

# Extract palette and save as CSV
np.array(im.getpalette()).tofile('palette.csv',sep=',')

# Save palette indices as single channel PGM image that OpenCV can read
na = np.array(im)
im = Image.fromarray(na).save('indices.pgm')

So that will have split image.png into indices.pgm that OpenCV can read as a single channel image and palette.csv that we can reload later.

And here is the second part, where we rebuild the image from indices.pgm and palette.csv

# First load indices
im = Image.open('indices.pgm')

# Now load palette 
palette = np.fromfile('palette.csv',sep=',').astype(np.uint8)

# Put palette back into image
im.putpalette(palette)

# Save
im.save('result.png')

Remember not to use any interpolation other than NEAREST_NEIGHBOUR in OpenCV else you will introduce new colours not present in the original image.

Keywords: Python, PNG, image processing, palette, palette indices, palette index

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432