Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent use of dithering in convert and quantize #5836

Open
zaprice opened this issue Nov 16, 2021 · 0 comments
Open

Inconsistent use of dithering in convert and quantize #5836

zaprice opened this issue Nov 16, 2021 · 0 comments

Comments

@zaprice
Copy link

zaprice commented Nov 16, 2021

What did you do?

Let's play around with image dithering, starting with this nice, low-res picture of Theresa May:

image

According to the docs calling convert in "P" mode defaults to the WEB palette with dithering:

orig.convert("P")

image

Nice, that looks good! And you can pass Image.NONE to disable the dithering.

What if we want to use less colors? Maybe we cut down the bit depth and dither to make up for it, for a retro effect.
Checking the docs again, it claims that we can use an ADAPTIVE palette and pass in some number of colors; let's use 16.
The docs say that dithering is used when converting from "RGB" to "P" and that it defaults to FLOYDSTEINBERG, but let's pass in dither just to make sure:

dither_lesscol_broken = orig.convert(
    "P", palette=Image.ADAPTIVE, colors=16, dither=Image.FLOYDSTEINBERG
)

image

Doesn't look dithered - test it on a gradient image and we get severe banding:

image

image

Is there a workaround? Well, the docs for quantize claim to apply a dither.
Let's try it:

orig.quantize(16, dither=Image.FLOYDSTEINBERG)

image

No dice, exactly the same as the previous. Looking at the code, we see that quantize calls ImagingCore.convert with a dither - but ONLY if you pass in a palette!
Let's try that:

# Call .quantize once to get the palette
pal = orig.quantize(16)
# And again using that palette
dither_lesscol = orig.quantize(16, palette=pal, dither=Image.FLOYDSTEINBERG)

image

There we go, that's what I expected.

What did you expect to happen?

Image.convert should be able to simultanously handle an ADAPTIVE palette and apply dithering (or not) based on the dither argument.

Additionally, Image.quantize should apply dithering (or not) based on the dither argument.

What actually happened?

orig.convert("P", palette=Image.ADAPTIVE, colors=16, dither=Image.FLOYDSTEINBERG) produces a non-dithered image.

orig.quantize(16, dither=Image.FLOYDSTEINBERG) also produces a non-dithered image.

What are your OS, Python and Pillow versions?

  • OS: Linux Mint 20.1
  • Python: 3.8.10
  • Pillow: 7.0.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant