In my previous post, I described how to dither an image in C# using the Floyd‑Steinberg algorithm. Continuing this theme, this post will cover the Burkes algorithm.
An example of 1bit conversion via a threshold
I will be using the same demonstration application as from the previous post, so I won't go over how this works again.
Burkes dithering
As with Floyd‑Steinberg, the Burkes algorithm is an error diffusion algorithm, which is to say for each pixel an "error" is generated and then distributed to pixels around the source. Unlike Floyd‑Steinberg however (which modifies 4 surrounding pixels), Burkes modifies 7 pixels.
Burkes is actually a modified version of the Stucki algorithm, which in turn is an evolution of the Jarvis algorithms.
The diagram below shows the distribution of the error coefficients.
How the error of the current pixel is diffused to its neighbours
8 for the pixel to the right of the current pixel
4 for the second pixel to the right
2 for the pixel below and two to the left
4 for the pixel below and to the left
8 for the pixel below
4 for the pixel below and to the right
2 for the pixel below and two to the right
Unlike Floyd‑Steinberg, the error result in this algorithm is divided by 32. But as that's still a power of two, once again we can use bit shifting to perform the division.
Due to the additional calculations I would assume that this algorithm will be slightly slower than Floyd‑Steinberg, but as of yet I haven't ran any form of benchmarks to test this.
Applying the algorithm
In my Floyd‑Steinberg example, I replicated the calculations four times for each pixel. As there are now seven sets of calculations with Burkes, I decided to store the coefficients in a 2D array mimicing the diagram above, then iterating this. I'm not entirely convinced this is the best approach, but it does seem to be working.
This sets up the matrix as a static that is only created once. I've also added some constants to control the offsets as I can't create an array with a non-zero lower bound. This does smell a bit so I'll be revisiting this!
Below is the code to dither a single pixel. Remember that the demonstration program uses a 1D array of ArgbColor structs to make it easy to read and understand, but you could equally use direct pointer manipulation on a bitmap's bits, with lots of extra code to handle different colour depths.
Due to the loop this code is now shorter than the Floyd‑Steinberg version. It's also less readable due the coefficients being stored in a 2D matrix. Of course, the algorithm is fixed and won't change so perhaps that's not an issue, but if performance really was a concern you can unroll the loop and duplicate all that code. I'll stick with the loop!
Final Output
The image below shows our sample image dithered using the Burkes algorithm. It's very similar to the output created via Floyd–Steinberg, albeit darker.
The final result - a bitmap transformed with Burkes dithering
Again, by changing the threshold at which colours are converted to black or white, we can affect the output of the dithering even if the conversion is to solid black.
The non-dithered version of this image is solid black
Source Code
The latest source code for this demonstration (which will be extended over time to include additional algorithms) can be found at our GitHib page.
The source code from the time this article was created is available from the link below, however may not be fully up to date.
Update History
2015-06-06 - First published
2020-11-21 - Updated formatting
2024-08-10 - Corrected the blue and green error values being used in reverse, thanks to Kevin Cahalan for discovering this
Like what you're reading? Perhaps you like to buy us a coffee?
The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game - but until that time comes, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.
Although I should really be working on adding the dithering algorithms into Gif Animator, I thought it would be useful to expand the repertoire of algorithms available for use with such tools. This article briefly mentions the expanded error diffusion algorithms that are now included, and in slightly more detail covers random dithering as well.
In my previous introductory post, I briefly described the concept of dithering an image. In this article, I will describe how to dither an image in C# using the Floyd–Steinberg algorithm.
When you reduce the number of colours in an image, it's often hard to get a 1:1 match, and so typically you can expect to see banding in an image - areas of unbroken solid colours where once multiple similar colours were present. Such banding can often ruin the look of the image, however by using dithering algorithms you can reduce such banding and greatly improve the appearance of the reduced image. This article briefly discusses dithering as a prelude to further articles with actual dithering implementations.