Get to Know a Feature: Color Table Wave Creation
Created on October 4 at 12:02 pm - by: admin
In "Get to Know a Feature: Color Table Wave Basics", we showed how Igor Pro 7's color table waves are more flexible than Igor Pro 6's color index waves because multiple uses of the same color table wave can cover differing Z value ranges.
We previously showed that you can use Data→Packages→Color Wave Editor to create color table waves by modifying a copy of a built-in color table, or by directly choosing the color for each row of a color wave.
COLOR TABLE WAVE VALUES
Color table (and color index) waves have one row for each color, and 3 or 4 columns for red, green, blue and optionally alpha components containing values that range from 0-65535.
It is useful to realize that any color index wave can also be used as a color table wave; the difference is simply that Igor 7 ignores the X scaling when using a wave as a color table wave.
Here are some other ways to create color table waves:
IMPORTING RGB VALUES FROM A CSV FILE
Sometimes a researcher is kind enough to provide a data file containing the red, green, blue values in a data file, which we can import and rescale to Igor's color range.
For example, from Color Schemes Appropriate for Scientific Data Graphics, you can download a "color number text file", such as this Blue-to-Green 14-step table:
You could just copy the values from the text file and paste them into an Igor table, but for larger color tables that would be too painful, and what are computers for, anyway?
Let's load the file (from the Clipboard in my case) as delimited text into a "color0" matrix wave:
Delimited text load from "Clipboard"
Matrix size: (14,14), wave: color0
•Edit color0
The last three columns are the Red, Green, and Blue components.
An easy way to make a color wave is to Duplicate columns 11, 12, and 13 into a new wave, which we will name "BlueGreen", using Igor 7's handy new /RMD flag for the Duplicate operation:
•Duplicate/O/RMD=[][11,13] color0, BlueGreen // copy all rows, cols 11-13
Igor uses a range of 0-65535 for colors, instead of the 0-255 used here.
It is not obvious, but multiplying by 257 (not the 256 you might expect) scales to Igor's 0-65535 rather nicely:
255.996
•Print 65535 / 257
255
Let's convert the wave's numeric type to unsigned 16-bit integer and then multiply by 257.
•BlueGreen *= 257
It wouldn't hurt to reset the Y scaling to match the column number, and even set dimension labels just for clarity:
•SetDimLabel 1, 0, red, BlueGreen
•SetDimLabel 1, 1, green, BlueGreen
•SetDimLabel 1, 2, blue, BlueGreen
This is what this color table wave looks like in an image plot and ColorScale:
Which compares nicely to the original:
RECREATING A COLOR TABLE FROM A SCREEN CAPTURE
A captured screen shot can be used to reproduce a color table when the numeric values aren't available.
Here's an example of reproducing the many-valued "Ametrine" color table from École Polytechnique Fédérale de Lausanne:
Screen capture of the Ametrine color table
Loading this screen capture as an Igor RGB Image plot is straightfoward; use a screen capture utility to save a .TIFF or .PNG file, and load it as an image.
STEP 1: SAVE A SCREEN CAPTURE TO A FILE
On Macintosh, the built-in screen capture to a file is done by pressing Cmd+Shift+4, using the mouse to drag out a marquee around the to-be-captured element.
Upon release of the mouse button, a .tiff file is saved to the desktop (some versions of Mac OS X save a .png file).
STEP 2: LOAD THE FILE AS AN IMAGE PLOT
Use Igor 7's Data->Load Image dialog and load the TIFF or PNG screen shot file as an RGB image:
STEP 3: EXTRACT THE COLOR TABLE WAVE FROM THE IMAGE PLOT
We want only a portion of the image plot's values for our color table. We can use the marquee to indicate where the colors are, and then create a GraphMarquee menu definition and associated code to extract the colors from that region:
(GraphMarquee code for extracting the color table values, ready to be copied and pasted into an Igor procedure file, follows at the end of this article.)
The resulting color table wave is displayed in an image plot so that you can check that no unintended pixels crept into the color table wave. Adjusting the marquee and selecting Color Table from Selection again updates the same color table wave and image plot.
COMPARING THE EXTRACTED THE COLOR TABLE WAVE WITH IDEAL VALUES
We can check the results by downloading the corresponding csv file:
•LoadWave/J/M/A=ametrine/P=text/K=0 "ametrine.txt" Delimited text load from "ametrine.txt" Matrix size: (256,3), wave: ametrine0
Then we can scale the ametrine0 values from 0-255 to 0-65535 and apply the resulting color table wave to a copy of the image plot with the extracted color table wave for comparison:
•Redimension/U/W ametrine0
•ModifyImage/W=CTabDemoColorTable_1 ColorTableImg ctab= {*,*,ametrine0,0}
Pretty tough to tell the difference:
REUSING THE EXTRACTED THE COLOR TABLE WAVE
A good way to make the color table wave available to multiple experiments is to save the color table wave as an Igor Binary wave in an easily-accessed folder.
Simply load the binary wave and choose it in the Modify Image Appearance dialog.
Whether you copy the binary wave into your experiment file or leave it "shared" (saved separate from the experiment on disk) is up to you.
Igor 7.01 or later will ship with many color table waves in a Color Table Waves folder for convenience. They have been created using the sources listed below.
REFERENCES
- Color Map Advice for Scientific Visualization
- Colormaps - Data Science at Scale
- How to display data by color schemes compatible with red-green color perception deficiencies, by Matthias Geissbuehler and Theo Lasser
- Color Schemes Appropriate for Scientific Data Graphics
- Matplotlib: show colormaps
COLOR TABLE FROM SELECTION CODE
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma version=7.01 // will ship with Igor 701
#pragma IgorVersion=7 // color table waves require Igor 7+
Menu "Image", dynamic
WMColorTableFromMenu("Color Table from Graph..."), /Q, WMColorTableFromGraph()
End
Menu "GraphMarquee", dynamic
WMColorTableFromMenu("Color Table from Selection..."), /Q, WMColorTableFromGraph()
End
Function/S WMColorTableFromMenu(String enabledItem)
String item="" // disappears
String graphName= WinName(0,1)
String images= ImageNameList(graphName, ";")
if( ItemsInList(images) == 1 ) // works with graphs having only 1 image plot
String imageName= StringFromList(0,images)
WAVE/Z image= ImageNameToWaveRef(graphName, imageName)
Variable layers= DimSize(image,2)
if( layers >= 3 ) // must be RGB or RGBA image.
item= enabledItem // "Color Table from Selection", etc.
endif
endif
return item
End
Proc WMColorTableFromGraph(outputName)
String outputName=StrVarOrDefault("root:Packages:WMColorTableFromGraph:ctableName", "ColorTable") // default, missing parameter dialog gives user chance to change it
outputName= CleanupName(outputName,1)
NewDataFolder/O root:Packages
NewDataFolder/O root:Packages:WMColorTableFromGraph
String/G root:Packages:WMColorTableFromGraph:ctableName = outputName
Variable created = WMColorTableGraphOrMarquee(outputName)
if( created )
WMDemoColorTableWave($outputName)
endif
End
// Samples the first image in the top graph along the image's or marquee's
// longest dimension, in the middle of the shortest dimension.
//
// Note that dimensions may not correlate with marquee lengths if h & v scales differ.
// Scakes will not differ if NewImage is used to initially display the image
// and if the graph is not resized afterwards.
//
// Best is to use ModifyGraph width={perUnit,1,top},height={perUnit,1,left} to avoid confusion.
Function WMColorTableGraphOrMarquee(String outputName)
outputName= CleanupName(outputName,1)
if( strlen(outputName) == 0 )
DoAlert 0, "Expected outputName!"
return 0
endif
String graphName= WinName(0,1)
if( strlen(graphName) == 0 )
DoAlert 0, "Expected graph!"
return 0
endif
String images= ImageNameList(graphName, ";")
if( strlen(images) == 0 )
DoAlert 0, "Expected image in "+graphName+"!"
return 0
endif
String imageName= StringFromList(0,images)
WAVE image= ImageNameToWaveRef(graphName, imageName)
Variable layers= DimSize(image,2)
if( layers < 3 )
DoAlert 0, "Expected "+imageName+" to be an RGB image!"
return 0
endif
Variable firstRow, lastRow, firstCol, lastCol
Variable haveMarquee= WMGetMarqueeImageBounds(graphName, imageName, firstRow, lastRow, firstCol, lastCol)
Variable cRows= lastRow-firstRow+1
Variable cCols= lastCol-firstCol+1
if( layers > 4 )
layers= 4
endif
Variable center
if( cRows > cCols )
center= floor((firstCol+lastCol)/2)
Make/O/N=(cRows, layers) $outputName/WAVE=ctable
ctable= image[p+firstRow][center][q]
else
center= floor((lastRow+firstRow)/2)
Make/O/N=(cCols, layers) $outputName/WAVE=ctable
ctable= image[center][p+firstCol][q]
endif
ctable *= 257 // scale to 0-65535
Redimension/U/W ctable // convert to 16-bit int wave
// set dimension labels
SetDimLabel 1, 0, red, ctable
SetDimLabel 1, 1, green, ctable
SetDimLabel 1, 2, blue, ctable
// remove any unnecessary alpha column
if( layers == 4 )
ImageStats/M=1/G={0, DimSize(ctable,0)-1, 3, 3} ctable
if( V_min == V_max )
Redimension/N=(-1,3) ctable
else
SetDimLabel 1, 3, alpha, ctable
endif
endif
return WaveExists(ctable) // in case of error
End
Function WMGetMarqueeImageBounds(graphName, imageName, firstRow, lastRow, firstCol, lastCol)
String graphName, imageName
Variable &firstRow, &lastRow, &firstCol, &lastCol
WAVE image= ImageNameToWaveRef(graphName, imageName)
Variable rows= DimSize(image,0)
Variable cols= DimSize(image,1)
firstRow= 0
lastRow= rows-1
firstCol=0
lastCol= cols-1
// Use entire image
String info= ImageInfo(graphName, imageName, 0)
String hAxis= StringByKey("XAXIS", info)
String vAxis= StringByKey("YAXIS", info)
GetMarquee /W=$graphName/Z $hAxis, $vAxis
Variable haveMarquee= V_Flag
if( haveMarquee ) // if marquee, use subset of image
// convert horizontal (X) coordinates to rows
// convert vertical (Y) coordinates to cols
Variable swap
// if swapXY, exchange V_left with V_top, and V_right with V_bottom
String hAxisInfo= AxisInfo(graphName, hAxis)
String hAxisType= StringByKey("AXTYPE", hAxisInfo)
Variable axesSwapped= CmpStr(hAxisType, "left") == 0 || CmpStr(hAxisType, "right") == 0
if( axesSwapped )
swap= V_left
V_left= V_top
V_top= swap
swap= V_right
V_right= V_bottom
V_bottom= swap
endif
firstRow= (V_left-DimOffset(image,0))/DimDelta(image,0)
lastRow= (V_right-DimOffset(image,0))/DimDelta(image,0)
if( firstRow > lastRow )
swap= firstRow
firstRow= lastRow
lastRow= swap
endif
if( firstRow < 0 )
firstRow= 0
endif
if( lastRow > rows-1 )
lastRow= rows-1
endif
firstRow= floor(firstRow)
lastRow= ceil(lastRow)
firstCol= (V_top-DimOffset(image,1))/DimDelta(image,1)
lastCol= (V_bottom-DimOffset(image,1))/DimDelta(image,1)
if( firstCol > lastCol )
swap= firstCol
firstCol= lastCol
lastCol= swap
endif
if( firstCol < 0 )
firstCol= 0
endif
if( lastCol > cols-1 )
lastCol= cols-1
endif
firstCol= floor(firstCol)
lastCol= ceil(lastCol)
endif
return haveMarquee
End
Function WMDemoColorTableWave(WAVE ctable)
Variable rows= DimSize(ctable,0)
String name= NameOfWave(ctable)
String graphName= CleanupName("CTabDemo"+name,0)[0,30]
String demoWaveName= CleanupName(name+"Img",0)[0,30]
Make/O/N=(rows, 30) $demoWaveName= p // simple ramp
WAVE img= $demoWaveName
DoWindow/F $graphName
if( V_Flag == 0 )
Newimage/N=$graphName img
ModifyImage/W=$graphName ''#0, ctab={*,*,ctable} // apply color table wave
endif
ModifyGraph/W=$graphName nticks=0, width=300, height=40, axThick=1
End
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More