Skip to content

Instantly share code, notes, and snippets.

@robertlugg
Created September 16, 2023 23:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertlugg/f0b618587c2981b744716999573c5b65 to your computer and use it in GitHub Desktop.
Save robertlugg/f0b618587c2981b744716999573c5b65 to your computer and use it in GitHub Desktop.
# Source
Original source no longer exists but was apparently:
http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT,
based on a stackoverflow discussion:
https://stackoverflow.com/a/9150427/2184122
There are several authors. I am capturing the file because I think it is of great value. However, I am happy to remove it if requested.
The authors are: Lee Daniel Crocker, Paul Boulay, and Mike Morra
The following text was taken from:
https://web.archive.org/web/20180927070416/http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT
on September 2023.
DHALF.TXT
June 20, 1991
Original name: DITHER.TXT
Original date: January 2, 1989
=====================================
ORIGINAL FOREWORD BY LEE CROCKER
What follows is everything you ever wanted to know (for the time being)
about digital halftoning, or dithering. I'm sure it will be out of date as
soon as it is released, but it does serve to collect data from a wide
variety of sources into a single document, and should save you considerable
searching time.
Numbers in brackets (e.g. [4] or [12]) are references. A list of these
works appears at the end of this document.
Because this document describes ideas and algorithms which are constantly
changing, I expect that it may have many editions, additions, and
corrections before it gets to you. I will list my name below as original
author, but I do not wish to deter others from adding their own thoughts and
discoveries. This is not copyrighted in any way, and was created solely
for the purpose of organizing my own knowledge on the subject, and sharing
this with others. Please distribute it to anyone who might be interested.
If you add anything to this document, please feel free to include your name
below as a contributor or as a reference. I would particularly like to see
additions to the "Other books of interest" section. Please keep the text in
this simple format: no margins, no pagination, no lines longer than 79
characters, and no non-ASCII or non-printing characters other than a CR/LF
pair at the end of each line. It is intended that this be read on as many
different machines as possible.
Original Author:
Lee Daniel Crocker [73407,2030]
Contributors:
Paul Boulay [72117,446]
Mike Morra [76703,4051]
=====================================
COMMENTS BY MIKE MORRA
I first entered the world of imaging in the fall of 1990 when my employer,
Epson America Inc., began shipping the ES-300C color flatbed scanner.
Suddenly, here I was, a field systems analyst who had worked almost
exclusively with printers and PCs, thrust into a new and arcane world of
look-up tables and dithering and color reduction and .GIF files! I realized
right away that I had a lot of catching up to do (and it needed to be done
quickly), so I began to frequent the CompuServe Information Service's
Graphics Support Forum on a very regular basis.
Lee Crocker's excellent paper called DITHER.TXT was one of the first pieces
of information that I came across, and it went a very long way toward
answering a lot of questions that I'd had about the subject of dithering.
It also provided me with the names of other essential reference works upon
which Lee had based his paper, and I immediately began an eager search for
these other references.
In the course of my self-study, however, I found that DITHER.TXT does
presume the reader's familiarity with some fundamental imaging concepts,
which meant that I needed to do a little "cramming." I get the impression
that Lee was directing his paper more toward graphics programmers than to
complete neophytes like me. I decided that I would rewrite and append to
DITHER.TXT and try to incorporate some of the more elementary information
that I'd absorbed along the way. In doing so, I hope that it will make it
even more comprehensive, and thus even more useful to first-time users.
I elected to rename the revised file and chose the name DHALF.TXT in homage
to the term "digital halftoning," as used in Robert Ulichney's splendid
reference work. Notwithstanding, this paper is still very much Lee's
original work, and I certainly do not propose that I have created something
new and original here. It is also quite possible that in changing the
presentation of some of the material therein, I may have unwittingly
corrupted Lee's original intent and delivery, and this was also not my
intention.
Accordingly, I've submitted this paper to the Graphics Support Forum as a
draft work only, at least for the time being. Quite honestly, I don't know
whether it would be appropriate as a replacement to DITHER.TXT, or as a
second, distinct document. Too, I may very well have misconstrued or
misinterpreted some factual information in my revision. As such, I welcome
criticism and comment from all the original authors and contributors, and
any readers, with the hope that their feedback will help me to address these
issues.
If this revision it is received favorably, I will submit it to the public
domain; if it is met with brickbats (for whatever reason), I will withdraw
it. Whatever the outcome, though, it will at least represent a very
rewarding learning experience on my part!
With the unselfish help of many of the denizens of the Graphics Support
Forum, I was ultimately able to thrash out (in my own mind) the answers to
my questions that I needed. I'd like to publicly thank the whole Forum
community in general for putting up with my unending barrage of questions
and inquiries over the past few months <g>. In particular, I would thank
John Swenson, Chris Young, and (of course) Lee Crocker for their invaluable
assistance.
Mike Morra [76703,4051]
June 20, 1991
=====================================
What is Digital Halftoning?
Throughout much of the course of computer imaging technology, experimenters
and users have been challenged with attempting to acceptably render
digitized images on display devices which were incapable of reproducing the
full spectrum of intensities or colors present in the source image. The
challenge is even more pronounced in today's world of personal computing
because of the technology gap between image generation and image rendering
equipment.
Today, we now have affordable 24-bit image scanners which can generate
nearly true-to-life scans having as many as 256 shades of gray, or in excess
of 16.7 million colors. Mainstream display technology, however, still lags
behind with 16- and 256-color VGA/SVGA video monitors and printers with
binary (black/white) "marking engines" as the norm. Without specialized
techniques for color reduction -- the process of finding the "best fit" of
the display device's available gray shades and/or colors -- the imaging
experimenter would be plagued with blotchy, noisy, off-color images.
(As of this writing, "true color" 24-bit video display devices, capable of
reproducing all of the color/intensity information in the source image, are
now beginning to migrate downward into the PC environment, but they exact a
premium in cost and processor power which many users are loathe to pay. So-
called "high-color" video displays -- typically 16-bit, with 32,768-color
capability -- are moving into the mainstream, but color reduction techniques
would still be required with these devices.)
The science of digital halftoning (more commonly referred to as dithering,
or spatial dithering) is one of the techniques used to achieve satisfactory
image rendering and color reduction. Initially, it was principally
associated with the rendering of continuous-tone (grayscale) images on
"binary" (i.e. 1-bit) video displays which could only display full black or
full white pixels, or on printers which could produce only full black spots
on a printed page. Indeed, Ulichney [3] gives a definition of digital
halftoning as "... any algorithmic process which creates the illusion of
continuous-tone images from the judicious arrangement of binary picture
elements."
Ulichney's study, as well as the earlier literature on the subject (and this
paper itself), discusses the process mostly in this context. Since we in
the PC world are still saddled primarily with black/white marking engines in
our hardcopy devices, this binary interpretation of digital halftoning is
still very pertinent. However, as we will see later in this discussion, the
concept can also be extended to include display devices (typically video
monitors) which support limited grayscale or color palettes. Accordingly,
we can broaden the traditional definition of digital halftoning to refer to
rendering an image on any display device which is unable to show the entire
range of colors or gray shades that are contained in the source image.
=====================================
Intensity/Color Resolution
The concept of resolution is essential to the understanding of digital
halftoning. Resolution can be defined as "fineness" and is used to
describe the level of detail in a digitally sampled signal.
Typically, when we hear the term "resolution" applied to images, we think of
what's known as "spatial resolution," which is the basic sampling rate for
the image. It describes the fineness of the "dots" (pixels or ink/toner
spots) which comprise the image, i.e. how many of them are present along
each horizontal and vertical inch. However, we can also speak of "intensity
resolution" or "color resolution," which describes the fineness of detail
available at each spot, i.e. the number of different gray shades or colors
in the image. (I will go back and forth between the two terms depending on
the type of image being discussed, but the reader should be aware that the
concepts are analogous to each other.)
As you might expect, the higher the resolution of a digital sample, the
better it can reproduce high frequency detail in the particular domain
described by that resolution. A VGA display, for example, has a relatively
good spatial resolution of 640 x 480 and a relatively poor color resolution
of 8 bits (256 colors). By comparison, an NTSC color television receiver
has a spatial resolution of approximately 350 x 525 and an excellent, nearly
infinite color resolution. Thus, images rendered on a VGA screen will be
quite sharp, but rather blotchy in color. The same image displayed on the
television receiver will not be as crisp, but will have much more accurate
color rendition.
It is often possible to "trade" one kind of resolution for another. If your
display device has a higher spatial resolution than the image you are trying
to reproduce, it can show a very good image even if its color resolution is
less. This is what most of us know as "dithering" and is the subject of
this paper. (The other tradeoff, i.e., trading color resolution for spatial
resolution, is called "anti-aliasing," and is not discussed here.)
For the following discussions I will assume that we are given a grayscale
image with 256 shades of gray, which are assigned intensity values from 0
(black) through 255 (white), and that we are trying to reproduce it on a
black and white output device, e.g. something like an Epson impact dotmatrix
printer, or an HP LaserJet laser printer. Most of these methods can be
extended in obvious ways to deal with displays that have more than two
levels (but still fewer than the source image), or to color images. Where
such extension is not obvious, or where better results can be obtained, I
will go into more detail.
=====================================
Fixed Thresholding
A good place to start is with the example of performing a simple (or fixed)
thresholding operation on our grayscale image in order to display it on our
black and white device. This is accomplished by establishing a demarcation
point, or threshold, at the 50% gray level. Each dot of the source image is
compared against this threshold value: if it is darker than the value, the
device plots it black, and if it's lighter, the device plots it white.
What happens to the image during this operation? Well, some detail
survives, but our perception of gray levels is completely gone. This means
that a lot of the image content is obliterated. Take an area of the image
which is made up of various gray shades in the range of 60-90%. After fixed
thresholding, all of those shades (being darker than the 50% gray threshold)
will be mapped to solid black. So much for variations of intensity.
Another portion of the image might show an object with an increasing,
diffused shadow across one of its surfaces, with gray shades in the range of
20-70%. This gradual variation in intensity will be lost in fixed
thresholding, giving way to two separate areas (one white, one black) and a
distinct, visible boundary between them. The situation where a transition
from one intensity or shade to another is very conspicuous is known as
contouring.
=====================================
Artifacts
Phenomena like contouring, which are not present in the source image but
produced by the digital signal processing, are called artifacts. The most
common type of artifact is the Moire' pattern. If you display or print an
image of several lines, closely spaced and radiating from a single point,
you will see what appear to be flower-like patterns. These are not part of
the original image but are an illusion produced by the jaggedness of the
display. We will encounter and discuss other forms of artifacts later in
this paper.
=====================================
Error Noise
Returning to our fixed-thresholded (and badly-rendered) image, how could we
document what has taken place to make this image so inaccurate? Expressing
it in technical terms, a relatively large amount of error "noise" is present
in the fixed-thresholded image. The error value is the difference between
the image's original intensity at a given dot and the intensity of the
displayed dot. Obviously, very dark values like 1 or 2 (which are almost
full black) incur very small errors when they are rendered as a 0 value
(black) dot. On the other hand, a gross error is incurred when a 129 value
dot (a medium gray) is displayed at 255 value (white), for instance.
Simply put, digital halftoning redistributes this "noise energy" in a way
which makes it less visible. This brings up an important concept: digital
halftoning does not INCREASE the noise energy. In some of the literature,
reference is made to the "addition of dither noise," which might give this
impression. This is not the case, however: effective digital halftoning
acts upon the low-frequency component of the error noise (the component
which contributes to graininess) and scatters it in higher-frequency
components where it is not as obvious.
=====================================
Classes of digital halftoning algorithms
The algorithms we will discuss in this paper can be subdivided into four
categories:
1. Random dither
2. Patterning
3. Ordered dither
4. Error-diffusion halftoning
Each of these methods is generally better than those listed before it, but
other considerations such as processing time, memory constraints, etc. may
weigh in favor of one of the simpler methods.
To convert any of the first three methods into color, simply apply the
algorithm separately for each primary color and mix the resulting values.
This assumes that you have at least eight output colors: black, red, green,
blue, cyan, magenta, yellow, and white. Though this will work for error
diffusion as well, there are better methods which will be discussed in more
detail later.
=====================================
Random dither
Random dithering could be termed the "bubblesort" of digital halftoning
algorithms. It was the first attempt (documented as far back as 1951) to
correct the contouring produced by fixed thresholding, and it has
traditionally been referenced for comparison in most studies of digital
halftoning. In fact, the name "ordered dither" (which will be discussed
later) was chosen to contrast random dither.
While it is not really acceptable as a production method, it is very simple
to describe and implement. For each dot in our grayscale image, we generate
a random number in the range 0 - 255: if the random number is greater than
the image value at that dot, the display device plots the dot white;
otherwise, it plots it black. That's it.
This generates a picture with a lot of "white noise", which looks like TV
picture "snow". Although inaccurate and grainy, the image is free from
artifacts. Interestingly enough, this digital halftoning method is useful
in reproducing very low-frequency images, where the absence of artifacts is
more important than noise. For example, a whole screen containing a
gradient of all levels from black to white would actually look best with a
random dither. With this image, other digital halftoning algorithms would
produce significant artifacts like diagonal patterns (in ordered dithering)
and clustering (in error diffusion halftones).
I should mention, of course, that unless your computer has a hardware-based
random number generator (and most don't), there may be some artifacts from
the random number generation algorithm itself. For efficiency, you can take
the random number generator "out of the loop" by generating a list of random
numbers beforehand for use in the dither. Make sure that the list is larger
than the number of dots in the image or you may get artifacts from the reuse
of numbers. The worst case would be if the size of your list of random
numbers is a multiple or near-multiple of the horizontal size of the image;
in this case, unwanted vertical or diagonal lines will appear.
As unattractive as it is, random dithering can actually be related to a
pleasing, centuries-old art know as mezzotinting (the name itself is an
Italianized derivative of the English "halftone"). In a mezzotint, the
skilled craftsman worked a soft metal (usually copper) printing plate, and
roughened or ground the dark regions of the image by hand and in a seemingly
random fashion. Analyzing it in scientific terms (which would surely insult
any mezzotinting artisan who might read this!) the pattern created is not
very regular or periodic at all, but the absence of low frequency noise
leads to a very attractive image without much graininess. A similar process
is still in use today, in the form of modern gravure printing.
=====================================
"Classical" halftoning
Let's take a short departure from the digital domain and look at the
traditional or "classical" printing technique of halftoning. This technique
is over a century old, dating back to the weaving of silk pictures in the
mid 1800's. Modern halftone printing was invented in the late 1800's, and
halftones of that period are even today considered to be attractive
renditions of their subjects.
Essentially, halftoning involves the printing of dots of different sizes in
an ordered and closely spaced pattern in order to simulate various
intensities. The early halftoning artisans realized that when we view a
very small area at normal viewing distances, our eyes perform a blending or
smoothing function on the fine detail within that area. As a result, we
perceive only the overall intensity of the area. This is known as spatial
integration.
Although the tools of halftoning (the "screens" and screening process used
to generate the varying dots of the printed image) have undergone
improvements throughout the years, the fundamental principles remain
unchanged. This includes the 45-degree "screen angle" of the lines of dots,
which was known even to the earliest halftone artisans as giving more
pleasing images than dot lines running horizontally and vertically.
=====================================
Patterning
This was the first digital technique to pay homage to the classical
halftone. It takes advantage of the fact that the spatial resolution of
display devices had improved to the point where one could trade some of it
for better intensity resolution. Like random dither, it is also a simple
concept, but is much more effective.
For each possible value in the image, we create and display a pattern of
pixels (which can be either video pixels or printer "spots") that
approximates that value. Remembering the concept of spatial integration, if
we choose the appropriate patterns we can simulate the appearance of various
intensity levels -- even though our display can only generate a limited set
of intensities.
For example, consider a 3 x 3 pattern. It can have one of 512 different
arrangements of pixels: however, in terms of intensity, not all of them are
unique. Since the number of black pixels in the pattern determines the
darkness of the pattern, we really have only 10 discrete intensity patterns
(including the all-white pattern), each one having one more black pixel than
the previous one.
But which 10 patterns? Well, we can eliminate, right off the bat, patterns
like:
--- X-- --X X--
XXX or -X- or -X- or X--
--- --X X-- X--
because if they were repeated over a large area (a common occurrence in many
images [1]) they would create vertical, horizontal, or diagonal lines.
Also, studies [1] have shown that the patterns should form a "growth
sequence:" once a pixel is intensified for a particular value, it should
remain intensified for all subsequent values. In this fashion, each pattern
is a superset of the previous one; this similarity between adjacent
intensity patterns minimizes any contouring artifacts.
Here is a good pattern for a 3-by-3 matrix which subscribes to the rules set
forth above:
--- --- --- -X- -XX -XX -XX -XX XXX XXX
--- -X- -XX -XX -XX -XX XXX XXX XXX XXX
--- --- --- --- --- -X- -X- XX- XX- XXX
This pattern matrix effectively simulates a screened halftone with dots of
various sizes. In large areas of constant value, the repetitive pattern
formed will be mostly artifact-free.
No doubt, the reader will realize that applying this patterning process to
our image will triple its size in each direction. Because of this,
patterning can only be used where the display's spatial resolution is much
greater than that of the image.
Another limitation of patterning is that the effective spatial resolution is
decreased, since a multiple-pixel "cell" is used to simulate the single,
larger halftone dot. The more intensity resolution we want, the larger the
halftone cell used and, by extension, the lower the spatial resolution.
In the above example, using 3 x 3 patterning, we are able to simulate 10
intensity levels (not a very good rendering) but we must reduce the spatial
resolution to 1/3 of the original figure. To get 64 intensity levels (a
very acceptable rendering), we would have to go to an 8 x 8 pattern and an
eight-fold decrease in spatial resolution. And to get the full 256 levels
of intensity in our source image, we would need a 16 x 16 pattern and would
incur a 16-fold reduction in spatial resolution. Because of this size
distortion of the image, and with the development of more effective digital
halftoning methods, patterning is only infrequently used today.
To extend this method to color images, we would use patterns of colored
pixels to represent shades not directly printable by the hardware. For
example, if your hardware is capable of printing only red, green, blue, and
black (the minimal case for color dithering), other colors can be
represented with 2 x 2 patterns of these four:
Yellow = R G Cyan = G B Magenta = R B Gray = R G
G R B G B R B K
(B here represents blue, K is black). In this particular example, there are
a total of 31 such distinct patterns which can be used; their enumeration is
left "as an exercise for the reader" (don't you hate books that do that?).
=====================================
Clustered vs. dispersed patterns
The pattern diagrammed above is called a "clustered" pattern, so called
because as new pixels are intensified in each pattern, they are placed
adjacent to the already-intensified pixels. Clustered-dot patterns were
used on many of the early display devices which could not render individual
pixels very distinctly, e.g. printing presses or other printers which smear
the printed spots slightly (a condition known as dot gain), or video
monitors which introduce some blurriness to the pixels. Clustered-dot
groupings tend to hide the effect of dot gain, but also produce a somewhat
grainy image.
As video and hardcopy display technology improved, newer devices (such as
electrophotographic laser printers and high-res video displays) were better
able to accurately place and size their pixels. Further research showed
that, especially with larger patterns, the dispersed (non-clustered) layout
was more pleasing. Here is one such pattern:
--- X-- X-- X-- X-X X-X X-X XXX XXX XXX
--- --- --- --X --X X-X X-X X-X XXX XXX
--- --- -X- -X- -X- -X- XX- XX- XX- XXX
Since clustering is not used, dispersed-dot patterns produce less grainy
images.
=====================================
Ordered dither
While patterning was an important step toward the digital reproduction of
the classic halftone, its main shortcoming was the spatial enlargement (and
corresponding reduction in resolution) of the image. Ordered dither
represents a major improvement in digital halftoning where this spatial
distortion was eliminated and the image could then be rendered in its
original size.
Obviously, in order to accomplish this, each dot in the source image must be
mapped to a pixel on the display device on a one-to-one basis. Accordingly,
the patterning concept was redefined so that instead of plotting the whole
pattern for each image dot, THE IMAGE DOT IS MAPPED ONLY TO ONE PIXEL IN THE
PATTERN. Returning to our example of a 3 x 3 pattern, this means that we
would be mapping NINE image dots into this pattern.
The simplest way to do this in programming is to map the X and Y coordinates
of each image dot into the pixel (X mod 3, Y mod 3) in the pattern.
Returning to our two patterns (clustered and dispersed) as defined earlier,
we can derive an effective mathematical algorithm that can be used to plot
the correct pixel patterns. Because each of the patterns above is a
superset of the previous, we can express the patterns in a compact array
form as the order of pixels added:
8 3 4 1 7 4
6 1 2 and 5 8 3
7 5 9 6 2 9
Then we can simply use the value in the array as a threshold. If the value
of the original image dot (scaled into the 0-9 range) is less than the
number in the corresponding cell of the matrix, we plot that pixel black;
otherwise, we plot it white. Note that in large areas of constant value, we
will get repetitions of the pattern just as we did with patterning.
As before, clustered patterns should be used for those display devices which
blur the pixels. In fact, the clustered-dot ordered dither is the process
used by most newspapers, and in the computer imaging world the term
"halftoning" has come to refer to this method if not otherwise qualified.
As noted earlier, the dispersed-dot method (where the display hardware
allows) is preferred in order to decrease the graininess of the displayed
images. Bayer [2] has shown that for matrices of orders which are powers of
two there is an optimal pattern of dispersed dots which results in the
pattern noise being as high-frequency as possible. The pattern for a 2x2
and 4x4 matrices are as follows:
1 3 1 9 3 11 These patterns (and their rotations
4 2 13 5 15 7 and reflections) are optimal for a
4 12 2 10 dispersed-dot ordered dither.
16 8 14 6
Ulichney [3] shows a recursive technique can be used to generate the larger
patterns. (To fully reproduce our 256-level image, we would need to use an
8x8 pattern.)
The Bayer ordered dither is in very common use and is easily identified by
the cross-hatch pattern artifacts it produces in the resulting display.
This artifacting is the major drawback of an otherwise powerful and very
fast technique.
=====================================
Dithering with "blue noise"
Up to this point in our discussion, we have (with the exception of dithering
with white noise) discussed digital halftoning schemes which rely on the
application of some fairly regular mathematical processes in order to
redistribute the error noise of the image. Unfortunately, the regularity of
these algorithms leads to different kinds of artifacting which detracts from
the rendered image. In addition, these images all tend to reflect the
display device's row-and-column dot pattern to some extent, and this further
contributes to the "mechanical" character of the output image.
Dithering with white noise, on the other hand, introduces enough randomness
to suppress the artifacting and the gridlike appearance, but the low-
frequency component of this noise introduces graininess.
Obviously, what is needed is a method which falls somewhere in the middle of
these two extremes. In theoretical terms, if we could take white noise and
remove its low-frequency content, this would be an ideal way to disperse the
error content of our image. Many of the digital halftoning developers,
making an analogy to the audio world, refer to this concept as dithering
with blue noise. (In audio theory, "pink noise," which is often used as a
diagnostic and testing tool, is white noise from which some level of high-
frequency content has been filtered.)
Alas, while an audio-frequency analog low-pass filter is a relatively simple
device to construct and operate, implementing a digital high-pass filter in
program code -- and one which operates efficiently enough so as not to
degrade display response time -- is no trivial task.
=====================================
Error-diffusion halftoning
After considerable research, it was found that a set of techniques known as
error diffusion (also termed error dispersion or error distribution)
accomplished this quite effectively. In fact, error diffusion generates the
best results of any of the digital halftoning methods described here. Much
of the low-frequency noise component is suppressed, producing images with
very little grain. Error-diffusion halftones also display a very pleasing
randomness, without the visual sensation of rows and columns of dots; this
effect is known as the "grid defiance illusion."
As in other areas of life, though, there ain't no such thing as a free
lunch. Error diffusion is, by nature, the slowest method of digital
halftoning. In fact, there are several variants of this technique, and the
better they get, the slower they are. However, one will realize a very
significant improvement in the quality of the processed images which easily
justifies the time and computational power required.
Error diffusion is very simple to describe. For each point in our image, we
first find the closest intensity (or color) available. We then calculate
the difference between the image value at that point and that nearest
available intensity/color: this difference is our error value. Now we
divide up the error value and distribute it to some of the neighboring image
areas which we have not visited (or processed) yet. When we get to these
later dots, we add in the portions of error values which were distributed
there from the preceding dots, and clip the cumulative value to an allowed
range if needed. This new, modified value now becomes the image value that
we use for processing this point.
If we are dithering our sample grayscale image for output to a black-and-
white device, the "find closest intensity/color" operation is just a simple
thresholding (the closest intensity is going to be either black or white).
In color imaging -- for instance, color-reducing a 24-bit true color Targa
file to an 8-bit, mapped GIF file -- this involves matching the input color
to the closest available hardware color. Depending on how the display
hardware manages its intensity/color palette, this matching process can be a
difficult task. (This is covered in more detail in the "Color issues"
section later in this paper.)
Up till now, all other methods of digital halftoning were point operations,
where any adjustments that were made to a given dot had no effect on any of
the surrounding dots. With error diffusion, we are doing a "neighborhood
operation." Dispersing the error value over a larger area is the key to the
success of these methods.
The different ways of dividing up the error can be expressed as patterns
called filters. In the following sections, I will list a number of the most
commonly-used filters and some info on each.
=====================================
The Floyd-Steinberg filter
This is where it all began, with Floyd and Steinberg's [4] pioneering
research in 1975. The filter can be diagrammed thus:
* 7
3 5 1 (1/16)
In this (and all subsequent) filter diagrams, the "*" represents the pixel
currently being scanning, and the neighboring numbers (called weights)
represent the portion of the error distributed to the pixel in that
position. The expression in parentheses is the divisor used to break up the
error weights. In the Floyd-Steinberg filter, each pixel "communicates"
with 4 "neighbors." The pixel immediately to the right gets 7/16 of the
error value, the pixel directly below gets 5/16 of the error, and the
diagonally adjacent pixels get 3/16 and 1/16.
The weighting shown is for the traditional left-to-right scanning of the
image. If the line were scanned right-to-left (more about this later), this
pattern would be reversed. In either case, the weights calculated for the
subsequent line must be held by the program, usually in an array of some
sort, until that line is visited later.
Floyd and Steinberg carefully chose this filter so that it would produce a
checkerboard pattern in areas with intensity of 1/2 (or 128, in our sample
image). It is also fairly easy to execute in programming code, since the
division by 16 is accomplished by simple, fast bit-shifting instructions
(this is the case whenever the divisor is a power of 2).
=====================================
The "false" Floyd-Steinberg filter
Occasionally, you will see the following filter erroneously called the
Floyd-Steinberg filter:
* 3
3 2 (1/8)
The output from this filter is nowhere near as good as that from the real
Floyd-Steinberg filter. There aren't enough weights to the dispersion,
which means that the error value isn't distributed finely enough. With the
entire image scanned left-to-right, the artifacting produced would be
totally unacceptable.
Much better results would be obtained by using an alternating, or
serpentine, raster scan: processing the first line left-to-right, the next
line right-to-left, and so on (reversing the filter pattern appropriately).
Serpentine scanning -- which can be used with any of the error-diffusion
filters detailed here -- introduces an additional perturbation which
contributes more randomness to the resultant halftone. Even with serpentine
scanning, however, this filter would need additional perturbations (see
below) to give acceptable results.
=====================================
The Jarvis, Judice, and Ninke filter
If the false Floyd-Steinberg filter fails because the error isn't
distributed well enough, then it follows that a filter with a wider
distribution would be better. This is exactly what Jarvis, Judice, and
Ninke [6] did in 1976 with their filter:
* 7 5
3 5 7 5 3
1 3 5 3 1 (1/48)
While producing nicer output than Floyd-Steinberg, this filter is much
slower to implement. With the divisor of 48, we can no longer use bit-
shifting to calculate the weights but must invoke actual DIV (divide)
processor instructions. This is further exacerbated by the fact that the
filter must communicate with 12 neighbors; three times as many in the Floyd-
Steinberg filter. Furthermore, with the errors distributed over three
lines, this means that the program must keep two forward error arrays, which
requires extra memory and time for processing.
=====================================
The Stucki filter
P. Stucki [7] offered a rework of the Jarvis, Judice, and Ninke filter in
1981:
* 8 4
2 4 8 4 2
1 2 4 2 1 (1/42)
Once again, division by 42 is quite slow to calculate (requiring DIVs).
However, after the initial 8/42 is calculated, some time can be saved by
producing the remaining fractions by shifts. The Stucki filter has been
observed to give very clean, sharp output, which helps to offset the slow
processing time.
=====================================
The Burkes filter
Daniel Burkes [5] of TerraVision undertook to improve upon the Stucki filter
in 1988:
* 8 4 The Burkes filter
2 4 8 4 2 (1/32)
Notice that this is just a simplification of the Stucki filter with the
bottom row removed. The main improvement is that the divisor is now 32,
which allows the error values to be calculated using shifts once more, and
the number of neighbors communicated with has been reduced to seven.
Furthermore, the removal of one row reduces the memory requirements of the
filter by eliminating the second forward array which would otherwise be
needed.
=====================================
The Sierra filters
In 1989, Frankie Sierra came out with his three-line filter:
* 5 3 The Sierra3 filter
2 4 5 4 2
2 3 2 (1/32)
A year later, Sierra followed up with a two-line modification:
* 4 3 The Sierra2 filter
1 2 3 2 1 (1/16)
and a very simple "Filter Lite," as he calls it:
* 2 The Sierra-2-4A filter
1 1 (1/4)
Even this very simple filter, according to Sierra, produces better results
than the original Floyd-Steinberg filter.
=====================================
Miscellaneous filters
Many image processing software packages offer one or more of the filters
listed above as dithering options. In nearly every case, the Floyd-
Steinberg filter (or a variant thereof) is included. The Bayer ordered
dither is sometimes offered, although the Floyd-Steinberg filter will do a
better job in essentially the same processing time. Higher-quality filters
like Burkes or Stucki are usually also present.
All of the filters described above are used on display devices which have
"square pixels." This is to say that the display lays out the pixels in
rows and columns, aligned horizontally and vertically and spaced equally in
both directions. This applies to the commonly-used video modes in VGA and
SVGA: 640 x 480, 800 x 600, and 1024 x 768, with a 4:3 "aspect ratio." It
would also include HP-compatible and PostScript desktop laser printers using
300dpi marking engines.
Some displays may use "rectangular pixels," where the horizontal and
vertical spacings are unequal. This would include various EGA and CGA video
modes and other specialized video displays, and most dot-matrix printers.
In many cases, the filters described earlier will do a decent job on
rectangular pixel grids, but an optimized filter would be preferred.
Slinkman [10] describes one such filter for his 640 x 240 monochrome display
with a 1:2 aspect ratio.
In other cases, video displays might use a "hexagonal grid" of pixels, where
rows of pixels are offset or staggered, in much the same fashion used on
broadcast television receivers. This is illustrated below:
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
square/rectangular hexagonal
Hexagonal grids are given a very thorough treatment by Ulichney, should you
be interested in further information.
While technically not an error-diffusion filter, a method proposed by Gozum
[11] offers color resolutions in excess of 256 colors by plotting red,
green, and blue pixel "triplets" or triads to simulate an "interlaced"
television display (sacrificing some horizontal resolution in the process).
Again, I would refer interested readers to his document for more
information.
=====================================
Special considerations
The speed disadvantages of the more complex filters can be eliminated
somewhat by performing the divisions beforehand and using lookup tables
instead of doing the math inside the loop. This makes it harder to use
various filters in the same program, but the speed benefits are enormous.
It is critical with all of these algorithms that when error values are added
to neighboring pixels, the resultant summed values must be truncated to fit
within the limits of hardware. Otherwise, an area of very intense color may
cause streaks into an adjacent area of less intense color.
This truncation is known as "clipping," and is analogous to the audio
world's concept of the same name. As in the case of an audio amplifier,
clipping adds undesired noise to the data. Unlike the audio world, however,
the visual clipping performed in error-diffusion halftoning is acceptable
since it is not nearly so offensive as the color streaking that would occur
otherwise. It is mainly for this reason that the larger filters work better
-- they split the errors up more finely and produce less clipping noise.
With all of these filters, it is also important to ensure that the sum of
the distributed error values is equal to the original error value. This is
most easily accomplished by subtracting each fraction, as it is calculated,
from the whole error value, and using the final remainder as the last
fraction.
=====================================
Further perturbations
As alluded to earlier, there are various techniques for the reduction of
digital artifacts, most of which involve using a little randomness to
lightly "perturb" a regular algorithm (particularly the simpler ones). It
could be said that random dither takes this concept to the extreme.
Serpentine scanning is one of these techniques, as noted earlier. Other
techniques include the addition of small amounts of white noise, or
randomizing the positions of the error weights (essentially, using a
constantly-varying pattern). As you might imagine, any of these methods
incur a penalty in processing time.
Indeed, some of the above filters (particularly the simpler ones) can be
greatly improved by skewing the weights with a little randomness [3].
=====================================
Nearest available color
Calculating the nearest available intensity is trivial with a monochrome
image; calculating the nearest available color in a color image requires
more work.
A table of RGB values of all available colors must be scanned sequentially
for each input pixel to find the closest. The "distance" formula most often
used is a simple pythagorean "least squares". The difference for each color
is squared, and the three squares added to produce the distance value. This
value is equivalent to the square of the distance between the points in RGB-
space. It is not necessary to compute the square root of this value because
we are not interested in the actual distance, only in which is smallest.
The square root function is a monotonic increasing function and does not
affect the order of its operands. If the total number of colors with which
you are dealing is small, this part of the algorithm can be replaced by a
lookup table as well.
When your hardware allows you to select the available colors, very good
results can be achieved by selecting colors from the image itself. You must
reserve at least 8 colors for the primaries, secondaries, black, and white
for best results. If you do not know the colors in your image ahead of
time, or if you are going to use the same map to dither several different
images, you will have to fill your color map with a good range of colors.
This can be done either by assigning a certain number of bits to each
primary and computing all combinations, or by a smoother distribution as
suggested by Heckbert [8].
An alternate method of color selection, based on a tetrahedral color space,
has been proposed by Crawford [12]. His algorithm has been optimized for
either dispersed-dot ordered dither or Floyd-Steinberg error diffusion with
serpentine scan.
=====================================
Hardware halftoning
In some cases, image scanning hardware may be able to digitally halftone and
dither the image "on the fly" as it is being scanned. The data produced by
the "raw" scan is then already in a 1- or 2-bit/pixel format. While this
feature would probably be unsuitable for cases where the image would need
further processing (see the "Loss of image information" section below), it
is very useful where the operator wants to generate a final image, ready for
printing or displaying, with little or no subsequent processing.
As an example, the Epson ES-300C color scanner (and its European equivalent,
the Epson GT-6000) offers three internal halftone modes. One is a standard
"halftone" algorithm, i.e. a clustered-dot ordered dither. The other two
are error-diffusion filters (one "sharp," the other "soft") which are
proprietary Epson-developed filters.
=====================================
Loss of image information incurred by digital halftoning
It is important to emphasize here that digital halftoning is a ONE-WAY
operation. Once an image has been halftoned or dithered, although it may
look like a good reproduction of the original, INFORMATION IS PERMANENTLY
LOST. Many image processing functions fail on dithered images; in fact, you
would not want to dither an image which had already been dithered to some
extent.
For these reasons, digital halftoning must be considered primarily as a way
TO PRODUCE AN IMAGE ON HARDWARE THAT WOULD OTHERWISE BE INCAPABLE OF
DISPLAYING IT. This would hold true wherever a grayscale or color image
needs to be rendered on a bilevel display device. In this situation, one
would almost never want to store the dithered image.
On the other hand, when color images are dithered for display on color
displays with a lower color resolution, the dithered images are more useful.
In fact, the bulk of today's scanned-image GIF files which abound on
electronic BBSs and information services are 8-bit (256 color), colormapped
and dithered files created from 24-bit true-color scans. Only rarely are
the 24-bit files exchanged, because of the huge amount of data contained in
them.
In some cases, these mapped GIF files may be further processed with special
paint/processing utilities, with very respectable results. However, the
previous warning still applies: one can never obtain the same image fidelity
when operating on the mapped GIF file as they could if they were operating
on the true-color image file.
Generally speaking, digital halftoning and dithering should be the last
stage in producing a physical display from a digitally stored image. The
data representing an image should always be kept in full detail in case you
should want to reprocess it in any way. As affordable display technology
improves, the day may soon come where you might possess the hardware to
allow you to use all of the original image information without the need for
digital halftoning or color reduction.
=====================================
Sample code
Despite my best efforts in expository writing, nothing explains an algorithm
better than real code. With that in mind, presented here are a few programs
which implement some of the concepts presented in this paper.
1) This code (in the C programming language) dithers a 256-level
monochrome image onto a black-and-white display with the Bayer ordered
dither.
/* Bayer-method ordered dither. The array line[] contains the intensity
** values for the line being processed. As you can see, the ordered
** dither is much simpler than the error dispersion dither. It is also
** many times faster, but it is not as accurate and produces cross-hatch
** patterns on the output.
*/
unsigned char line[WIDTH];
int pattern[8][8] = {
{ 0, 32, 8, 40, 2, 34, 10, 42}, /* 8x8 Bayer ordered dithering */
{48, 16, 56, 24, 50, 18, 58, 26}, /* pattern. Each input pixel */
{12, 44, 4, 36, 14, 46, 6, 38}, /* is scaled to the 0..63 range */
{60, 28, 52, 20, 62, 30, 54, 22}, /* before looking in this table */
{ 3, 35, 11, 43, 1, 33, 9, 41}, /* to determine the action. */
{51, 19, 59, 27, 49, 17, 57, 25},
{15, 47, 7, 39, 13, 45, 5, 37},
{63, 31, 55, 23, 61, 29, 53, 21} };
int getline(); /* Function to read line[] from image */
/* file; must return EOF when done. */
putdot(int x, int y); /* Plot white dot at given x, y. */
dither()
{
int x, y;
while (getline() != EOF) {
for (x=0; x<WIDTH; ++x) {
c = line[x] >> 2; /* Scale value to 0..63 range */
if (c > pattern[x & 7][y & 7]) putdot(x, y);
}
++y;
}
}
2) This program (also written in C) dithers a color image onto an 8-color
display by error-diffusion using the Burkes filter.
/* Burkes filter error diffusion dithering algorithm in color. The array
** line[][] contains the RGB values for the current line being processed;
** line[0][x] = red, line[1][x] = green, line[2][x] = blue.
*/
unsigned char line[3][WIDTH];
unsigned char colormap[3][COLORS] = {
0, 0, 0, /* Black This color map should be replaced */
255, 0, 0, /* Red by one available on your hardware */
0, 255, 0, /* Green */
0, 0, 255, /* Blue */
255, 255, 0, /* Yellow */
255, 0, 255, /* Magenta */
0, 255, 255, /* Cyan */
255, 255, 255 }; /* White */
int getline(); /* Function to read line[][] from image */
/* file; must return EOF when done. */
putdot(int x, int y, int c); /* Plot dot of given color at given x, y. */
dither()
{
static int ed[3][WIDTH] = {0}; /* Errors distributed down, i.e., */
/* to the next line. */
int x, y, h, c, nc, v, /* Working variables */
e[4], /* Error parts (7/8,1/8,5/8,3/8). */
ef[3]; /* Error distributed forward. */
long dist, sdist; /* Used for least-squares match. */
for (x=0; x<WIDTH; ++x) {
ed[0][x] = ed[1][x] = ed[2][x] = 0;
}
y = 0; /* Get one line at a time from */
while (getline() != EOF) { /* input image. */
ef[0] = ef[1] = ef[2] = 0; /* No forward error for first dot */
for (x=0; x<WIDTH; ++x) {
for (c=0; c<3; ++c) {
v = line[c][x] + ef[c] + ed[c][x]; /* Add errors from */
if (v < 0) v = 0; /* previous pixels */
if (v > 255) v = 255; /* and clip. */
line[c][x] = v;
}
sdist = 255L * 255L * 255L + 1L; /* Compute the color */
for (c=0; c<COLORS; ++c) { /* in colormap[] that */
/* is nearest to the */
#define D(z) (line[z][x]-colormap[c][z]) /* corrected color. */
dist = D(0)*D(0) + D(1)*D(1) + D(2)*D(2);
if (dist < sdist) {
nc = c;
sdist = dist;
}
}
putdot(x, y, nc); /* Nearest color found; plot it. */
for (c=0; c<3; ++c) {
v = line[c][x] - colormap[c][nc]; /* V = new error; h = */
h = v >> 1; /* half of v, e[1..4] */
e[1] = (7 * h) >> 3; /* will be filled */
e[2] = h - e[1]; /* with the Floyd and */
h = v - h; /* Steinberg weights. */
e[3] = (5 * h) >> 3;
e[4] = h = e[3];
ef[c] = e[1]; /* Distribute errors. */
if (x < WIDTH-1) ed[c][x+1] = e[2];
if (x == 0) ed[c][x] = e[3]; else ed[c][x] += e[3];
if (x > 0) ed[c][x-1] += e[4];
}
}
++y;
}
}
3) This program (in somewhat incomplete, very inefficient pseudo-C)
implements error diffusion dithering with the Floyd and Steinberg
filter. It is not efficiently coded, but its purpose is to show the
method, which I believe it does.
/* Floyd/Steinberg error diffusion dithering algorithm in color. The array
** line[][] contains the RGB values for the current line being processed;
** line[0][x] = red, line[1][x] = green, line[2][x] = blue. It uses the
** external functions getline() and putdot(), whose purpose should be easy
** to see from the code.
*/
unsigned char line[3][WIDTH];
unsigned char colormap[3][COLORS] = {
0, 0, 0, /* Black This color map should be replaced */
255, 0, 0, /* Red by one available on your hardware. */
0, 255, 0, /* Green It may contain any number of colors */
0, 0, 255, /* Blue as long as the constant COLORS is */
255, 255, 0, /* Yellow set correctly. */
255, 0, 255, /* Magenta */
0, 255, 255, /* Cyan */
255, 255, 255 }; /* White */
int getline(); /* Function to read line[] from image file; */
/* must return EOF when done. */
putdot(int x, int y, int c); /* Plot dot of color c at location x, y. */
dither()
{
static int ed[3][WIDTH] = {0}; /* Errors distributed down, i.e., */
/* to the next line. */
int x, y, h, c, nc, v, /* Working variables */
e[4], /* Error parts (7/8,1/8,5/8,3/8). */
ef[3]; /* Error distributed forward. */
long dist, sdist; /* Used for least-squares match. */
for (x=0; x<WIDTH; ++x) {
ed[0][x] = ed[1][x] = ed[2][x] = 0;
}
y = 0; /* Get one line at a time from */
while (getline() != EOF) { /* input image. */
ef[0] = ef[1] = ef[2] = 0; /* No forward error for first dot */
for (x=0; x<WIDTH; ++x) {
for (c=0; c<3; ++c) {
v = line[c][x] + ef[c] + ed[c][x]; /* Add errors from */
if (v < 0) v = 0; /* previous pixels */
if (v > 255) v = 255; /* and clip. */
line[c][x] = v;
}
sdist = 255L * 255L * 255L + 1L; /* Compute the color */
for (c=0; c<COLORS; ++c) { /* in colormap[] that */
/* is nearest to the */
#define D(z) (line[z][x]-colormap[c][z]) /* corrected color. */
dist = D(0)*D(0) + D(1)*D(1) + D(2)*D(2);
if (dist < sdist) {
nc = c;
sdist = dist;
}
}
putdot(x, y, nc); /* Nearest color found; plot it. */
for (c=0; c<3; ++c) {
v = line[c][x] - colormap[c][nc]; /* V = new error; h = */
h = v >> 1; /* half of v, e[1..4] */
e[1] = (7 * h) >> 3; /* will be filled */
e[2] = h - e[1]; /* with the Floyd and */
h = v - h; /* Steinberg weights. */
e[3] = (5 * h) >> 3;
e[4] = h = e[3];
ef[c] = e[1]; /* Distribute errors. */
if (x < WIDTH-1) ed[c][x+1] = e[2];
if (x == 0) ed[c][x] = e[3]; else ed[c][x] += e[3];
if (x > 0) ed[c][x-1] += e[4];
}
} /* next x */
++y;
} /* next y */
}
=====================================
Bibliography
[1] Foley, J.D. and A. van Dam, Fundamentals of Interactive Computer
Graphics, Addison-Wesley, Reading, MA, 1982.
This is a standard reference for many graphic techniques which has
not declined with age. Highly recommended. This edition is out
of print but can be found in many university and engineering
libraries. NOTE: This book has been updated and rewritten, and
this new version is currently in print as:
Foley, J.D., A. van Dam, S.K. Feiner, and J.F. Hughes; Computer
Graphics: Principles and Practice. Addison-Wesley, Reading, MA, 1990.
This rewrite omits some of the more technical data of the 1982
edition, but has been updated to include information on error-
diffusion and the Floyd-Steinberg filter. Currently on computer
bookstore shelves and rather expensive (around $75 list price).
[2] Bayer, B.E., "An Optimum Method for Two-Level Rendition of Continuous
Tone Pictures," IEEE International Conference on Communications,
Conference Records, 1973, pp. 26-11 to 26-15.
A short article proving the optimality of Bayer's pattern in the
dispersed-dot ordered dither.
[3] Ulichney, R., Digital Halftoning, The MIT Press, Cambridge, MA, 1987.
This is the best book I know of for describing the various black
and white dithering methods. It has clear explanations (a little
higher math may come in handy) and wonderful illustrations. It
does not contain any code, but don't let that keep you from
getting this book. Computer Literacy normally carries it but the
title is often sold out.
[MFM note: I can't describe how much information I got from this
book! Several different writers have praised this reference to
the skies, and I can only concur. Some of it went right over my
head -- it's heavenly for someone who is thrilled by Fourier
analysis -- but the rest of it is a clear and excellent treatment
of the subject. I had to request it on an interlibrary loan, but
it was worth the two weeks' wait and the 25 cents it cost me for
the search. University or engineering libraries would be your
best bet, as would technical bookstores.]
[4] Floyd, R.W. and L. Steinberg, "An Adaptive Algorithm for Spatial Gray
Scale." SID 1975, International Symposium Digest of Technical Papers,
vol 1975m, pp. 36-37.
Short article in which Floyd and Steinberg introduce their filter.
[5] Daniel Burkes is unpublished, but can be reached at this address:
Daniel Burkes
TerraVision, Inc.
2351 College Station Road, Suite 563
Athens, GA 30305
or via CIS at UID# 72077,356. The Burkes error filter was submitted to
the public domain on September 15, 1988 in an unpublished document,
"Presentation of the Burkes error filter for use in preparing
continuous-tone images for presentation on bi-level devices." The file
BURKES.ARC, in LIB 15 (Publications) of the CIS Graphics Support Forum,
contains this document as well as sample images.
[6] Jarvis, J.F., C.N. Judice, and W.H. Ninke, "A Survey of Techniques for
the Display of Continuous Tone Pictures on Bi-Level Displays," Computer
Graphics and Image Processing, vol. 5, pp. 13-40, 1976.
[7] Stucki, P., "MECCA - a multiple-error correcting computation algorithm
for bilevel image hardcopy reproduction." Research Report RZ1060, IBM
Research Laboratory, Zurich, Switzerland, 1981.
[8] Heckbert, P. "Color Image Quantization for Frame Buffer Display."
Computer Graphics (SIGGRAPH 82), vol. 16, pp. 297-307, 1982.
[9] Frankie Sierra is unpublished, but can be reached via CIS at UID#
76356,2254. Pictorial presentations of his filters can be found in LIB
17 (Developer's Den) of the CIS Graphics Support Forum as the files
DITER1.GIF, DITER2.GIF, DITER6.GIF, DITER7.GIF, DITER8.GIF, and
DITER9.GIF.
[10] J.F.R. "Frank" Slinkman is unpublished, but can be reached via CIS at
UID# 72411,650. The file NUDTHR.ARC in LIB 17 (Developer's Den) of the
CIS Graphics Support Forum contains his document "New Dithering Method
for Non-Square Pixels" as well as sample images and encoding program.
[11] Lawrence Gozum is unpublished, but can be reached via CIS at UID#
73437,2372. His document "Notes of IDTVGA Dithering Method" can be
found in LIB 17 (Developer's Den) of the CIS Graphics Support Forum as
the file IDTVGA.TXT.
[12] Robert M. Crawford is unpublished, but can be reached via CIS at UID#
76356,741. The file DGIF.ZIP in LIB 17 (Developer's Den) of the CIS
Graphics Support Forum contains documentation, sample images, and demo
program.
========================================================================
Other works of interest:
Knuth, D.E., "Digital Halftones by Dot Diffusion." ACM Transactions on
Graphics, Vol. 6, No. 4, October 1987, pp 245-273.
Surveys the various methods available for mapping grayscale images to
B&W for high-quality phototypesetting and laser printer reproduction.
Presents an algorithm for smooth dot diffusion. (With 22 references.)
Newman, W.M. and R.F.S. Sproull, Principles of Interactive Computer
Graphics, 2nd edition, McGraw-Hill, New York, 1979.
Similar to Foley and van Dam in scope and content.
Rogers, D.F., Procedural Elements for Computer Graphics, McGraw-Hill, New
York, 1985.
More of a conceptual treatment of the subject -- for something with
more programming code, see the following work. Alas, the author errs
in his discussion of the Floyd-Steinberg filter and uses the "false"
filter pattern discussed earlier.
Rogers, D.F. and J. A. Adams, Mathematical Elements for Computer Graphics,
McGraw-Hill, New York, 1976.
A good detailed discussion of producing graphic images on a computer.
Plenty of sample code.
Kuto, S., "Continuous Color Presentation Using a Low-Cost Ink Jet Printer,"
Proc. Computer Graphics Tokyo 84, 24-27 April, 1984, Tokyo, Japan.
Mitchell, W.J., R.S. Liggett, and T. Kvan, The Art of Computer Graphics
Programming, Van Nostrand Reinhold Co., New York, 1987.
Pavlidis, T., Algorithms for Graphics and Image Processing, Computer Science
Press, Rockville, MD, 1982.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment