Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tensorflow Deeplab image colormap removal confusion

In this following code, I only see, an image is read and written again. But how do the image pixel values get changed so drastically? Apparently, converting the PIL image object to numpy array causes this but don't know why. I have read the doc for PIL images but didn't see any reasonable explanation for this to happen.

import numpy as np
from PIL import Image


def _remove_colormap(filename):
  return np.array(Image.open(filename))


def _save_annotation(annotation, filename):
  pil_image = Image.fromarray(annotation.astype(dtype=np.uint8))
  pil_image.save(filename)


def main():
  raw_annotation = _remove_colormap('2007_000032.png')
  _save_annotation(raw_annotation, '2007_000032_output.png')


if __name__ == '__main__':
  main()

Input image is,

Input image

Here is the output,

Output image

Note: The value at the red area in the input image is [128,0,0] and in the output image it's [1,1,1].

The actual source of the code is here.

Edit: As @taras made it clear in his comment,

Basically, palette is a list of 3 * 256 values in form 256 red values, 256 green values and 256 blue values. Your pil_image is an array of greyscale pixels each taking a single value in 0..255 range. When using 'P' mode the pixel value k is mapped to a color (pallette[k], palette[256 + k], palette[2*256 + k]). When using 'L' mode the color is simply k or (k, k, k) in RGB

The segmentation image annotations use a unique color for each object type. So we don't need the actual color palette for the visualization, we get rid of the unnecessary color palette.

like image 940
Sumsuddin Shojib Avatar asked Aug 06 '18 07:08

Sumsuddin Shojib


2 Answers

A quick check of the opened image mode with

Image.open(filename).mode

shows the input file is opened with 'P' mode which stands for

8-bit pixels, mapped to any other mode using a color palette

So, when you generate image with Image.fromarray the palette is simply lost and you are left with a greyscale image in 'L' mode.

You simply need to provide the palette info when creating the output array.

The palette can be extracted with Image.getpalette():

def _remove_colormap(filename):
    img = Image.open(filename)
    palette = img.getpalette()
    return np.array(img), palette

Once you created your pil_image you can set the palette back with Image.putpalette(palette)

def _save_annotation(annotation, palette, filename):
    pil_image = Image.fromarray(annotation.astype(dtype=np.uint8))
    pil_image.putpalette(palette)
    pil_image.save(filename)

And your main changed accordingly:

def main():
    raw_annotation, palette = _remove_colormap('SqSbn.png')
    _save_annotation(raw_annotation, palette, '2007_000032_output.png')

Edit:

palette is a list of 3 * 256 values in the following form:
256 red values, 256 green values and 256 blue values.

pil_image is an array of greyscale pixels each taking a single value in 0..255 range. When using 'P' mode the pixel value k is mapped to an RGB color (pallette[k], palette[256 + k], palette[2*256 + k]). When using 'L' mode the color is simply k or (k, k, k) in RGB.

like image 129
taras Avatar answered Nov 12 '22 09:11

taras


Mode conversion is missing in _remove_colormap(filename). As it's defined in the question (and the answer from @taras), remove_colormap converts a PIL image into a numpy array. _save_annotation() further converts the numpy array into a PIL image. RGB image is saved as such. convert('L') should be used for converting to grayscale. Modified function definition is as under:

def _remove_colormap(filename):
    img = Image.open(filename).convert('L')
    palette = img.getpalette()
    print("palette: ", type(palette))
    return np.array(img), palette
like image 1
Abhishek Saurabh Avatar answered Nov 12 '22 10:11

Abhishek Saurabh