Some notes on IDL color

It is necessary to understanding how IDL handles color. The description given here will in the context of switching from 8-bit pseudocolor to 24-bit true color using X windows.

IDL can only use one visual class at a time. Once that class is selected the only way to use a different one is to exit IDL and start a new session.

IDL sets the visual class on the first graphics call or help,/device. The default class may be set using the device procedure, or in the .Xdefaults file in your home directory (see the online IDL help).

For unix xdpyinfo will list the available visual classes for your system.

Visual Classes
X Windows has six visual classes. These are listed in the IDL online help under X Windows visuals. In order of the least to most flexible these are: StaticGray, GrayScale, StaticColor, PseudoColor, TrueColor, DirectColor. These may be organized into three pairs of similar visuals: [StaticGray, GrayScale], [StaticColor, PseudoColor], and [TrueColor, DirectColor]. The first of each pair has fixed colors (grays for the Gray pair), the second allows a lookup table to set the colors. Also for each visual it may be possible to specify how many bits are used. So for example, for StaticGray a certain pixel value would always be a certain shade of gray. To change the appearance you would have to change the pixel value. For GrayScale however the pixel values are run through a lookup table to set the appearance, so you could change the brightness of a pixel value just be changing the lookup table entry for that value. The gray visuals are probably not commonly used in IDL. For 8-bit displays the PseudoColor visual is common visual. It's related visual is StaticColor, which has fixed colors so it not very useful.

PseudoColor (8-bit color)
PseudoColor has a three-part lookup table, one each for red, green, and blue. Each image pixel is a value used to index into these tables, the table entry at that index gives the red, green, blue color mix for that pixel. The appearance of the image may be changed by changing the lookup table. The color of any added graphics is controlled by plotting it with a value that indexes to the desired color in the lookup table. Some advantages of 8-bit PseudoColor are: the images may be read back from the display just as they appear, they are compact (8 bits), they are easy to save in non-lossy format (GIF for example). A disadvantage is that only a limited number of colors is available for any given image. This is not usually such a problem when creating an image, but if you want to add something in a color that is not in the image it is much harder.

24-bit color
A good 24-bit color visual class is TrueColor. This class has no lookup table so once you know how to specify a color you will get the expected result. This visual class may be set (if it is not the default) by the call device, true_color=24. This may be done in the IDL startup file before any graphics commands. Note: direct_color does not allow you to emulate 8-bit mode using device,decomp=0, so
Make sure you use device, true_color=24 in IDL <===<<<

Differences: image colors
Coming from 8-bit pseudocolor, 24-bit truecolor may seem a bit odd and even limited in some ways. First, color tables appear not to work at all. Try it:

   loadct,4 & tvscl,dist(200)
You should just get a gray scale image, no color. There are no color tables, just a 24-bit display. The tvscl (and tv) command knows to display a byte array in all three color channels at the same time, red, green, and blue, giving a grayscale result. You can send a byte image to a given color channel:
   erase & tvscl,dist(200),channel=1
loads the image in the red color channel. The image replaces only the given channel, not the other color channels (green and blue). That's why the erase was needed, otherwise no change would be seen. This example can be turned yellow by putting the same image in the green channel without erasing the red image first (since green and red make yellow):
   tvscl,dist(200),channel=2
By default the truecolor (or directcolor) class works with decomposed color. For decomposed color, the 24 bits are interpreted as three 8-bit color values, red (first 8 bits), green (second 8 bits), and blue (third 8 bits). The tv and tvscl commands only display byte images, converting where needed. So to display a 24 bit image it must be given as 8 bit color components. This may be done using the channel keyword as shown above, or by packing the image into a 3-d array by interleaving the colors in some way. The interleaving dimension is given by the value of the keyword true to tv or tvscl.

Color tables may be used after doing
device, decompose=0
There is a way to display images using a color table. IDL can be told to interpret the lower 8 bits of a 24 bit value as the index into a color table:

   device,decompose=0
   loadct,4
   tvscl,dist(200)

For 24-bit color there is no 8-bit image

To see a new color table the image must be reloaded

The result should look just as it did for pseudocolor, or maybe a bit better since you will get a full 256 colors and not some lesser number. But if you load another color table the displayed image will not change. The decompose=0 trick only modifies the way the image value is interpreted when loaded, not the visual class which does not change. So the displayed image is still a 24-bit image, there is no 8-bit image involved. To see the image with the new color table it must be reloaded so its values go through that lookup table.

This is related to another problem, tvrd will appear to work incorrectly at first. Try this in 24-bit truecolor mode:

   device,decompose=0
   loadct,4
   tvscl,dist(200)
   a=tvrd()
   tv,a
Use the TRUE keyword on TVRD
to read a 24-bit image
The result is not what was there to start with. The problem is that tvrd tries to return an 8 bit image, check array a (help,a) to verify that it was returned as 8 bits (a byte array). But which 8 bits? Without telling IDL which, it will return the max value of the three channels (R,G,B) at each pixel. That's no problem for a grayscale image but is probably never right for a color image. You can read individual color channels by using the channel keyword, or better, you can read the full 24-bit image back as a 3-d array using the true keyword to specify which dimension the RGB channels should be interleaved in:
   a=tvrd(true=3)
   tv,a,true=3
Look at array a to see that it is interleaved in the third dimension.

Differences: plot colors
Colors used by plot, oplot, or plots all work in a similar way. First, if decompose=0 is set they use the color table index to give the plot color. If decomposed color is in use (device,decomp=1) then the color is given as a 24-bit value, but as a long integer, not as 3 bytes (as for tv). The first 8 bits, 0-255, give the amount of red in the color. The next 8 bits, (0-255)*256L, give the amount of green. The last 8 bits, (0-255)*256L*256L, give the amount of blue. So if R,G, and B are three byte values giving the amount of Red, Green, and Blue as 0 to 255, then the full color value to use would be C = R + G*256L + B*65536L (make sure you are using decomposed color, device,decomp=1).

tarclr(r,g,b) is an easy way to specify
a 24-bit color: oplot,x,y,color=tarclr(r,g,b)
This difference in the way colors are specified makes it harder to write software that works for both 8-bit and 24-bit color. An APL IDL library routine, tarclr, solves this problem. It detects which color mode is in effect and returns the correct color. For 8-bit color tarclr finds the closest match in the color table to a given target color. For 24-bit color tarclr returns the exact color. Colors may be specified as RGB or HSV. tarclr has some other conveniences for 8-bit color, such as setting color table values and allowing reserved colors. Those options are not needed for 24-bit color and are ignored. Do c=tarclr(/help) for the details. tarclr detects decomp=0 for 24-bit color and treats this case like 8-bit color. That means it will search the current color table for the closest match.

The IDL erase command takes color in the same format as the plot commands. So tarclr works for erase also: erase,tarclr(255,255,0).

JPEG is an easy image format for 24-bit
Differences: saving images
For 8-bit color it was easy to read the displayed image and save it in a non-lossy format such as GIF. That is no longer so easy. First, there is no 8-bit image involved. The only image available to read-back is the 24-bit image. Compressed TIFF might be a choice for non-lossy image storage. If lossy storage is ok then the easiest format is JPEG:

   a = tvrd(true=3)
   write_jpeg,'test.jpg',a,true=3
See the documentation for write_jpeg for more details. An example using JPEG is how the background image on this page was made.

color_quan converts a 24-bit image to 8-bit.
If for some reason you really need an 8-bit image format like GIF there are several ways to get one. One is to convert the 24-bit image to an 8-bit image using color_quan:

   a = tvrd(true=3)
   c = color_quan(a,3,r,g,b)
Array c will contain the 8-bit image and r,g,b is the color table needed to view it. To see the result you will have to use the decomp=0 trick:
   device,decomp=0
   tvlct,r,g,b
   tv,c
Remember to load the color table first, then the image. The 8-bit image and color table may be saved as a GIF using the write_gif routine (but not from the screen of course. Save the arrays c,r,g, and b: write_gif,'name.gif',c,r,g,b).

zwindow lets you work in 8-bit mode
Another way to get an 8-bit image is to use the Z buffer. The Z buffer is always 8 bits and uses the color lookup table. The APL IDL library routine zwindow (do zwindow,/help for details) was made to make using the Z buffer easy. Make a z window just like an ordinary window, do the graphics commands, and read the result (image and color table) back just like an 8-bit display window. Example:

   zwindow
   loadct,4
   tvscl,dist(200)
   zwindow,/copy
   loadct,3
   zwindow,/copy
Note that in the above example, calling zwindow sets the graphics device to the Z buffer (do help,/device to see that). The command zwindow,/copy temporarily switches to X windows to display the result, then returns to the Z buffer graphics device. When done with the Z buffer do zwindow,/close to return to the initial graphics device (and free memory used for the Z buffer). You can switch back and forth between the z window and x windows, but make sure not to give any z buffer commands while in x windows. Use help,/device to make sure which mode you are in.

An example of saving the Z buffer image created above to a GIF image:

   a=tvrd()
   tvlct,r,g,b,/get
   write_gif,'ztest.gif',a,r,g,b
The APL IDL library routine gifscreen can do this operation even more easily: gifscreen,'ztest.gif'

When using the Z buffer, remember that there is only one. If the Z buffer is in use and it is needed for a subtask it must be saved first, then restored after use.

Working with color images in 24-bit

Reading a GIF image and splitting into R,G,B
The first example will be reading a GIF image and splitting it into Red, Green, and Blue component images. A small version of the example image is shown on the right (click on it to see the full size image).

The image may be read into IDl using the following command:

a = read_image('cat_c.gif',r,g,b)
Array a is the 8-bit image, r,g,b are the color table arrays. To split the image into color components do the following:
rr = r(a)
gg = g(a)
bb = b(a)
These components may be easily displayed. First find the size of the image:
idl> help,a
A               BYTE      = Array[1033, 750]
Set up a display window:
window,xs=1033,ys=750
Individual color components may be loading using the TV command and giving the channel: 1=red, 2=green, 3=blue:
erase & tv,rr,chan=1
erase & tv,gg,chan=2
erase & tv,bb,chan=3
Note, the erase is needed to show individual channels, otherwise each tv command will add the new channel giving the full color image as the end result. Try loading all the color components into the wrong channels to get some interesting images. Also, individual color channels may be processed in some way before loading, perhaps smoothed or shifted.

To save the individual color components in GIF files first set up a linear color table: lin = bindgen(256). Use this linear array for the desired color and arrays of 0s for the other colors. For example (click on file name for full size result):

write_image,'cat_red.gif','GIF',rr,lin,lin*0,lin*0
write_image,'cat_grn.gif','GIF',gg,lin*0,lin,lin*0
write_image,'cat_blu.gif','GIF',bb,lin*0,lin*0,lin

Saving Red, Green, Blue components in a JPEG image
The image array for a GIF image is a 2-D array. For a JPEG image it is a 3-D array since the color is given directly and not using a lookup table. Each 24-bit image pixel needs 3 bytes. Let the image size be m × n. The image pixels may be arranged in one of 3 ways:

Pixel interleaved: Image array dimensions: [3,m,n]
Row-interleaved: Image array dimensions: [m,3,n]
Band-interleaved: Image array dimensions: [m,n,3]
The R,G,B components may be concatentated to get a 3-D array. However care must be used to avoid forming a 2-D array. Using the previous color component arrays as an example:
[rr,gg,bb] gives an array of [3099, 750]
[[rr],[gg],[bb]] gives an array of [1033, 2250]
[[[rr]],[[gg]],[[bb]]] gives an array of [1033, 750, 3]

Note the first two cases concatenate along an image edge to give a larger 2-D array. The third case gives a useful 3-D array. The result is saved:

write_image,'cat_c.jpg','JPEG',[[[rr]],[[gg]],[[bb]]]
write_image figures out the type of channel interleaving so that need not be specified. Note, to set a quality factor for the JPEG save just add the keyword to the call. This is not documented in the online IDL help but it works as expected. The JPEG version of the image is less than 1/4 the size of the GIF version.

TVRD and saving as a JPEG
The TVRD procedure can read back the 24-bit image into a 3-D array with the type of color channel interleaving specified using the TRUE keyword. For example:

idl> help,tvrd(true=1)
    BYTE      = Array[3, 1033, 750]
idl> help,tvrd(true=2)
    BYTE      = Array[1033, 3, 750]
idl> help,tvrd(true=3)
    BYTE      = Array[1033, 750, 3]
For a 3-D array in any of the above formats just use write_image,filename,'JPEG',array to save as a JPEG image.


By R. Sterner