Get Teem at SourceForge.net. Fast, secure and Free Open Source software downloads
teem / nrrd / demo

  Basic 3D volume inspection with unu

This page covers the basics of using unu to look at a 3D scalar dataset. One trick which simplifies command-line data inspection is to make an alias for an image viewing program. On Windows, there is a "pv" program as part of the Windows binary build of teem that can view PNG images easily. Windows users should be using Cygwin in order to get anything useful done here. Everyone else has the "xv" program for looking at PPM/PGM images (among other formats).

Thus, Windows/Cygwin users should make this alias:

alias XV "unu quantize -b 8 | unu save -f png | pv -"
and everyone else should make this alias:
alias XV "unu quantize -b 8 | unu save -f pnm | xv -"
The "XV" alias will be used repeatedly below.

The examples below will use the vfrhand.nhdr dataset created in another nrrd demonstration page.

Histograms

To get a sense of the value distribution in a volume, you make a histogram with "unu histo", and then display this histogram in a meaningful way with "unu dhisto".

For integral data values (as result from most scans), its best for each bin in the histogram to have the same number of values mapping to it, or to have exactly one value mapping to each bin. Currently, "unu histo" doesn't have an easy way of doing this automatically, so you first have to learn the value range by using "unu minmax":

unu minmax vfrhand.nhdr
Which returns:
min: 0.000000
max: 2632.000000
That means if we make a histogram with 878 bins, we have a nearly perfect mapping of three values per bin (878 * 3 == 2634 == one more than # unique values). We can constrain the range of values that are histogrammed with the "-min" and "-max" options of "unu histo", but it will probably suffice to use its default behavior of histogramming the exact full range of values it sees in the input nrrd. Once we have formed the histogram, it can be displayed with "unu dhisto", which has to be told (via "-h") how high to make the image:
unu histo -i vfrhand.nhdr -b 878 | unu dhisto -h 600 | XV

The bin counts in the histogram are linearly scaled and plotted in the white part of the image. The gray plot shows a base-10 logarithm of the histogram, which is very useful for seeing variations in small bin counts. The horizontal lines are the powers of 10 for the logarithm-scaled histogram: the first line marks 10 hits, second line marks 100 hits, and so on.

This particular histogram has two clear peaks, the low one for the background value, and the middle one for soft tissue. Notice that there isn't really a peak for bone- its values are spread over a wide range of high values.

Slices

The easiest way to reduce the dimensionality of data from three to two is by taking a slice, with unu slice. The two things you need to know are the axis along which to slice, and the coordinate to slice at. These are given with the "-a" and "-p" options, respectively:
unu slice -i vfrhand.nhdr -a 0 -p 100 | XV
unu slice -i vfrhand.nhdr -a 1 -p 100 | XV
unu slice -i vfrhand.nhdr -a 2 -p 100 | XV
"-a 0" "-a 1" "-a 2"

If you want to save the image out to disk instead of displaying it, you might say something like:

unu slice -i vfrhand.nhdr -a 2 -p 100 | unu quantize -b 8 -o z100.png
The pre-built versions of unu know how to read and write PNG images. Because it has lossless compression of gray scale and color images, as well representing images with 8-bit or 16-bit channels, and because it is supported in nearly every browser and office application, PNG is the only format anyone should ever think about using. No kidding.

Projections

The other way to reduce the dimension of a dataset is to project each scanline along some axis to a single scalar quantity by "measuring" all the values in the scanline. This is done with "unu project". Nrrd knows about a dozen different measurements, but there are only three that are especially useful for dataset inspection: max (or maybe min), sum, and variance:
unu project -i vfrhand.nhdr -a 1 -m max | XV
unu project -i vfrhand.nhdr -a 1 -m sum | XV
unu project -i vfrhand.nhdr -a 1 -m var | XV
"-m max" "-m sum" "-m var"

Notice that the variance image has the useful effect of clarifying where there are large changes in the value, and this helps delimit features of interest, like the hand. In this kind of CT dataset, the more "interesting" things tend to be at higher data values, so doing a max projection helps show them. There are some kinds of datasets where the interesting values are lower, so the analogous measure would be min (with "-m min") instead of max.

NOTE: As currently implemented, "unu project" is pretty stupid about how it traverses memory. Thus "unu project -a 0" will run faster than will "unu project -a 1", which will run faster than "unu project -a 2". This will probably be fixed in later version of nrrd.

In projection images (and often in slice images) the contrast can be improved to show more image detail. One method of automatic contrast adjustment is histogram equalization, which is available as "unu heq". This needs to know how many bins to put in the histogram (via the "-b" option), and how much of the equalization to apply (via the "-a"). The examples below demonstrate usefully reliable values for both of these:

unu project -i vfrhand.nhdr -a 1 -m max | unu heq -b 3000 -a 0.8 | XV
unu project -i vfrhand.nhdr -a 1 -m sum | unu heq -b 3000 -a 0.8 | XV
unu project -i vfrhand.nhdr -a 1 -m var | unu heq -b 3000 -a 0.8 | XV
"-m max" "-m sum" "-m var"

If you want to better see small variations in the values in a projection, you can use a colormap. Nrrd knows how to read unadorned text files, so a file like darkhue.txt is the simplest way to store a colormap. When the colormap control points are regularly spaced (and hence not explicitly represented), the command to do the colormapping is "unu rmap". The result of the mapping will have the same type as the colormap itself, and because plain text files are always read in as floating point values, the result has to be quantized again before display (but "XV" does that for us):

unu project -i vfrhand.nhdr -a 1 -m max \
 | unu heq -b 3000 -a 0.8 | unu rmap -m darkhue.txt | XV
unu project -i vfrhand.nhdr -a 1 -m sum \
 | unu heq -b 3000 -a 0.8 | unu rmap -m darkhue.txt | XV
unu project -i vfrhand.nhdr -a 1 -m var \
 | unu heq -b 3000 -a 0.8 | unu rmap -m darkhue.txt | XV
"-m max" "-m sum" "-m var"
There is one final type of projection that function as a crude kind of isosurface visualization, "-m histo-min". This treats each scanline as a histogram, and looks for the first non-zero value, and returns its bin index. Similarly, "-m histo-max" returns the highest non-zero bin index. In order to usefully apply these measures, however, the values in the dataset have to be shifted downward by an amount corresponding to the isovalue of interest.

From looking at the histogram above, and knowing the value range, we can guess that an isosurface for the air/soft-tissue boundary is about at value 500, and an isosurface for the soft-tissue boundary is about at value 1200. The first two steps here clamp the values to be no lower than 500, and the subtracts 500 from all the values. The last step ("unu 2op exists") is a trick to deal with the fact that some scanlines had no values greater than 500, so there is no sensible depth value for the isosurface.

unu 2op max 500 vfrhand.nhdr | unu 2op - - 500 \
 | unu project -a 1 -m histo-min | unu 2op exists - 0 | XV
unu 2op max 1300 vfrhand.nhdr | unu 2op - - 1300 \
 | unu project -a 1 -m histo-min | unu 2op exists - 0 | XV
500 1300

To look at the "far side" of the object, use "-m histo-max". Histogram equalization and colormapping are useful here as well:

alias IMG "unu 2op exists - 0 | unu heq -b 3000 -a 0.8 -s 1 | unu rmap -m darkhue.txt"
unu 2op max 500 vfrhand.nhdr | unu 2op - - 500 \
 | unu project -a 1 -m histo-min | IMG | XV
unu 2op max 1300 vfrhand.nhdr | unu 2op - - 1300 \
 | unu project -a 1 -m histo-min | IMG | XV
unu 2op max 500 vfrhand.nhdr | unu 2op - - 500 \
 | unu project -a 1 -m histo-max | IMG | XV
unu 2op max 1300 vfrhand.nhdr | unu 2op - - 1300 \
 | unu project -a 1 -m histo-max | IMG | XV
500, "-m histo-min" 1300, "-m histo-min" 500, "-m histo-max" 1300, "-m histo-max"

An "IMG" alias was set up for some repetitious steps. Notice the "-s 1" option to "unu heq": this turns on "smart" processing, so that the value warping based on the value histogram is not at all affected by a constant background value, which is necessary for this particular kind of projection.