Mike Stark
Homework #2
Excursions in Color Theory
Overview
For this assignment we were asked to render the visible spectrum, over a
range of luminances. The problem is that modern computer monitors cannot, in
general, faithfully produce any pure "spectral" color, simply because
the small light-producing elements, or phosphors, do not produce a purely
monochromatic color. The following is a rough illustration of the
fundamental plane in the CIE XYZ color space.
The pure colors in the visible spectrum at a fixed luminance are found along
the white horseshoe-shaped curve, the interior of which contains all the
colors visible to the human eye (at least approximately) at a fixed luminance.
The colored triangle is the roughly "gamut" of the Sony monitor on the SGI
workstation I used for this assignment. The grayish point at the center is
the "white point" of the monitor, and should correspond roughly with the white
point of the color space. Lines radiating outward from the white point have
the same hue, and become more saturated as they approach the horseshoe.
The corners of the triangle are, approximately, the colors of the phosphors of
the monitor. Notice they are not actually on the spectrum (as noted above)
but they are partially saturated spectral colors.
There is a standard, if somewhat imperfect method of converting visible
colors in CIE XYZ coordinates, as described in the
homework assignment
The image above actually is something of an over-simplification, because the
actual gamut of a monitor is a three-dimensional parallelepiped; the triangle
above is this solid projected onto the fundamental CIE XYZ plane X + Y + Z =
1. True spectral colors must therefore be mapped somehow into the space of
the gamut of the monitor, in a manner that preserves the hue of the color.
Methods
I've tried a huge number of ideas to find a good way of doing this, and I've
most of them weren't any good. But I did have some success; although I must
admit most of the ideas that worked weren't really my own.
Clipping and Scaling
In general the RGB values of a spectral color are outside the RGB color cube
in all directions. That is, values above 1 and below zero appear in all three
coordinates. So an obvious reduction method is to simply "clip" RGB values
outside the interval [0, 1]:
This works better than one might expect, although the image above has the RGB
values implicitly scaled to a "nice" range which I found easier to work
with. The most serious problem with the clipping method is that is the
absence of the so-called "secondary colors", yellow, cyan, and violet. In
fact, there is a significant dark space where the cyan, or indigo, range
should be. Also, there are "Mach" bands in the primary color regions.
An alternative but similar approach is to scale the components into the cube,
rather than clipping them. In this method, the minimum RGB component is
scaled to zero, and the maximum to one.
Desaturation
The boundary of the monitor gamut consists essentially of spectral hues with
less saturation, so perhaps the most natural reduction method is to simply
"desaturate", or add white, to each spectral color until it reaches the
monitor gamut, as shown below:
For this figure, the desaturation was done in RGB space; enough white was
added so that the largest negative component was raised to zero, and the
result was then clipped. Is this an improvement? Well, there is more violet
on the left side, which appeals to me, because I like that color! Also, the
primary colors aren't as dominant, and there is less of a "flame" effect which
is desirable, as scan-lines should ideally only differ in brightness, or
luminance. However, some of the colors are under-saturated, as is particularly
noticeable in the cyan region which is too large anyway. The curved Mach bands
are not as prevalent, but now the primary colors themselves appear in Mach
bands, albeit vertical bands, but now instead of being over-emphasized, the
primary colors are reduced to these narrow bands.
A Hybrid Approach
There appear to be definite advantages to both clipping and desaturization,
so a hybrid approach seems the natural next step. The method is to desaturate
only most of the way to the monitor gamut, then clip as usual. I
found that about 70% works pretty well, and this value was used to create to
following image.
Notice the Mach bands in the primary colors have been reduces, as has the
under-saturated cyan band.
Exactly how much desaturation we should do isn't so clear. For fun, I put
together an
animation
of the result of 0 through 100% desaturation.
Fudging
The hybrid image above leaves much to be desired aesthetically, so perhaps it
is worth some effort to "fudge" the image. One approach is to apply a gamma
correction on the desaturated and clipped RGB values. Such an image, using an
exponent of 0.8 is shown below:
Is this an improvement? Well, the primary color Mach bands are more
noticeable, but the image "fadeout" is improved somewhat. I put together
another
animation
of gamma exponents 0.5 through about 2, although this really only shows the
effect of gamma corrections. Of course, the monitor is supposedly already
gamma corrected, but it never hurts to experiment.
If one really wanted to work on the general appearance of the spectrum, more
general corrections need to be used. The following image has been adjusted in
a variety of ways, mostly based on localized gamma corrections. I don't
claim this image is the best we can do, but it does show how local corrections
can be included to make overall improvements.
In particular, the pinkish area to at the right of the image has been reduced,
as has the blue Mach band. They cyan area has been contracted somewhat;
however, we have lost some of the dimmer yellow and violet. But with more
work, these could probably be brought out.
Final Comments
These images were produced using the data Brian Smits provided for the SGI
Sony monitors.
As a point of possible interest, the X Window System, beginning with
Release 5, provides an extensive mechanism for device-independent color,
including functions for converting between various color spaces. Here is how
X11 handled the conversion: