5
\$\begingroup\$

I want to achieve color dithering between discrete colors like this:

enter image description here

As you can see, there is no gradient, or technically there is, but very discrete as it's only a 3 color image (right bar).

So I want the same results applying ordered dither (or at least try it and see if it works).

I have this test sample:

Undithered

And this is teh function where i do the dithering:

// OrderedDither applies a Bayer matrix to dither.
func OrderedDither(img image.Image, palette []color.Color) image.Image {

rImage := image.NewRGBA(img.Bounds())

matrix := [][]float64{
    {0, 4, 1, 5},
    {8, 12, 9, 13},
    {2, 6, 3, 7},
    {10, 6, 11, 15}}

for j := 0; j < img.Bounds().Max.Y; j++ {
    for i := 0; i < img.Bounds().Max.X; i++ {
        m := matrix[i%4][j%4] / 16
        oldPixel, _ := colorful.MakeColor(img.At(i, j))
        oldPixel.R = oldPixel.R*m + oldPixel.R
        oldPixel.G = oldPixel.G*m + oldPixel.G
        oldPixel.B = oldPixel.B*m + oldPixel.B
        newPixel := GetClosestColor(palette, oldPixel)
        r, g, b, _ := newPixel.RGBA()

        rImage.Set(i, j, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
    }
}
return rImage

}

I get this:

Sucky

As you can see, there is not dithering in the borders and the dithering inside the boxes is awful.

Any recommendations?

Edit: I implemented the average distance in the 16 color palette:

r := AverageColorDistance(palette)

for j := 0; j < img.Bounds().Max.Y; j++ {
    for i := 0; i < img.Bounds().Max.X; i++ {
        m := matrix[i%4][j%4]/16 - 0.5
        oldPixel, _ := colorful.MakeColor(img.At(i, j))
        oldPixel.R = oldPixel.R + r*m
        oldPixel.G = oldPixel.G + r*m
        oldPixel.B = oldPixel.B + r*m
        newPixel := GetClosestColor(palette, oldPixel)
        r, g, b, _ := newPixel.RGBA()

        rImage.Set(i, j, color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255})
    }
}

And the result was not satisfactory neither:

The average color was 0.4792891. (got 256 values in a 16 color palette).

enter image description here

But in either case i still got no blur between the boxes. Should i stop trying to make Ordered work and go Floyd-Steinberg? I want to get some blur between the hard edges.

\$\endgroup\$
1
\$\begingroup\$

You have a small mistake in there. First of all, you have to subtract 1/2 from m to make it centered around 8 in this case. Secondly, instead of multiplying m with the r, g and b components of the original color, you have to multiply it by the average distance between the colors. For instance if you have three colors, (0,0,0), (100,0,0) and (255,0,0), then the average distance is

r = ((100 - 0) + (255 - 100) + (255 - 0)) / 3 = 170

For different r, g and b valies, use the 3d extension of the pythagorean theorem to calculate distance.

So basically, instead of

oldPixel.R = oldPixel.R * m + oldPixel.R

do

oldPixel.R = oldPixel.R + r * m
\$\endgroup\$
  • \$\begingroup\$ Thanks. But the average distance between what colors? All in my palette? All in the original image? \$\endgroup\$ – Gabriel A. Zorrilla Jul 16 '18 at 2:50
  • \$\begingroup\$ @Gabriel All your palette \$\endgroup\$ – Bálint Jul 16 '18 at 11:00
  • \$\begingroup\$ did your suggestions, check my edit please! \$\endgroup\$ – Gabriel A. Zorrilla Jul 16 '18 at 16:13

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.