fellow devs!
I am trying to dither a 8-bit grayscale image to a 2-bit / 4 color image with an ordered dithering based on a Bayer 8×8 matrix. My result is somewhat there, but not quite. I kept banging my head at the problem but can’t figure out where I went wrong so any help would be greatly appreciated.
Here’s my code:
// image_array_2d is a 2d array of grayscale values from 0-255
// matrix is a 2d array with values normalized to be between 0.0 and 1.0
// LUT is an array of 8-bit values, in this case [0, 85, 170, 255]
const MAX = 255
const image_height = image_array_2d.length
const image_width = image_array_2d[0].length
for (let y = 0; y < image_height; y++)
{
for (let x = 0; x < image_width; x++)
{
const threshold = (matrix[y % 8][x % 8]) * MAX
let pixel_value = image_array_2d[y][x];
//pixel_value = gamma_correct(pixel_value)
// find closest value to pixel in LUT
let value_current, value_prev
let closest = lut[0]
for (let i = 0; i < lut.length; i++)
{
const closestDifference = Math.abs(closest - pixel_value);
const currentDifference = Math.abs(lut[i] - pixel_value);
if (currentDifference < closestDifference)
{
value_prev = lut[i-1]
value_current = lut[i]
closest = value_current
}
}
let new_value = 0
new_value = pixel_value > threshold ? value_current : value_prev
new_value = pixel_value >= MAX ? MAX : new_value
image_array_2d[y][x] = new_value
}
}
Just for safety, that’s my Bayer matrix before its values get normalized to 0.0 – 1.0
[ 0, 32, 8, 40, 2, 34, 10, 42],
[48, 16, 56, 24, 50, 18, 58, 26],
[12, 44, 4, 36, 14, 46, 6, 38],
[60, 28, 52, 20, 62, 30, 54, 22],
[ 3, 35, 11, 43, 1, 33, 9, 41],
[51, 19, 59, 27, 49, 17, 57, 25],
[15, 47, 7, 39, 13, 45, 5, 37],
[63, 31, 55, 23, 61, 29, 53, 21]
(The following images have been scaled up by 400% for convenience)
This is the image I test my algorithm with:
And this is the intended target:
However, this is the result of the above code with the LUT array being [0, 85, 170, 255]
EDIT: Here’s the code pen link:
► https://codepen.io/PixelProphet/pen/JjxLejZ
The function in question is ordered_dither()
2
Answers
It appears that your approach is on the right track, but there are potential issues in your code. Here are some suggestions to enhance your Bayer matrix-based dithering algorithm:
Ensure that your Bayer matrix is correctly normalized between 0 and 1. Currently, you haven’t included the normalization process in the provided code. You need to divide each element of the matrix by the maximum value in the matrix (63 in this case).
The loop for searching in the Look-Up Table (LUT) seems to be offset. Ensure that you are not accessing negative indices in the LUT. Modify your loop to avoid this issue.
Make sure that you are applying dithering correctly by comparing the pixel value with the Bayer matrix threshold.
With these adjustments, your dithering algorithm should work more accurately. Remember to test it with different images and fine-tune the parameters as needed.
I noticed a few issues with your code. The general theme is: you think of color values instead of color ranges.
lut
, instead of searching for an interval your pixel falls into. Remember, you need two colors to dither, not just one.I fixed these issues in your code and it is now working.