Jim's Dither Library
 
 
 
 
Check out the development journal and join the community by visiting the JimDither LiveJournal community!

Well, a while ago I wanted to make a program that could display 4 level grayscale images on the TI-89. The actuall TI-89 display software was really easy (if you use TIGCC the stuff for doing it is actually built into the standard libraries.) And getting a basic 4 grayscale image was also easy. But it had it's flaw: standard quantitization isn't very good looking.

Starting Image 4 Gray Raw Quantitization

So obviously this necessitated some kind of solution. But worse yet, I needed to be able to do it myself since the way grayscale is done on the TI-89 is not just a simple 2 bit palette, instead it's handled as 2 discrete planes (A light plane and a dark plane.) And worse yet, it had to be converted into C style constants. When all of the different issues where considered, writing the image processor from scratch seemed to be the obvious path. The problem was that I had absolutely no idea how to go about doing that. So I did a bit of research and discovered the Floyd-Stienberg dithering algorithm.

Floyd-Stienberg is a specific kind of "spatial color distribution" using error diffusion. (Oooh! fancy words.) What this basically boils down to is that it takes the nearest grayscale color, then solves for how much of the brightness had to be left off (Or extra brightness added), and this difference (called the error) is spread out to some of the neighboring pixels. When it becomes their turn, this extra intensity is added in, and it makes the image appear to be reasonably continuous grayscale even though the actual number of grays is much smaller. What makes the Floyd-Steinberg algorithm special is how much of the error is spread out, and to where. In the Floyd-Steinberg method, the error is broken up into 16 parts and they're spread out as is shown in the picture below.

Floyd-Steinberg Error Distribution

So I sat down and wrote a program that applied the basic method. Using floating point values and the Bitmap object's getpixel and setpixel (In VB.Net), I was able to make a program that could render an image in only a couple of seconds. The image quality was superb. The only problem was that it took almost a full second to process an image for a Ti-89 (160x100).

4 Gray Floating Point Floyd-Steinberg

This is just too slow for large images. So afetr discovering some good ways to access bitmaps directly, I was able to greatly reduce the rendering time (about 1.682 Milliseconds per pixel on a 2GHz 200 MHz FSB P4) But it still wan't fast enough when dealing with larger images, so I decided that I should develop an all integer method of implementing the Floyd-Steinberg distribution. This obviously lead to a couple of sacrafices, rounding errors being the most obvious. Since the algorithim couldn't depend on floating point methods for anything, it ment that I had to use integer division in several places. This became especially important when implementing the error distribution algorithm. However, time and refinement have been able to minimize the impact of these flaws and now they are only slightly noticeable as a tendancy for the dots of a given color to line up in vertical columns when working with a gradient.

4 Gray Floating Point Floyd-Steinberg 4 Gray Integer Floyd-Steinberg
Notice the tendancy for the dots on the top left to arrange themselves into vertical lines

Now seeing as those algoritms are very sequential and basically parse straight through a chunk of memory in a sequential fashion, it only seemed logical that working with a managed memory programming language (VB.Net in this case) really wan't practical for the actual processing algorithms. So a C DLL was created to interface with the VB.Net application so that the data could be manipulated with the far more efficient progarmming techniques that C allows for when working with this kind of data. Also, because it's working with it in an unmanaged context, a whole bunch of the safety checks can be turned off if the library is written VERY carefully. So I rewrote the algorithms using pointer optimized C and was able to yield a MAJOR increase in speed. However, it's still getting it's imput from the VB.Net app so as to take advantage of the extremely simple GUI programming and also to take advantage of the file load algorithms ('Cause I REALLY don't think I have the patience to write a JPEG decompressor. ^.^;;;)

Speed Comparison for Current Algorithms

Time (In Nanoseconds)
Algorithm VB.Net VB.Net
Floating Point
C DLL C DLL
Floating Point
Raw Quantitization
(4 Grays)
193 N/A 111 N/A
Floyd-Steinberg
(4 Grays)
268 1650 134 195
Palettized Floyd-Steinberg
(EGA Palette)
339 4889 330 468
Tests done on 100 iterations of the test.jpg image

Now I'm still working on these algorithms, and several more, but at the very least your more than welcome to get what I've got. Don't expect much, it's not really intended for distribution (I do plan to change this BTW) so it has almost no appropriate documentation. But you're more than welcome to look at it and comment on it. Send your Questions/Comments jtc@nqig.net.

Accessed: 3:14:45 8/01/10 MST Last Update: 22:58:04 1/27/05 MST
 
 
 
 
 
Viewable in HTML 4.0 Transitional and CSS 1.0 compliant browers.

© 2001-2010 JimTheCactus Click for Legal Info
All resemblance to real grammar is purely coincidental. The spellings have been changed to protect the literate.