Dithering in MATLAB

Alec Jacobson

February 08, 2011

weblog/

Although I original set out to find/implement a halftone algorithm in MATLAB, I got side-tracked having fun with dithering. MATLAB is unfortunately a poor choice of programming paradigm for most of the standard dithering algorithms since they use a process called error diffusion: iterate from one pixel to the next, round it to the nearest color in your palette and pass on the error to neighboring pixels that have not yet been processed. Being iterative this can run very slow in MATLAB for large images. I briefly tried to imagine parallelizing this, but quickly decided to use random dithering instead. Here, every pixel is rounding up or down to the nearest color based its proximity compared against a random number. The big disadvantage of this method is that it tends to introduce so much static that the original detail is lost. Playing around with it I found a couple simple ways to retrieve some of the detail and experimented with some methods of adding expression. So here are some results in dithering (turning a grayscale image into a binary image, where each pixel is either white or black):

Original image

original dithering image
im = im2double(imread('max-schmeling.jpg'));

Threshold

threshold dithering
th = im > 0.5;
Thresholding is usually what dithering is trying to improve upon. Details in large grey areas are lost and sharp edges appear where they may have been smooth gradients in the original image. In terms of random dithering we can think of the threshold result as "random" dithering where the "random" number is always 0.5.

Random

random dithering
ra = im > rand(size(im));
The simplest true, random dithering. Pick a truly random number for each pixel and compare the value against it.

High contrast, random

random high constrast dithering
sp = -(im).*(im).*(im-1.5)*2>rand(size(im));
Here, I crank up the contrast of the original image before computing the random dithering as above. This sharpens the detail a little, though there is still a lot of static.

Random dithering and threshold, randomly mixed

random dithering and threshold, randomly mixed
rr = round(rand(size(im)));
rara = rr.*round(im)+(1-rr).*(im > rand(size(im)));
Here, I compute the threshold image and the random dithering image then randomly mix them. This has a similar effect as the high contrast random dithering, but with a very high contrast, almost cartoony, look.

Random dithering and threshold, randomly mixed except near edges

random dithering and threshold, randomly mixed except near edges
e = edge(im,0.05);
blur_width=5;
h = fspecial('gaussian',round(blur_width),round(blur_width));
blurred_e=ceil(imfilter(e+0.0,h,'replicate'));
rre = (1-((1-rr).*(1-blurred_e)));
rarae = rre.*round(im)+(1-rre).*(im > rand(size(im)));
Here, I first compute an image that tells me where strong edges occur in the original image. I blur this a bit so that the image tells me if I'm near a strong edge. Then when I mix the threshold image and the random dithering image, as I did above, I only mix them randomly if I'm not near an edge. If I'm near an edge I use the threshold image. This has a very nice cartoony effect.