This is everything I have come to know about IDL and colors.
It's substantial.
Colors and IDL
==================
In IDL, a color is composed of 3 values, known as a color triple. The
color may be expresses as (red, green, blue) where each of the
three values dictates the amount of red, green, and blue light
assigned to this color on the display device. Each value has an 8-bit
range and may be assigned a value between 0 and 255. So, a specific
color may be composed of 28 shades of red, 28 shades of green, and
28 shades of blue, for a grand total of 224 ( = 16.77 million) available
colors.
COLOR TABLES
A color table consists of three columns of numbers, the first
containing values of red, the second values of green, the last values
of blue. You can access a color in the color table using an
index, which is just the row of the color table. The color
index tells the computer where to go and grab a color in the color
table. So, if a pixel in an image has the value 64, then the color
index is 64; the computer grabs the 64th row of the color table. This
might happen to be (255,255,0), which would correspond to the color
yellow. This is known as the Indexed Color Model, since an index is
used to specify a color.
IDL always uses the Indexed Color Model when run on 8-bit display
devices. This is completely necessary since there are a maximum of
28 colors available on such devices!
COLORS ON A 24-BIT DISPLAY
[The semantics seem odd here: in reality, a color table forces a
representation for a color to be broken into its constituent elements
(r,g,b) and placed into a table, thus the value representing a color
is necessarily decomposed; however, the RGB Color Model
requires that the constituent parts be combined into a single color
value, which is certainly an undecomposed quantity. Rather, my
guess as to how this confusing nomenclature surfaced is as follows:
the Indexed Color Model picks a color (any color) based on an index,
which itself contains no information on the (r,g,b) content of the
color that it references in the color table, so in this sense it is
undecomposed into r,g,b values; the RGB Color Model accesses a
color with an index that is built from the r,g,b contributions to the
color, hence the single value references a color whose r,g,b values
can be decomposed. I think this was a poor choice for a
descriptor.]
X WINDOWS VISUAL CLASSES
A more fundamental issue than how IDL handles color is how the
hardware handles color! There are three types of displays: Gray
Scale, Pseudo Color and Decomposed Color displays.
* decomposed (separated into constituent parts)
* visual classes
* N.B. Usually, your desktop manager sucks up (quite) a few color
table indices, so if you start up IDL...
Display Device : X
Visual Class : PseudoColor
Visual Depth : 8-Bit
Color Table Size: 50
Number of Colors: 50
Decomposed Color: 0
use window, colors=256
Display Device : X
Visual Class : PseudoColor
Visual Depth : 8-Bit
Color Table Size: 256
Number of Colors: 256
Decomposed Color: 0
* xdpyinfo
Visual Name | Color | Colormap | 8-bit | 24-bit |
StaticGray | Grayscale | Static | + | + |
GrayScale | Grayscale | Dynamic | + | + |
StaticColor | Undecomposed | Static | + | + |
PseudoColor | Undecomposed | Dynamic | + | + |
TrueColor | Decomposed | Static | - | + |
DirectColor | Decomposed | Dynamic | - | + |
When using a 24-bit display, IDL uses "decomposed" color when the RGB
Color Model is used. Rather than a single color index which
corresponds to a row in the color table, IDL tries to decompose
the color index into 3 separate indices. It assumes that the color
index is a 24-bit long integer. The lowest 8 bits of the number
represent the red index, the middle 8 bits the green index, and the
last 8 bits the blue index. Here's how to convert color indices (0 to
255) into a 24-bit integer:
COLOR_INDEX = R + G*256L + B*(256L)^2
or, if you're nasty,
COLOR_INDEX = long(R) + ishft(long(G),8) + ishft(long(B),16)
You could use hexadecimal notation, but real men and women don't. In
case you're wondering, it takes only 2 hexadecimal digits to set 8
bits. The digits 0-9,A-F correspond to 0-15, so that '6B'xL =
(6*16)+11 = 107 and 'FF'xL = (15*16)+15 = 255. In hexadecimal, yellow
would be 'FFFF00'xL. (In IDL, hexadecimals need to be in single quotes
and followed by an x. We include the L to make sure the
resulting value will be a 24-bit long integer.)
The only two visual classes that use the RGB Color Model are TrueColor
and DirectColor.
static = read-only colormap
dynamic = writable colormap
, a static color
display in which colors are displayed directly. A dynamic color table
has the property (good or bad) of changing the color of a pixel
immediately when a color table is changed. However, a static color
table will (for the most part) allow pixels to retain their colors
when color table values are changed since the colors are specified
directly.
Therefore, 24-bit displays are incredibly valuable tools for
research since they are able to display all 224 colors simultaneously,
while 8-bit displays can only display 28 simultaneous colors
even though more than 16 million are available!
You can discover what IDL is color model you are using as follows:
IDL> device, get_visual_name=name, get_visual_depth=depth
IDL> print, name, depth
The visual name will be either PseudoColor, DirectColor, or
TrueColor, while the depth will be either 8, 16 or 24 (the
number of bits necessary to specify a color in the visual class.)
EASY...
PseudoColor : a dynamic color visual
TrueColor : a static color visual
NOT-SO-EASY...
DirectColor : only available on UNIX machines. Usual side effect is a
private color map in which the graphics window takes up all the
correct colors, causing other windows to disappear. This problem has
earned its own name: "the color flashing problem". Doesn't always
happen, but more than likely it will. the problem is a result of the
way in which the X Window manager handles color tables.
Here are the options you have for the visual class assignment
statements using UNIX:
IDL> device, pseudo=8, retain=2
IDL> device, true_color=24, retain=2
IDL> device, direct_color=24, retain=2
You may as well stick the correct one for your system in your
.idlstartup file.
SPECIFYING COLORS ON 8-BIT DISPLAYS
Suppose you wanted to input the color yellow into the current color
table at the position 66 and the color red at position 132. Just use
the tvlct command. Here's an example that will show you what's
happening:
IDL> device, get_visual_name=a, get_visual_depth=b
IDL> print, 'Visual: ', a, b
IDL> ysize = 256
IDL> xsize = 256
IDL> window, 0, ysize=ysize, xsize=xsize
IDL> loadct, 6
IDL> tvlct, 255, 255, 0, 66
IDL> tvlct, 255, 0, 0, 132
IDL> tv, indgen(ysize) ## (intarr(xsize)+1)
KEEPING TRACK OF THE COLOR TABLE
All IDL color table procedures keep track of the current color table
in a common block called COLORS:
common COLORS, r_orig, g_orig, b_orig, r_curr, g_curr, b_curr
Each variable is an array of length equal to the number of color
indices. If you want to access and diddle with the color table, the
convention is to read the current table from r_orig, g_orig, b_orig,
modify it, load it with the tvlct procedure and leave the modified
color table in r_curr, g_curr, b_curr.
SWITCHING DEVICES BUT KEEPING THE SAME COLOR TABLE
If you want to conserve the current color table but send your image to
a new device, say a PostScript file, send both the /color and
the /interpolate keywords to your call to device.
MAKE DEVICE INDEPENDENT COLORS
STRETCHING A COLORTABLE
If you want to take the currently loaded color table and stretch the
entire range of colors over a subset of color indices, use the
stretch procedure:
IDL> stretch, 66, 200
stretches the color table from index 66 to 200. Everything outside of
this range will be set to the maximum color index. Use the /chop
keyword to set the outside range to 0.
BLAHBETY BLAH BLAH
loadct, 3
stretch, 0, !d.table_size-8
setcolors
tv, bytscl(image, top=!d.table_size-8)
N.B. You might think, as I did, that the following would be equivalent:
tvscl, image, top=!d.table_size-8
but according to Fanning, "if color is important to you (and it almost
always is), then you probably never want to use the
TVScl command." Rather, scale the data yourself. Since both
TV and TVScl are proprietary, there's no easy way of
making an absolute comparison, but one thing is certain: TVScl
scales an image into the number of colors in your IDL session. Also,
two data sets may have different ranges, so scaling with TVScl
will introduce errors!
Now overplot or xyouts accessing the colors stored in plotcolors.
(If you want to make an image of a separate data set, but over the
same data range, make use of the min and max keywords
for BytScl.)
DISPLAYING 24-BIT IMAGES
If you want to display a 24-bit image, you must either load a
gray-scale color table or set the decomposed device keyword to 1!
A 24-bit image will always be a 3-D data set... 3 2-D images: the red
version, the green version, and the blue version. For completeness:
IMAGE: LINGO:
m-by-n-by-3 band-interleaved
m-by-3-by-n row-interleaved
3-by-m-by-n pixel-interleaved
When displaying a 24-bit image with TV, you need to specify
which interleaving was used with the True keyword:
True=1 pixel-interleaved
True=2 row-interleaved
True=3 band-interleaved
*** If you want to view a 24-bit image, but you're on an 8-bit
display, here's what you do (take image to be your 3-D color
image):
IDL> image2d = color_quan(image, 1, r, g, b)
IDL> tvlct, r, g, b
IDL> tv, image2d
I've never tried this, but it's supposed to give you a ratty version
of a 24-bit color image. I'd suggest just throwing your monitor out
the window, saying it was stolen, and having your advisor buy you a
new one with a 24-bit display.
IMAGE INTERLEAVING
redimg = byte( (0 > (intimg1*colr[ colorimg, 0])) < 255)
grnimg = byte( (0 > (intimg1*colr[ colorimg, 1])) < 255)
bluimg = byte( (0 > (intimg1*colr[ colorimg, 2])) < 255)
;DISPLAY THE IMAGE INTERLEAVED DATA CUBE...
tv, [[[redimg]], [[grnimg]], [[bluimg]]], $
xtvleft, ytvbottom, ysize=yplotsize, xsize=xplotsize, $
/normal, true=3
DISPLAYING A 24-BIT IMAGE ON AN 8-BIT DISPLAY
DISPLAYING AN 8-BIT IMAGE ON A 24-BIT DISPLAY
Since 24-bit colors are directly accessed, working with color tables
on a 24-bit machine is tricky. Therefore, if you change the color
table, you need to redisplay your image to see the changes. On
an 8-bit machine, you see the changes as soon as you change the color
table since the colors are indexed to a value in a color lookup table.
However, DirectColor devices are dynamic color devices: a
change to the color table values automatically causes a change to the
image! TrueColor devices are static color devices: "the colors
used for the graphics are not tied in any way to the colors loaded in
the current color table. If you change the color table values, you
MUST redisplay the graphic to see the colors take effect. I presume,
but do not know for sure, that the Mac is also a True-Color device."
(Fanning)
If on a static color device, set color decomposition off, change the
color table, then redisplay the image. Probably want to set color
decomposition back on after redisplaying the image.
HAVEN'T COME UP WITH A WAY TO FIX THIS, BUT FANNING HAS A WAY.
Color Images in PostScript
==========================
Images may be made with 1,2,4,8 bits per pixel yielding 1,2,16,256
possible colors.
A pseudo-color image is a 2D image with each pixel indexing the color
table, obtaining an RGB value for each possible pixel value.
A true-color image is a 3D image, one of the dimensions having a size
of three (one for each color component, RGB). It is therefore, three
2D images... one for each color component. By convention the color
order is ALWAYS RGB. So an n x m true-color image can be ordered in the
following ways:
(3,n,m) : pixel interleaved
(n,3,m) : row interleaved
(n,m,3) : image interleaved
When calling TV or TVSCL, the TRUE keyword must be set to:
1 : pixel interleaving
2 : row interleaving
3 : image interleaving
So, in order to set up the PostScript device with 24-bit color (8 bits
per color!)
set_plot, 'ps', /copy, /interpolate
device, file='file.ps', /color, bits_per_pixel=8
[N.B. COPY keyword demands that the device color tables are copied
directly from IDL's internal color tables. However, if the new
device's color tables contain more indices than those of the old
device, the new device's tables are not completely filled!! Use the
INTERPOLATE keyword to force the internal color tables to be
interpolated to fill the range of the new device!]
Now, combine your red, grn, and blu images to (n,m,3) cube (image
interleaved) and TV them with true color:
tv, [[[red]], [[grn]], [[blu]]], true=3
Now revert back to x-windows:
device, /close
set_plot, 'x'
-----
We may also create a pseudo-color table based on the
Lightness-Hue-Brightness (LHB) system. Carl suggests this color
mapping:
pseudo, 100, 100, 100, 100, 22.5, .7, colr