Dithering in Colour(obrhubr.org)
65 points by surprisetalk 3 days ago | 8 comments
AndrewStephens 1 hour ago
Dithering is something of a lost art now that our displays can handle millions of colors in high definition, but it can be a striking artistic effect.

If anyone thinks their websites are too colorful, I made a pure JavaScript web component to dither images on client in real time, taking into account the real pixel size of the current display.

https://sheep.horse/2023/1/improved_web_component_for_pixel-...

hooli_gan 1 hour ago
Very cool, but the image in the bottom of the page flickers when scrolling.
tuyiown 1 hour ago
Dithering does that !
mattdesl 3 hours ago
It might be worth using a lightness estimate like OKLab, OKLrab[1], or CIE Lab instead of the RGB luminance weighting, as it should produce a more perceptually accurate result.

The other issue with your code right now, is that it is using euclidean distance in RGB space to choose the nearest color, but it would be probably also more accurate to use a perceptual color difference metric, a very simple choice is euclidean distance on OKLab colors.

I think dithering is a pretty interesting area of exploration, especially as a lot of the popular dithering algorithms are quite old and optimized for ancient compute requirements. It would be nice to see some dithering that isn't using 8-bits for errors, is based on perceptual accuracy, and perhaps uses something like a neural net to diffuse things in the best way possible.

[1] https://bottosson.github.io/posts/colorpicker/

funks_ 1 hour ago
If you are interested in color dithering with different color difference metrics [1], I've implemented just that [2]. You can find an example comparing metrics in my docs [3].

[1]: https://juliagraphics.github.io/Colors.jl/stable/colordiffer...

[2]: https://github.com/JuliaImages/DitherPunk.jl

[3]: https://juliaimages.org/DitherPunk.jl/stable/#Dithering-with...

crazygringo 57 minutes ago
Did the author forget to finish the blog post?

They show a single example of incorrect dithering, explain it's wrong, and then don't show a corrected version. There isn't a single example of proper color dithering.

And they talk about the distance to the nearest color (RGB) but don't explain how to account for black or white -- how to trade off between accuracy of hue, brightness, and saturation, for example.

This post doesn't explain at all how to actually dither in color. I don't understand why this is on the front page with over 50 votes.

Clamchop 2 days ago
They may not want to imply that didder's linearized rabbit is wrong, but I'm comfortable saying so. It's not just a little dark, it's way dark, to the point of hiding detail.

The linearized RGB palette is similarly awful. It clobbers a whole swath of colors, rendering them as nearly black. Purples are particularly brutalized. Yellows disappeared and became white.

On my phone, the middle palette doesn't appear too bright to my eyes, either.

Even the linearized gradient looks worse, .

Maybe linear is not best for perceptual accuracy.

badmintonbaseba 3 hours ago
I agree. I think the problem is a banal missing color transformation somewhere in the pipeline, like converting the palette and image to linear colorspace, doing the dithering there and mistakenly writing the linear color values instead of sRGB color values into the image.

Others suggest that the error is using the wrong metric for choosing the closest color, but I disagree. That wouldn't such drastic systematic darkening like this, as the palette is probably still pretty dense in the RGB cube.

Where the linearisation really matters is the arithmetic for the error diffusion, you definitely want to diffuse the error in a linear colorspace, and you are free to choose a good perceptual space for choosing the closest color at each pixel, but calculate the error in a linear space.

Visual perception is weird. But when you squint your eyes to blur the image, you are definitely mixing in a linear colorspace, as that's physical mixing of light intensities before the light even reaches your retina. So you have to match that when diffusing the error.

edit:

It also doesn't help that most (all?) browsers do color mixing wrong when the images are scaled, so if you don't view the dithered images at 100% without DPI scaling than you might get significantly distorted colors due to that too.

edit2:

For comparison this is what imageworsener does:

https://imgur.com/a/XmJKQnz

You really need to open the image in a viewer where each image pixel is exactly one device pixel large, otherwise the color arithmetic used for scaling by viewers is of variable quality (often very poor).

nextts 5 hours ago
> We have just committed a mortal sin of image processing. I didn’t notice it, you might not have noticed either, but colour-space enthusiasts will be knocking on your door shortly.
obrhubr 2 days ago
Thanks for your comment! I'm glad you're seeing the same thing :) I re-implemented the linearised dithering in python and got similar results. I checked and rechecked the colour profiles in GIMP, nothing... At this point I can only hope for an expert to appear and tell me what exactly I am doing wrong.
mkesper 4 hours ago
Did you try any of the OKlab color space implementations for calculating? https://bottosson.github.io/posts/oklab/
contravariant 2 hours ago
The linearized gradient does look off, but not because it is linearized. It is simply wrong.

The dithered gradient shouldn't be pure black halfway through.

Sesse__ 5 hours ago
For perceptual color difference, there are much better metrics than “distance in linear RGB”. CIE has some implementations of a metric called ΔE*, for instance.

I don't know if they actually do well in dithering, though. My experience with dithering is that it actually works better in gamma space than trying to linearize anything, since the quantization is fundamentally after gamma.

spacejunkjim 2 hours ago
When I saw this, I immediately had flashbacks to a little project I did for my CS course when I was an undergrad! We were all assigned a computer graphics algorithm and were tasked to build an animation explaining how it works.

This was nearly eight years ago, but I managed to find it this morning and uploaded it to YouTube.

Here was the resulting animation: https://youtu.be/FHrIQOWeerg

I remember I used Processing to build it, and it took so long to animate as I had to export it frame-by-frame. Fun days!

kaoD 3 hours ago
> Dithering a black-to-white gradient will be wrong without linearising first.

TBH both look wrong to me. If I squint, neither dithering patterns match the original gradient... but the non-linearized one looks the most similar.

What could be causing this?

Retr0id 13 minutes ago
You're looking at a scaled version of the bitmap (potentially re-scaled multiple times) and some or all of those interpolations may not have been done in a linear colour space.

But in this case I think it's just wrong. The entire first 40% of the bar is black, and I don't think it should be.

shiandow 1 hour ago
They seem to be using some kind of error diffusion. And getting error diffusion to play nice with linear colour space is nontrivial.

I remember I had quite a bit of discussion with madshi when MadVR tried implementing it. You can do something that comes close by modifying the colour space into something that is gamma light in the integer part and linear light in the fractional part.

If the value of a pixel is x you then get something like floor(x) + (l - ginv(x)) / (l - u) with l and u the the two shades corresponding to floor(x) and ceil(x) in linear light.

Though technically error diffusion will still be incorrect, but it does handle constant shades correctly and most alternatives are worse somehow.

TinkersW 2 hours ago
I don't know where they got the idea you don't dither in srgb, the point of dithering is to map it to the nearest bit pattern with a random adjustment so that it could go either way(aside from artistic choice), you should dither in srgb if you are going to display it in srgb, which is probably why the "not linearized" version looks more accurate.

See: Dithering should happen in sRGB https://www.shadertoy.com/view/NssBRX

badmintonbaseba 2 hours ago
Apart from implementing it incorrect, an uncalibrated display could also cause this. Check out http://www.lagom.nl/lcd-test/gamma_calibration.php with DPI scaling turned off, at 100% zoom level (how browsers scale images are also horrible, so you want to avoid that).

edit:

Reading back, viewing the gradients also not at 100% zoom level could also itself cause the mismatch, because browsers just suck at image scaling.

gus_massa 2 hours ago
Mac vs pc?

They have a different default gamma and they may show a different gray level.

(It bite me a long time ago. I made a gif that has the same RGB bacground than a webpage. In my pc it was fine, but in a mac they the border was very visible and the result horrible. My solution was to change the backgroung of the webpage from a RGB number to a 1 pixel gif with repetition or scale to fill the page.)

hagbard_c 3 hours ago
> What could be causing this?

Hypercorrection, in this care over-linearisation.

magicalhippo 3 hours ago
I've always been curious to what degree, if any, color constancy[1] affects color dithering.

Seems that at some level it should, though perhaps not directly at the pixel level due to the high frequency of the per-pixel differences, but maybe at the more coarse "averaged" level?

One of those things I've wanted to explore but remains on my to-do list...

[1]: https://en.wikipedia.org/wiki/Color_constancy

yapyap 4 hours ago
Dithering is so neat.
oniony 3 hours ago