2

I have an image that I want to perform quantization on it. I want to use PIL and Numpy libraries and the original image is:

enter image description here

and I expect the output for 2-level quantization to be like this:

enter image description here

but the output is like this:

enter image description here

What am I doing wrong?

from PIL import Image 
import PIL 

  
# creating an image object (main image) 
im1 = Image.open(r'File Address').convert('L')
  
# quantize a image 
im1 = im1.quantize(2) 
  
# to show a specified image 
im1.show()
  • Is the problem just the display range? It looks like it is only 2 colors. – matt Mar 29 '23 at 08:16
  • Yes, it should be black and white. the second image was generated using openCV and works as expected but I don't know why it doesn't work in PIL. – Shadow23548 Mar 29 '23 at 08:50
  • You can just quantize to a palette that only has black and white entries if that's what you want. – Mark Setchell Mar 29 '23 at 10:56
  • 1
    @MarkSetchell I looked into that, but it isn't obvious how to pass the palette to the quantize method. You probably have a good answer. – matt Mar 29 '23 at 14:06

2 Answers2

1

The resulting image is 2 colors, so you just need to adjust the contrast.

PIL.ImageOps.autocontrast(im1.convert("L")).show()

The convert("L") seems unecessary, but I get an error without it.

A better way might be to include an argument to quantize, but I didn't understand how to do that.

Instead of converting to grayscale and contrasting you could also just set the palette.

p = im1.getpalette()
p[0] = 255
p[1] = 255
p[2] = 255
p[3] = 0;
p[4] = 0;
p[5] = 0

im1.putpalette( p )
matt
  • 10,892
  • 3
  • 22
  • 34
1

You can quantize to another image with a specific palette, so you just need to make such an image first and then quantize to it:

#!/usr/bin/env python3

from PIL import Image

# Make tiny palette Image, one black pixel
palIm = Image.new('P', (1,1))

# Make your desired B&W palette containing only 1 pure white and 255 pure black entries
palette = [255, 255, 255 ] + [0, 0 ,0] * 255 

# Push in our lovely B&W palette and save just for debug purposes
palIm.putpalette(palette)
palIm.save('DEBUG-palette.png')

# Load actual image 
actual = Image.open('95KIY.png').convert('RGB')

# Quantize actual image to palette
actual = actual.quantize(palette=palIm, dither=Image.Dither.NONE)

actual.save('result.png')

enter image description here


So if you want to quantize to orange and navy blue, just change the palette to:

palette = [255, 128, 0 ] + [0, 0 ,128] * 255

And you get this:

enter image description here

And if you remove the dither parameter, you get a dithered version:

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • I did a similar answer years ago actually. There's a PIL-based answer and a Numpy/OpenCV answer to the same question. Have a look here https://stackoverflow.com/a/57202093/2836621 – Mark Setchell Mar 29 '23 at 17:19
  • How do you know to pass an image as the palette argument? – matt Mar 29 '23 at 18:42
  • 1
    @matt Through years of trying to understand software documentation If you look here https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize you can see the call signature and underneath it says *"palette - Quantize to the palette of given **PIL Image.Image**"* which means you have to pass a PIL Image (with the palette you want). – Mark Setchell Mar 29 '23 at 18:58
  • Ha! Now that I know, I don't know what else that could mean.Thanks! – matt Mar 30 '23 at 07:17