-
Notifications
You must be signed in to change notification settings - Fork 142
fix color-remapping for images that are already in platte mode #221
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
base: main
Are you sure you want to change the base?
Conversation
@bablokb - is this able to be merged or reviewed? |
Yes, this is tested and ready to merge. One note though: this patch works as long as the image actually uses the palette of pure colors that are defined in the PR (
I did not feel like it is worth the extra work, since pre-dithered images hopefully use pure colors. Best thing is to document this though. |
Sorry for the delay getting back here. We've had other problems with the image remapping code (where P mode images have more than six colours) and have attempted a more thorough rewrite - most of it was very, very old code I wrote back when I had much less idea what I was doing - which should fix.. I would tentatively say... all cases. We pretty much just let PIL do the work, since that's what it's supposed to be good for 😆 and try to avoid special casing image formats (except for the one exception where a P mode image has no palette at all, and thus no colour information.) If you have a moment, I'd appreciate knowing if the changes work for you: #228 |
I will test it, but just by looking at the code I am not sure if this is correct:
This will take the original P-mode image (already quantized and dithered), convert it to RGB and re-quantize and re-dither it again?! But maybe my understanding of the code is just wrong. The main reason (at least for me) to use P-mode images is that I have the control over dithering. I noticed that the current code (i.e. from main) tends to blow-out the highlights (on the plus side it does not create as many artifacts). |
It will. In theory this should be a no-op since the conversion to RGB will preserve the colour information, and the pure colours should not be dithered. In practise I expect this will be complicated by the palette saturation code. Frustratingly we'll probably still have to edge case this. My goal is to lean on PIL as much as possible and avoid doing messy manual remapping, since I hope this function can be ported to the other drivers: def set_image(self, image, saturation=0.5):
"""Copy an image to the display.
:param image: PIL image to copy, must be 800x480
:param saturation: Saturation for quantization palette - higher value results in a more saturated image
"""
if not image.size == (self.width, self.height):
raise ValueError(f"Image must be ({self.width}x{self.height}) pixels!")
dither = Image.Dither.FLOYDSTEINBERG
# Image size doesn't matter since it's just the palette we're using
palette_image = Image.new("P", (1, 1))
if image.mode == "P":
# Create a pure colour palette from DESATURATED_PALETTE
palette = numpy.array(DESATURATED_PALETTE, dtype=numpy.uint8).flatten().tobytes()
# Assume that palette mode images with an unset palette use the
# default colour order and "DESATURATED_PALETTE" pure colours
if not image.palette.colors:
image.putpalette(palette)
# Assume that palette mode images with exactly six colours use
# all the correct colours, but not exactly in the right order.
if len(image.palette.colors) == 6:
dither = Image.Dither.NONE
palette_image.putpalette(palette)
else:
# All other image should be quantized and dithered
palette = self._palette_blend(saturation)
palette_image.putpalette(palette)
image = image.convert("RGB").quantize(6, palette=palette_image, dither=dither)
# Remap our sequential palette colours to display native (missing colour 4)
remap = numpy.array([0, 1, 2, 3, 5, 6])
self.buf = remap[numpy.array(image, dtype=numpy.uint8).reshape((self.rows, self.cols))] The conversion to RGB, and quantisation with a fixed palette and no dither should be equivalent to manually remapping the colours (though the Quantize method might have something to say about this). Things would be simpler if we excluded/ignored And if we're exposing stuff like |
So the BTW this all came up because I ported the e673 to CircuitPython. CP does no dithering (or better: no palette dithering), so I needed pre-dithered images anyhow. But with CP the images on the spectra6 look identical to what I see on my laptop/PC. Color-mapping within CP is purely based on chroma, so even if I don't use a specific palette for quantization, I will get sensible results as long as the palette that I use will have something that is red or near red (and so on for other colors). The majority of users will just throw a RGB-image on the screen and will be happy. Anybody using pre-dithered images will certainly expect to see them "as is". |
I think our comic example is the exception that proves the rule here, images can randomly be palette-mode and there's no real way to tell if they're dithered or not. (in the case of the comic example, they'd never be native to our display anyway) I've updated the linked branch to detect 6-colour images and assume they are as-is, pre-dithered images (with the exception that their colours will be remapped correctly). A lot of the complexity comes from efforts to make the palette colours somewhat true to the screen, or at least a reasonable balance of realistic screen colours and the intended pure colours. Basing this on crudely mixed RGB values was probably a bad way to start. Thanks for your feedback/suggestions here, a lot of Inky is just my best attempt at making stuff work... circa 5-10 years ago. It doesn't always pan out 😆 |
Images that are already in mode='P' are remapped with a fix remapping that ignores the internal colormap of the image. The result is terrible.
This patch generates the remap-array automatically. Note that this is a patch only for the e673, the other drivers probably need this as well.