A QUICK TOUR OF THE DIGITAL IMAGE PROCESSING PACKAGE

Mathematica Developer Conference
October 21-23, 1999

Mariusz Jankowski
mjkcc@usm.maine.edu

Introduction

The ImageProcessing package is a new Mathematica 4 add-on package for digital image processing. Designed for speed and functionality, the package features a comprehensive collection of over one hundred one- and two-dimensional linear and non-linear image operators for common measurement, analysis and image enhancement tasks. Here we give a quick tour, with references to the full documentation in the User's Guide, of selected ImageProcessing functions and operators. All the examples are taken from the User's Guide.

Image representation

This loads the package.

<< ImageProcessing`

A digital image is a two-dimensional (2-D) discrete signal. Mathematically, such signals can be represented as functions of two independent variables, for example, a brightness function of two spatial variables. A monochrome digital image f(x, y) is a 2-D array of luminance values,

f(x, y) = (({{f(0, 0), f(0, 1), ., f(0, N - 1)}, {f(1, 0), f(1, 1), ., f(1, N - 1)}, {., ., ., .}, {f(M - 1, 0), f(M - 1, 1), ., f(M - 1, N - 1)}}))

with  0 ≤  f(x, y)  ≤  L, and typically L = 255. Each element of the array is called a pel (picture element), or more commonly pixel. Typical image dimensions are M×N =   256×256 and 512×512.

Consider a small portion of the "head" image, one of the monochrome example images. We read the image and extract the raw pixel data. The example images included in the ImageProcessing package are located in the Data directory of the root ImageProcessing directory. The location on any system, is given by the system variable $ImageDataDirectory. In a typical  Windows installation the path may be as follows.

$ImageDataDirectory

The directory paths listed in this system variable are automatically searched for named files in an ImageRead operation.

H = ImageRead["head.tif"] ;

Take[RawImageData[H],  {64, 71}, {64, 71}] // MatrixForm

Small values represent dark areas of an image, while large values represent bright pixels.

A color digital image is typically represented by a triplet of values, one for each of the color channels, as in the frequently used RGB color scheme. The individual color values are almost universally 8-bit values, resulting in a total of 3 bytes (or 24-bits) per pixel. This yields a three-fold increase in the storage requirements for color versus monochrome images.  Naturally, there are a number of alternative methods of storing the image data. Most widely used are the so-called pixel-interleaved (or meshed) and color interleaved (or planar) formats. Less frequent, but possible are row-wise or column-wise interleaving methods. In a pixel-interleaved format every image pixel is represented by a list of three values:

f(x, y)=(({{(Subscript[r, 0, 0]), (Subscript[g, 0, 0]), (Subscript[b, 0, 0]), (Subscript[r, 0, 1]), 
                  (Subscript[g, 0, 1]), (Subscript[b, 0, 1]), ., (Subscript[r, 0, N - 1]), 
                  (Subscript[g, 0, N - 1]), (Subscript[b, 0, N - 1])}, {(Subscript[r, 1, 0]), (Subscript[g, 1, 0]), (Subscript[b, 1, 0]), (Subscript[r, 1, 1]), 
                  (Subscript[g, 1, 1]), (Subscript[b, 1, 1]), ., (Subscript[r, 1, N - 1]), 
                  (Subscript[g, 1, N - 1]), (Subscript[b, 1, N - 1])}, {., ., ., .}, {(Subscript[r, M - 1, 0]), (Subscript[g, M - 1, 0]), 
                  (Subscript[b, M - 1, 0]), (Subscript[r, M - 1, 1]), (Subscript[g, M - 1, 1]), 
                  (Subscript[b, M - 1, 1]), ., (Subscript[r, M - 1, N - 1]), 
                  (Subscript[g, M - 1, N - 1]), (Subscript[b, M - 1, N - 1])}})),

whereas in the color-interleaved format, the color information is separated into three matrices, one for each of the three color channels:

f(x, y)= ((({{(Subscript[r, 0, 0]), (Subscript[r, 0, 1]), ., (Subscript[r, 0, N - 1])}, {(Subscript[r, 0, 1]), (Subscript[r, 1, 1]), ., (Subscript[r, 1, N - 1])}, {., ., ., .}, {(Subscript[r, M - 1, 0]), (Subscript[r, M - 1, 1]), ., (Subscript[r, M - 1, N - 1])}})), (({{(Subscript[g, 0, 0]), (Subscript[g, 0, 1]), ., (Subscript[g, 0, N - 1])}, {(Subscript[g, 0, 1]), (Subscript[g, 1, 1]), ., (Subscript[g, 1, N - 1])}, {., ., ., .}, {(Subscript[g, M - 1, 0]), (Subscript[g, M - 1, 1]), ., (Subscript[g, M - 1, N - 1])}})),(({{(Subscript[b, 0, 0]), (Subscript[b, 0, 1]), ., (Subscript[b, 0, N - 1])}, {(Subscript[b, 0, 1]), (Subscript[b, 1, 1]), ., (Subscript[b, 1, N - 1])}, {., ., ., .}, {(Subscript[b, M - 1, 0]), (Subscript[b, M - 1, 1]), ., (Subscript[b, M - 1, N - 1])}}))).

Here we read the color example image - "beans".

B = ImageRead["beans.tif"] ;

Here we extract a 4x4 region and display it in a meshed format.

RawImageData[ImageTake[B, {25, 28}, {25, 28}]] // MatrixForm

Here, the same region of the color image is displayed in a planar format.

MatrixForm  /@  
      RawImageData[
        PlanarImageData[ImageTake[B, {25, 28}, {25, 28}]]]

The RGB color scheme is just one of many color representation methods used in practice. The letters R, G, B stand for red, green and blue, the three primary colors used to synthesize any one of (2^24) or approximately 16 million colors. Equal quantities of the three color values result in shades of gray in the range {0, 255}. Other supported color models, include monochrome, HSV which stands for hue, saturation, value and CMYK which stands for cyan, magenta, yellow. The latter has found application primarily in the printing and graphics markets. HSV is useful in color image processing, since it separates the color information from brigthness.

Further reading

Getting Started: How to find the example image?

User's Guide: Sections 2.1, 2.2, 2.3.

Function Index: ImageRead, ImageTake, PlanarImageData, RawImageData.

Structure of ImageData

The color image data structure is encapsulated in an ImageData Mathematica expression. ImageData is a graphics primitive that facilitates the easy manipulation and handling of single and multi-channel images. This reads in a small example image in RGB color format.

R = ImageRead["rgb.tif"];

ImageData like other graphics primitives may be displayed using Show command.

Show[Graphics[R]];

Here is the input form of ImageData.

R // InputForm

The default ... This converts the image to a planar format.

PlanarImageData[R] // InputForm

This returns a monochrome (i.e. single channel) image.

ToGrayLevel[R] // InputForm

This converts from the default RGB format to HSV.

ToHSVColor[R] // InputForm

Further reading

User's Guide: Section 2.4.

Function Index: ImageRead, ToGrayLevel, ToHSVColor, PlanarImageData.

Image display

All image data imported into Mathematica using the ImageRead command may be displayed using Show, since ImageRead returns an ImageData object  that is Show compatible.

Show[
        GraphicsArray[Graphics /@ {B, G = ToGrayLevel[B]}]];

Raw monochrome image data may be visualized in any number of ways, such as surface plots, contour plots and  intensity plots. The latter is the most natural (see ListDensityPlot, Raster).

Here is a 32-by-32  fragment of the example image.

tmp = RawImageData[ImageTake[G,  {12, 43}, {12, 43}]];
\(\(Show[
        GraphicsArray[{{ListDensityPlot[tmp, \ Mesh -> False, \ 
                DisplayFunction -> Identity], 
Graphics[
                Raster[tmp, {{0, 0}, {32, 32}}, {0, 255}], 
                AspectRatio -> Automatic]}, 
	{ListPlot3D[tmp, 
                DisplayFunction -> Identity], 
	
              ListContourPlot[tmp, Contours -> 6, 
                DisplayFunction -> Identity]}}]];\)\)

According to (3), color image data in a planar format may be treated as three separate single color images, one for each of the color channels. Here, the three channels are displayed as a GraphicsArray.

Show[GraphicsArray[Graphics /@ ToChannels[B]]];

Alternatively, the three individual color channels may be viewed as separate monochrome images. This is a convenient conceptualization of color images allowing the simple and immediate extension of many monochrome image processing techniques to the color image domain.

Further reading

User's Guide: Section 2.5.

Function Index: ImageRead, ImageTake, ToChannels, ToGrayLevel, PlanarImageData.

Image processing with point operations

Image enhancement refers to any technique that improves or modifies the image data, either for puposes of subsequent visual evaluation or further numerical processing. Image enhancement techniques include gray level and contrast manipulation, noise reduction, edge sharpening, linear and non-linear filtering, magnification, pseudocoloring, and so on. One useful, broad categorization of enhancement techniques divides them into point- and region-based operations. Point operations modify pixels of an image based on the value of the pixel. These are also called zero memory operations. In contrast, region-based operations calculate a new pixel value based on the values in a local neighborhood (typically small).

Negating an image is the simplest image modification operation. This operation changes large values to small and vice versa, according to

\(x\&_\) =(2^n)-1-x

where x is a pixel value and n = 8 for the typical 8-bits-per-pixel monochrome image. For color images the same transformation is applied to the individual color values. Here we show the effect of negation on both the color and grayscale images.

Show[GraphicsArray[Graphics /@ {255 - B, 255 - G}]];

A common approach to contrast modification is to use a power law point transformation, where each pixel of the original image is raised to a specified exponent value. By selecting the exponent values appropriately, either high or low luminance values can be boosted. A simple, yet useful contrast manipulation technique, is to define a piecewise linear transformation to selectively stretch and/or compress a range of luminance values. The slope of the transformation is chosen greater than 1 in the region of stretch and less than 1 in the region of compression.

Examples of the effect of selected point operations on the color "beans" image are displayed below.

x = Range[0, 255];

{{ListPlot[ScaleLinear[(x^3), {0, 255}], PlotJoined -> True, 
            DisplayFunction -> Identity], 
          ListPlot[ScaleLinear[Log[1 + x], {0, 255}], PlotJoined -> True, 
            DisplayFunction -> Identity]}, {Graphics[
            ScaleLinear[(B^3), {0, 255}]], 
Graphics[
            ScaleLinear[Log[1 + B], {0, 255}]]}};

Show[GraphicsArray[%]];

The image histogram is an estimate of the probability density of the image pixels. As such, it measures the frequency of occurance of the pixel luminance values. Many higher-level image processing tasks require the calculation of a histogram. Here we present the histograms of each of the three color channels in the "beans" image.

ImageHistogram[B];

Show[
        GraphicsArray[ListPlot[#,  PlotJoined -> True, 
                PlotRange -> {0, 0.04}, AspectRatio -> 1/3,  
                DisplayFunction -> Identity] & /@ % ]];

Image equalization, or linearization, is a common image enhancement technique. Here is a linearized version of the beans iamge.

Show[
        GraphicsArray[Graphics /@ {B, HistogramEqualize[B]}]];

Amplitude thresholding is one of many segmentation techniques. Two-level or binary thresholding changes a value to 0 or 1 depending on the setting of a threshold value. Here is an image segmentation example where we extract the green beans by thresholding the individual color channels. The respective thresholds were selected from an examination of the channel histograms.

T = Threshold[B, {170, 150, 150}];

Show[Graphics[PlanarImageData[T]]];

We now find all the image regions that do not have green pixels and set them to black.

RegionProcessing[0 &, B,  
        Where[T, _?( # != {0, 1, 0} &)]];

Here we display the original and segmented images.

Show[GraphicsArray[Graphics /@ {B, %}]];

Further processing with morphological filters (User's Guide: Section 6.1) may be used to clean up the segmented image.

Further reading

User's Guide: Section 3.2, 3.3, 3.4, 7.2.

Function Index: HistogramEqualize, ImageHistogram, PlanarImageData, RegionProcessing, ScaleLinear, Threshold, Where.

Image processing using LSI filters

Many useful image operations are implemented with linear shift-invariant (LSI) filters. A smoothing operation is frequently a first step in operations such as noise reduction, edge detection or interpolation. A commonly used smoothing filter has constant coefficients. The effect of smoothing or blurring an image is achieved by convolving the image with such a filter. The third example will demonstrate the use of convolution to "sharpen" an image by a method called unsharp masking. The simplest form of unsharp masking may be implemented by subtracting a scaled smoothed image from the original.

Here we smooth and sharpen image the "head" example image.

(Subscript[H, S]) = 
        DiscreteConvolve[
          H,  (1/9.)  {{1. , 1. , 1. }, {1. , 1. , 1. }, {1. , 1. , 
                1. }}];
(Subscript[H, U]) = ImagePlus[ (5/4) H, - (1/4) (Subscript[H, S])];

Here we display the three results.

Show[GraphicsArray[Graphics /@ {(Subscript[H, S]), (Subscript[H, U])}]];

Edge detection is an important step in many shape-based recognition tasks. Edge detection is typically implemented as a convolution operation with appropriately chosen differentiating filters. Two examples of edge detection using two common edge filters, the Sobel gradient edge detector and the Laplacian-of-Gaussian edge detector, conclude this section.

Show[
        GraphicsArray[
          Graphics /@ {Threshold[EdgeMagnitude[H, N[SobelFilter]], 50], 
              ZeroCrossing[
                EdgeMagnitude[H, LoGFilter[21, 21]]]}]];

Further reading

User's Guide: Sections 5.2, 5.3, 7.3.

Function Index: DiscreteConvolve, EdgeMagnitude, ImagePlus, LoGFilter, SobelFilter, Threshold, ZeroCrossing.

Region-of-interest processing

It is sometimes of interest to process a single sub-region of an image leaving other regions unchanged. This is commonly referred to as region-ot-interest (ROI) processing. Here we show an example of edge detection in a rectangular ROI using a real image. This loads the example image.

L = ImageRead["lipid.tif"];

Here we define a region-of-interest. Image regions may be conveniently selected using the mouse and keyboard. To select a particular region, click the displayed graphics object. In Windows (for other systems consult Input/Get Graphics Coordinates ), press the [Ctrl] key and click a point of interest. The point will be selected. This may be repeated for as many points as desired. Use the Copy and Paste command to paste the recorded list of positions to any cell or expression in the notebook.

roi = 
      Polygon[Reverse /@ 
          Round[{{29.9211, 84.7558}, {126.665, 185.116}, {187.242, 
                49.4942}}]]

This shows the result of applying a Sobel edge detector to the region defined by roi.

Show[
        Graphics[
          RegionProcessing[EdgeMagnitude[#1, N[SobelFilter]] &, L, 
            roi]]];

Further reading

User's Guide: Sections  5.6, 7.3.

Function Index: ImageRead, EdgeMagnitude, RegionProcessing, SobelFilter.

Image transforms: DCT

It is well known that LTI operators may be implemented in the Fourier transform domain, leading to computational efficiencies. The energy compaction property of transforms such as the discrete Fourier transform (DFT), discrete cosine transform (DCT) or discrete wavelet transform (DWT) plays an important role in many image/video compression techniques. Here we demonstrate image compression using the DCT transform.

Here we take the block cosine transform of the head image. The blocks are non-overlapping, of dimensions 8x8.

ℋ = 
        BlockProcessing[DCT[# - 128]  &, RawImageData[H], {8, 8}, {8, 8}, 
          MatrixJoin];

Here we show a fragment of the head image and the DCT coefficients.

Show[
        GraphicsArray[ListDensityPlot[Take[#,  {64, 95}, {64, 95}], 
                Mesh -> False,  
                DisplayFunction -> Identity]  &  /@  {RawImageData[H],  
              Abs[ℋ]}]];

We now retain cosine coefficients located in a low-frequency zone of each block. We then use the inverse DCT to calculate an approximation to the original image. Here is a typical zonal mask.

(M = 
          ZeroPad[{{1, 1, 1, 1, 1}, {1, 1, 1, 1, 0}, {1, 1, 1, 0, 0}, {1, 1, 
                0, 0, 0}, {1, 0, 0, 0, 0}},  {0, 3}, {0, 3}])  // 
      MatrixForm

\(\(H\&~ = 
        BlockProcessing[
            Round[IDCT[M*\ #]] &, \ ℋ, {8, 8}, {8, 8}, 
            MatrixJoin] + 128;\)\)

\(\(Show[
        GraphicsArray[
		\(ListDensityPlot[#, Mesh -> False, 
                Frame -> False, PlotRange -> {0, 255}, \ 
                DisplayFunction -> Identity] &\)\  /@ \ {RawImageData[H], 
              H\&~, RawImageData[H] - H\&~ + 128}]];\)\)

The compression capabilities of the DCT are clearly visible.  Using only 23% of the image's total energy, the reconstructed image is a reasonable approximation of the original. The error signal is on the farthest right. The approximation \(TraditionalForm\`H\&~\) is guaranteed to improve as the number of coefficients is increased.

Further reading

User's Guide: Sections 8.4, 8.7.

Function Index: BlockProcessing, DiscreteCosineTransform, InverseDiscreteCosineTransform, RawImageData.