Pillow conversion from RGB to indexed P mode

Question:

I have an RGB image with only 25 colors,

enter image description here

and I want to convert it to P mode to make it indexed (saving space, as far as I understand), but the resulting image, though in P mode and indexed, has got 70 colors instead of 25:

>>> from PIL import Image
>>> img = Image.open(r"test_rgb2p.png")
>>> img.mode
'RGB'
>>> len(img.getcolors())
25
>>> img = img.convert(mode='P')
>>> img.mode
'P'
>>> len(img.getcolors())
70

I guess that the convert command performs some kind of unnecessary resampling, because I see no reason why the number of colors should increment. In fact with GIMP I’m able to convert this same image to indexed colors and the number of colors does not change.

Is there a way to do this RGB to P mode conversion without changes in colors?

Asked By: mmj

||

Answers:

You could try

# Quantize the image to reduce the number of colors to 25
img = img.quantize(colors=25)

# Convert the image to P mode
img = img.convert(mode='P')

# Check the number of colors in the image
print(len(img.getcolors()))  # should print 25

This worked great for me at least!

Answered By: Dexty

Pillow has no idea that the number of colors in your image is restricted, so it must invent a palette that it thinks can handle everything. You’ll probably find that nearly none of those 70 colors is exactly identical to the colors in your image.

In fact if you look at the documentation for Image.convert you’ll see that it defaults to a web palette. If you add palette=ADAPTIVE it might give better results:

img = img.convert(mode='P', palette=Image.Palette.ADAPTIVE)

Older versions of PIL/Pillow didn’t have the Palette enumeration (I used 8.1.0 for this answer) and the constant is part of Image itself:

img = img.convert(mode='P', palette=Image.ADAPTIVE)
Answered By: Mark Ransom
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.