Image Enhancement
Chapter describes the basics of improving the visual appearance of images through enhancement operations.
 7.1 Introduction
 7.2 Adding Borders to Images
 7.3 Cropping an Image
 7.4 Amplitude Rescaling
 7.5 Histogram Equalization
 7.6 Lookup Table Modification
 7.7 Convolution Filtering
 7.8 Median Filtering
 7.9 Frequency Domain Processing
 7.10 Singleimage Pixel Point Processing
 7.11 Dual Image Pixel Point Processing
 7.12 Thresholding
7.1 Introduction
The ImageN API image enhancement operations include:

Adding borders

Cropping an image

Amplitude rescaling

Histogram equalization

Lookup table modification

Convolution filtering

Median filtering

Frequency domain processing

Pixel point processing

Thresholding (binary contrast enhancement)
7.2 Adding Borders to Images
ImageN provides two different ways of adding a border to an image. These two ways are described in the following paragraphs.
7.2.1 The Border Operation
The Border
operation allows you to add a simple filled border around a source image. The border extends the source image's boundaries by a specified number of pixels.The amount of extension may be specified separately for the top, bottom, and left and right sides. The following types of border fill may be specified:

Zero fill  the border area is extended with zeros (
BORDER_ZERO_FILL
). 
Constant fill  the border area is extended with a specified constant value (
BORDER_CONST_FILL
). An array of constants must be supplied. The array must have at least one element, in which case this same constant is applied to all destination image bands. Or, it may have a different constant entry for each corresponding band. For all other border types, thisconstants
parameter may benull
. 
Extend  the border area is created by copying the edge and corner pixels (
BORDER_COPY
). 
Reflection  the border area is created by reflection of the image's outer edge (
BORDER_REFLECT
). 
Wrap  the border area is extended by "wrapping" the image plane toroidally, that is, joining opposite edges of the image (
BORDER_WRAP
).
Figure 71 Image Borders
The image layout (tile width, height, and offsets; SampleModel
and ColorModel
) is copied from the source. The Border
operation takes one rendered source image and six parameters:
Parameters  Type  Description 

leftPad  Integer  The image's left padding. 
rightPad  Integer  The image's right padding. 
topPad  Integer  The image's top padding. 
bottomPad  Integer  The image's bottom padding. 
type  Integer  The border type. One of BORDER_ZERO, BORDER_CONST_FILL, BORDER_COPY, BORDER_REFLECT, or BORDER_WRAP. The default is BORDER_ZERO. 
constant  double  The constants used by the BORDER_CONST_FILL. 
7.2.2 Extending the Edge of an Image
Some area operations, such as convolve, scale, and rotate, benefit from the addition of an extended border around the source image. The extended border comes into play when the convolution kernel overlaps the source image as the key value is scanned over it.
A BorderExtender
may be applied to an operation using a suitable hint. The hints are defined in Table 71.
Table 71 BorderExtender Hints
Name  Description 
BorderExtenderZero  Extends an image's border by filling all pixels outside the image bounds with zeros. See Section 7.2.2.1, "BorderExtenderZero."\ 
BorderExtenderConstant  Extends an image's border by filling all pixels outside the image bounds with constant values. See Section 7.2.2.2, "BorderExtenderConstant."\ 
BorderExtenderCopy  Extends an image's border by filling all pixels outside the image bounds with copies of the edge pixels. Useful as a way of padding source images prior to area or geometric operations, such as convolution, scaling, or rotation. See Section 7.2.2.3, "BorderExtenderCopy."\ 
BorderExtenderWrap  Extends an image's border by filling all pixels outside the image bounds with copies of the whole image. This form of extension is appropriate for data that is inherently periodic, such as the Fourier transform of an image, or a wallpaper pattern. See Section 7.2.2.4, "BorderExtenderWrap."\ 
BorderExtenderReflect  Extends an image's border by filling all pixels outside the image bounds with copies of the whole image. This form of extension avoids discontinuities around the edges of the image. See Section 7.2.2.5, "BorderExtenderReflect."\ 
The BorderExtender
class is the superclass for four classes that extend a WritableRaster
with additional pixel data taken from a PlanarImage
. Instances of BorderExtender
are used by the PlanarImage.getExtendedData
and PlanarImage.copyExtendedData
methods.
The PlanarImage.getExtendedData
method returns a copy of an arbitrary rectangular region of the image in a Raster
. The portion of the rectangle of interest outside the bounds of the image will be computed by calling the given BorderExtender
. If the region falls entirely within the image, the extender will not be used. Thus it is possible to use a null
value for the extender
parameter when it is known that no actual extension will be required. The returned Raster
should be considered nonwritable. The copyExtendedData
method should be used if the returned Raster
is to be modified.
The PlanarImage.copyExtendedData
method copies an arbitrary rectangular region of the RenderedImage
into a callersupplied WritableRaster
. The portion of the supplied WritableRaster
that lies outside the bounds of the image is computed by calling the given BorderExtender
. The supplied WritableRaster
must have a SampleModel
that is compatible with that of the image.
Each instance of BorderExtender
has an extend
method that takes a WritableRaster
and a PlanarImage
. The portion of the raster that intersects the bounds of the image will already contain a copy of the image data. The remaining area is to be filled in according to the policy of the BorderImage
subclass. The subclasses are described in Table 71.
API: org.eclipse.imagen.Planarimage
Raster getExtendedData(Rectangle region, BorderExtender extender)
void copyExtendedData(WritableRaster dest, BorderExtender extender)
API: org.eclipse.imagen.BorderExtender
static BorderExtender createInstance(int extenderType)
abstract void extend(WritableRaster raster, PlanarImage im)
7.2.2.1 BorderExtenderZero
The BorderExtenderZero
class is a subclass of BorderExtender
that implements border extension by filling all pixels outside of the image bounds with zeros. For example, Figure 72 shows the result of using BorderExtenderZero
to extend an image by adding two extra rows to the top and bottom and two extra columns on the left and right sides.
Figure 72 BorderExtenderZero Example
API: org.eclipse.imagen.BorderExtenderZero
final void extend(WritableRaster raster, PlanarImage im)
7.2.2.2 BorderExtenderConstant
The BorderExtenderConstant
class is a subclass of BorderExtender
that implements border extension by filling all pixels outside of the image bounds with constant values. For example, Figure 73 shows the result of using BorderExtenderConstant
to extend an image by adding two extra rows to the top and bottom and two extra columns on the left and right sides.
In the figure, X is the constant fill value. The set of constants is clamped to the range and precision of the data type of the Raster
being filled. The number of constants used is given by the number of bands of the Raster
. If the Raster
has b bands, and there are c constants, constants 0 through b  1 are used when b ≤ c. If b *c*, zeros are used to fill out the constants array.
Figure 73 BorderExtenderConstant Example
API: org.eclipse.imagen.BorderExtenderConstant
BorderExtenderConstant(double[] constants)
final void extend(WritableRaster raster, PlanarImage im)
7.2.2.3 BorderExtenderCopy
The BorderExtenderCopy
class is a subclass of BorderExtender
that implements border extension by filling all pixels outside of the image bounds with copies of the edge pixels. For example, Figure 74 shows the result of using BorderExtenderCopy
to extend an image by adding two extra rows to the top and bottom and two extra columns on the left and right sides.
Although this type of extension is not particularly visually appealing, it is useful as a way of padding source images prior to area or geometric operations, such as convolution, scaling, or rotation.
Figure 74 BorderExtenderCopy Example
API: org.eclipse.imagen.BorderExtenderCopy
final void extend(WritableRaster raster, PlanarImage im)
7.2.2.4 BorderExtenderWrap
The BorderExtenderWrap
class is a subclass of BorderExtender
that implements border extension by filling all pixels outside of the image bounds with copies of the whole image. For example, Figure 75 shows the result of using BorderExtenderWrap
to extend an image by adding two extra rows to the top and bottom and two extra columns on the left and right sides.
This form of extension is appropriate for data that is inherently periodic, such as the Fourier transform of an image or a wallpaper pattern.
Figure 75 BorderExtenderWrap Example
API: org.eclipse.imagen.BorderExtenderWrap
final void extend(WritableRaster raster, PlanarImage im)
7.2.2.5 BorderExtenderReflect
The BorderExtenderReflect
class is a subclass of BorderExtender
that implements border extension by filling all pixels outside the image bounds with reflected copies of the whole image. For example, Figure 76 shows the result of using BorderExtenderReflect
to extend an image by adding two extra rows to the top and bottom and one extra column on the left and right sides.
This form of extension avoids discontinuities around the edges of the image.
Figure 76 BorderExtenderReflect Example
API: org.eclipse.imagen.BorderExtenderReflect
final void extend(WritableRaster raster, PlanarImage im)
7.3 Cropping an Image
The Crop
operation crops a rendered or renderable image to a specified rectangular area. The x, y, width, and height values are clipped to the source image's bounding box. These values are rounded to type int
for rendered images.
The Crop
operation takes one rendered or renderable source image and four parameters. None of the parameters have default values; all must be supplied.
Parameters  Type  Description 

x  Float  The x origin for each band. 
y  Float  The y origin for each band. 
width  Float  The width for each band. 
height  Float  The height for each band. 
Figure 77 Crop Operation
7.4 Amplitude Rescaling
Amplitude rescaling provides a linear amplitude transformation of input pixel values to output pixel values. Amplitude rescaling can be used to enhance images that have insufficient contrast between the lightest and darkest values, such as caused by underexposure or overexposure of the original image.
The full dynamic range of one band of an eightbit image is 0 to 255. An underexposed image may only contain pixel values from 10 to 180, resulting in an image that does not fully use the dynamic range of the display. Such an image can be greatly improved by linearly stretching the contrast range; mapping the lowest values to 0 and the highest values to 255.
The rescale
operation takes a rendered or renderable source image and maps the pixel values of the image from one range to another range by multiplying each pixel value by one of a set of constants and then adding another constant to the result of the multiplication. If the number of constants supplied is less than the number of bands of the destination, the constant from entry 0 is applied to all the bands. Otherwise, a constant from a different entry is applied to each band. There must be at least one entry in each of the constants and offsets arrays.
The pixel values of the destination image are defined by the following pseudocode:
constant = (constants.length < dstNumBands) ?
constants[0] : constants[b];
offset = (offsets.length < dstNumBands) ?
offsets[0] : offsets[b];
dst[x][y][b] = src[x][y][b]*constant + offset;
The pixel arithmetic is performed using the data type of the destination image. By default, the destination will have the same data type as the source image unless an ImageLayout
containing a SampleModel
with a different data type is supplied as a rendering hint.
The values of the lowest and highest pixel amplitudes must be known. This information can be acquired through the Extrema
operation (see Section 9.3, "Finding the Extrema of an Image").
The following equations show the relationships between the extrema and the scale and offset factors.
: where max(b) and min(b) are the largest and smallest pixel values in the band, respectively.
The rescale
operation takes one rendered or renderable source image and two parameters:
Parameters  Type  Description 

constants  double  The perband constants to multiply by. 
offsets  double  The perband offsets to be added. 
7.5 Histogram Equalization
An image histogram is an analytic tool used to measure the amplitude distribution of pixels within an image. For example, a histogram can be used to provide a count of the number of pixels at amplitude 0, the number at amplitude 1, and so on. By analyzing the distribution of pixel amplitudes, you can gain some information about the visual appearance of an image. A highcontrast image contains a wide distribution of pixel counts covering the entire amplitude range. A low contrast image has most of the pixel amplitudes congregated in a relatively narrow range.
See Section 9.4, "Histogram Generation," for information on how to generate a histogram for an image. The next two sections describe ImageN operations that use an image histogram to enhance an image's appearance.
7.5.1 Piecewise Linear Mapping
The Piecewise
operation performs a piecewise linear mapping of an image's pixel values. The piecewise linear mapping is described by a set of breakpoints that are provided as an array of the form:
float breakPoints[N][2][numBreakPoints]
Where the value of N may be either unity or the number of bands in the source image.
If N is unity, the same set of breakpoints will be applied to all bands in the image. The abscissas of the supplied breakpoints must be monotonically increasing.
The pixel values of the destination image are defined by the following pseudocode:
if(src[x][y][b] < breakPoints[b][0][0])
dst[x][y][b] = breakPoints[b][1][0]);
} else if(src[x][y][b] breakPoints[b][0][numBreakPoints1]) {
dst[x][y][b] = breakPoints[b][1][numBreakPoints1]);
} else {
int i = 0;
while(breakPoints[b][0][i+1] < src[x][y][b]) {
i++;
}
dst[x][y][b] = breakPoints[b][1][i] +
(src[x][y][b]  breakPoints[b][0][i])*
(breakPoints[b][1][i+1]  breakPoints[b][1][i])/
(breakPoints[b][0][i+1]  breakPoints[b][0][i]);
The Piecewise
operation takes one rendered or renderable source image and one parameter:
Parameters  Type  Description 

breakPoints  Float  The breakpoint array. 
Listing 71 shows a code sample of a Piecewise
operation, showing only the construction of the piecewisemapped image and the operation. The generation of the source image, fmt, is not shown.
Listing 71 Example Piecewise Operation
// Create a piecewisemapped image emphasizing low values.
float[][][] bp = new float[numBands][2][];
for(int b = 0; b < numBands; b++) {
bp[b][0] = new float[] {0.0F, 32.0F, 64.0F, 255.0F};
bp[b][1] = new float[] {0.0F, 64.0F, 112.0F, 255.0F};
}
// Create the Piecewise operation.
RenderedOp pw = JAI.create("piecewise", fmt, bp);
7.5.2 Histogram Matching
It is sometimes desirable to transform an image so that its histogram matches that of a specified functional form. The MatchCDF
operation performs a piecewise linear mapping of the pixel values of an image such that the cumulative distribution function (CDF) of the destination image matches as closely as possible a specified cumulative distribution function.
The CDF of an image is its areanormalized threshold area function. The desired CDF for the MatchCDF
operation is described by an array of the form:
float CDF[numBands][numBins[b]]
Where numBins
denotes the number of bins in the histogram of the source image for band b.
Each element in the array CDF[b]
must be nonnegative, the array must represent a nondecreasing sequence, and the last element of the array must be 1.0F. The source image must have a Histogram
object available via its getProperty
method.
The MatchCDF
operation takes one rendered or renderable source image and one parameter:
Parameters  Type  Description 

CDF  Float  The desired cumulative distribution function. 
The operation requires that the image histogram be available.
Listing 72 shows a code sample of a MatchCDF
operation, showing only the histogram operation, construction of two different CDFs, and the operations that use them.
Listing 72 Example MatchCDF Operation
// Retrieves a histogram for the image.
private static Histogram getHistogram(RenderedOp img,
int binCount) {
// Get the band count.
int numBands = img.getSampleModel().getNumBands();
// Allocate histogram memory.
int[] numBins = new int[numBands];
double[] lowValue = new double[numBands];
double[] highValue = new double[numBands];
for(int i = 0; i < numBands; i++) {
numBins[i] = binCount;
lowValue[i] = 0.0;
highValue[i] = 255.0;
}
// Create the Histogram object.
Histogram hist = new Histogram(numBins, lowValue, highValue);
// Set the ROI to the entire image.
ROIShape roi = new ROIShape(img.getBounds());
// Create the histogram op.
RenderedOp histImage =
JAI.create("histogram", img,
hist, roi, new Integer(1), new Integer(1));
// Retrieve the histogram.
hist = (Histogram)histImage.getProperty("histogram");
return hist;
}
// Create an equalization CDF.
float[][] CDFeq = new float[numBands][];
for(int b = 0; b < numBands; b++) {
CDFeq[b] = new float[binCount];
for(int i = 0; i < binCount; i++) {
CDFeq[b][i] = (float)(i+1)/(float)binCount;
}
}
// Create a normalization CDF.
double[] mean = new double[] {128.0, 128.0, 128.0};
double[] stDev = new double[] {64.0, 64.0, 64.0};
float[][] CDFnorm = new float[numBands][];
for(int b = 0; b < numBands; b++) {
CDFnorm[b] = new float[binCount];
double mu = mean[b];
double twoSigmaSquared = 2.0*stDev[b]*stDev[b];
CDFnorm[b][0] =
(float)Math.exp(mu*mu/twoSigmaSquared);
for(int i = 1; i < binCount; i++) {
double deviation = i  mu;
CDFnorm[b][i] = CDFnorm[b][i1] +
(float)Math.exp(deviation*deviation/twoSigmaSquared);
}
}
for(int b = 0; b < numBands; b++) {
double CDFnormLast = CDFnorm[b][binCount1];
for(int i = 0; i < binCount; i++) {
CDFnorm[b][i] /= CDFnormLast;
}
}
// Create a histogramequalized image.
RenderedOp eq = JAI.create("matchcdf", fmt, CDFeq);
// Create a histogramnormalized image.
RenderedOp nm = JAI.create("matchcdf", fmt, CDFnorm);
7.6 Lookup Table Modification
The lookup table modification provides a nonlinear amplitude transformation. Nonlinear amplitude transformation is useful if you have a nonlinear amplitude response difference between the sensor that captures the image data and the display.
The lookup table modification mechanism allows you to arbitrarily convert between the source image byte, short, or integer pixel value and one or more output values. The output value can be a byte, short, integer, float, or double image pixel.
The input pixel value acts as an address to the lookup table inputs, as shown in Figure 78. Each location in the lookup table stores the desired output value for that particular address.
Figure 78 Lookup Table
The lookup table is first loaded with the necessary data. Table 72 shows a partial listing of an example lookup table. In this example, the input values range from 0 to 255. The output values provide a scaled square root transformation between the input and output, according to the following equation:
Table 72 Example Lookup Table
Input  Output 

0  0 
1  16 
2  23 
3  28 
.  . 
253  254 
254  255 
255  255 
This example provides a nonlinear amplitude transformation between input and output pixel values, in which the smaller input amplitude values are amplified and the larger input values are attenuated. Other types of lookup values can be used to solve nearly any nonlinear amplitude scaling problem.
7.6.1 Creating the Lookup Table
The LookupTableJAI
object represents a single or multibanded table or a color cube of any supported data types. A single or multibanded source image of integer data types is passed through the table and transformed into a single or multibanded destination image of both integral and float or double data types.
The LookupTableJAI
object is used for the ErrorDiffusion
operation, where it describes a color map, and the Lookup
operation, where it describes the lookup table. For the Lookup
operation, the table data may cover only a subrange of the legal range of the input data type. The subrange is selected by means of an offset parameter that is to be subtracted from the input value before indexing into the table array.
The procedures for constructing a lookup table vary slightly, depending on whether the input image is singlebanded or multibanded. For a singleband input image, you construct a single lookup table. For a multiband image, you construct a single lookup table with entries for each band.
7.6.1.1 Creating a Singleband Lookup Table
The singlebanded lookup table contains data for a single channel or image component. To create a lookup table for a singleband input image, use one of the singleband constructors. The constructors take up to three parameters:

A pointer to the data to be stored in the table. The data may be of type
Byte
,Short
,UShort
,Int
,Float
, orDouble
. 
The offset. The offset selects the lookup table subrange. The offset value is subtracted from the input value before indexing into the table array.

A boolean flag that indicates whether Short data is of type Short or UShort.
Listing 73 shows an example of the construction of a singleband byte lookup table.
Listing 73 Example Singleband Lookup Table
byte[] tableData = new byte[0x10000];
for (int i = 0; i < 0x10000; i++) {
tableData[i] = (byte)(i >8);
}
// Create a LookupTableJAI object to be used with the
// "lookup" operator.
LookupTableJAI table = new LookupTableJAI(tableData);
API: org.eclipse.imagen.LookupTableJAI
LookupTableJAI(byte[] data)
LookupTableJAI(byte[] data, int offset)
LookupTableJAI(short[] data, boolean isUShort)
LookupTableJAI(short[] data, int offset, boolean isUShort)
LookupTableJAI(int[] data)
LookupTableJAI(int[] data, int offset)
LookupTableJAI(float[] data)
LookupTableJAI(float[] data, int offset)
LookupTableJAI(double[] data)
LookupTableJAI(double[] data, int offset)
7.6.1.2 Creating a Multiband Lookup Table
The multiband lookup table contains data for more than one channels or image components, such as separate arrays for R, G, and B. To create a lookup table for a multiband input image, use one of the multiband constructors. Like the singleband constructors, the multiband constructors take up to three parameters:

A pointer to the data to be stored in the table. The data may be of type Byte, Short, UShort, Int, Float, or Double.

The offset. The offset selects the lookup table subrange. The offset value is subtracted from the input value before indexing into the table array. The constructors allow you to specify one offset for all of the bands or separate offsets for each band.

A boolean flag that indicates whether Short data is of type Short or UShort.
Listing 74 shows an example of the construction of a multibanded byte lookup table.
Listing 74 Example Multiband Lookup Table
// Create the table data.
byte[][] tableData = new byte[3][0x10000];
for (int i = 0; i < 0x10000; i++) {
tableData[0][i] = (byte)(i >8); // this may be different
tableData[1][i] = (byte)(i >8); // for each band
tableData[2][i] = (byte)(i >8);
}
// Create a LookupTableJAI object to be used with the
// "lookup" operator.
LookupTableJAI table = new LookupTableJAI(tableData);
API: org.eclipse.imagen.LookupTableJAI
LookupTableJAI(byte[][] data)
LookupTableJAI(byte[][] data, int offset)
LookupTableJAI(byte[][] data, int[] offsets)
LookupTableJAI(short[][] data, boolean isUShort)
LookupTableJAI(short[][] data, int offset, boolean isUShort)
LookupTableJAI(short[][] data, int[] offsets, boolean isUShort)
LookupTableJAI(int[][] data)
LookupTableJAI(int[][] data, int offset)
LookupTableJAI(int[][] data, int[] offsets)
LookupTableJAI(float[][] data)
LookupTableJAI(float[][] data, int offset)
LookupTableJAI(float[][] data, int[] offsets)
LookupTableJAI(double[][] data)
LookupTableJAI(double[][] data, int[] offsets)
7.6.1.3 Creating a Colorcube Lookup Table
Dithering operations that use a color cube are considerably faster than those that use a generic lookup table. However, the color cube provides less control over the exact contents of the lookup table.
The ColorCube
class is a subclass of LookupTableJAI
and represents a color cube lookup table. You create a colorcube using one of the ColorCube.createColorCube
methods. Rather than specifying the data to be loaded into the lookup table, you provide an array of dimensions
. The dimensions
parameter specifies the size (or number of levels) of each band of the image.
Although a color cube implies three dimensions, that is not always the case. The color cube has the same number of dimensions
as the image has bands. For example, a monochrome image requires only one dimension
parameter.
The values in the dimensions
parameter are signed. A positive value indicates that the corresponding color ramp increases. A negative value indicates that the ramp decreases.
ImageN provides two predefined color cubes, which can be used for the ordered dither operation (see Section 6.6.1, "Ordered Dither"):
ColorCube  Description 

BYTE_496  A ColorCube with dimensions 4:9:6, useful for dithering RGB images into 216 colors. The offset of this ColorCube is 38. This color cube dithers blue values in the source image to one of 4 blue levels, green values to one of 9 green levels, and red values to one of 6 red levels. This is the default color cube for the ordered dither operation. 
BYTE_855  A ColorCube with dimensions 8:5:5, useful for dithering YC~b~C~r~ images into 200 colors. The offset of this ColorCube is 54. This color cube dithers blue values in the source image to one of 8 blue levels, green values to one of 5 green levels, and red values to one of 5 red levels. 
These color cubes are specified by the colorMap
parameter that is required by the OrderedDither
operation.
API: org.eclipse.imagen.ColorCube
static ColorCube createColorCube(int dataType, int offset, int[] dimensions)
static ColorCube createColorCube(int dataType, int[] dimensions)
static ColorCube createColorCubeByte(int[] dimensions)
static ColorCube createColorCubeByte(int offset, int[] dimensions)
static ColorCube createColorCubeShort(int[] dimensions)
static ColorCube createColorCubeShort(int offset, int[] dimensions)
static ColorCube createColorCubeUShort(int[] dimensions)
static ColorCube createColorCubeUShort(int offset, int[] dimensions)
static ColorCube createColorCubeInt(int[] dimensions)
static ColorCube createColorCubeInt(int offset, int[] dimensions)
static ColorCube createColorCubeFloat(int[] dimensions)
static ColorCube createColorCubeFloat(int offset, int[] dimensions)
static ColorCube createColorCubeDouble(int[] dimensions)
static ColorCube createColorCubeDouble(int offset, int[] dimensions)
7.6.2 Performing the Lookup
The lookup
operation performs a general table lookup on a rendered or renderable image. The destination image is obtained by passing the source image through the lookup table. The source image may be single or multibanded of data types byte
, ushort
, short
, or int
. The lookup table may be single or multibanded of any ImageNsupported data types.
The destination image must have the same data type as the lookup table, and its number of bands is determined based on the number of bands of the source and the table. If the source is singlebanded, the destination has the same number of bands as the lookup table; otherwise, the destination has the same number of bands as the source.
If either the source or the table is singlebanded and the other one is multibanded, the single band is applied to every band of the multibanded object. If both are multibanded, their corresponding bands are matched up.
The table may have a set of offset values, one for each band. This value is subtracted from the source pixel values before indexing into the table data array.
It is the user's responsibility to make certain the lookup table supplied is suitable for the source image. Specifically, the table data must cover the entire range of the source data. Otherwise, the result of this operation is undefined.
By the nature of this operation, the destination may have a different number of bands and/or data type from the source. The SampleModel
of the destination is created in accordance with the actual lookup table used in a specific case.
There are three specific cases of table lookup that determine the pixel values of the destination image:

If the source image is singlebanded and the lookup table is single or multibanded, the destination image has the same number of bands as the lookup table:
for (int h = 0; h < dstHeight; h++) { for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstNumBands; b++) { dst[h][w][b] = table[b][src[h][w][0]  offsets[b]] } } }

If the source image is multibanded and the lookup table is singlebanded, the destination image has the same number of bands as the source image:
for (int h = 0; h < dstHeight; h++) { for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstNumBands; b++) { dst[h][w][b] = table[0][src[h][w][b]  offsets[0]] } } }

If the source image is multibanded and the lookup table is multibanded, with the same number of bands as the source image, the destination image will have the same number of bands as the source image:
for (int h = 0; h < dstHeight; h++) { for (int w = 0; w < dstWidth; w++) { for (int b = 0; b < dstNumBands; b++) { dst[h][w][b] = table[b][src[h][w][b]  offsets[b]] } } }
The lookup
operation takes one rendered or renderable source image and one parameter:
Parameter Type Description ——————— ————————— ———————————————————————– table LookupTableJAI The lookup table through which the source image is passed.
:
See Section 7.6.1, "Creating the Lookup Table" for more information.
For a complete example of the Lookup
operation, see Listing A1 on page 417.
7.6.3 Other Lookup Table Operations
7.6.3.1 Reading the Table Data
Several methods are available to read the current contents of the lookup table. The choice of method depends on the data format: byte, short, integer, floatingpoint, or double floatingpoint.
API: org.eclipse.imagen.LookupTableJAI
java.awt.image.DataBuffer getData()
byte[][] getByteData()
byte[] getByteData(int band)
short[][] getShortData()
short[] getShortData(int band)
int[][] getIntData()
int[] getIntData(int band)
float[][] getFloatData()
float[] getFloatData(int band)
double[][] getDoubleData()
double[] getDoubleData(int band)
7.6.3.2 Reading the Table Offsets
There are three methods for reading the offset values within the current lookup table.
API: org.eclipse.imagen.LookupTableJAI
int[] getOffsets()
int getOffset()
int getOffset(int band)
7.6.3.3 Reading the Number of Bands
A single method is used to read the number of bands in the lookup table.
API: org.eclipse.imagen.LookupTableJAI
int getNumBands()
7.6.3.4 Reading the Number of Entries Per Band
A single method is used to read the number of entries per band in the lookup table.
API: org.eclipse.imagen.LookupTableJAI
int getNumEntries()
7.6.3.5 Reading the Data Type
A single method is used to read the data type of the lookup table.
API: org.eclipse.imagen.LookupTableJAI
int getDataType()
7.6.3.6 Reading the Destination Bands and SampleModel
API: org.eclipse.imagen.LookupTableJAI
int getDestNumBands(int sourceNumBands)
java.awt.image.SampleModel getDestSampleModel(java.awt.image.SampleModel srcSampleModel)
java.awt.image.SampleModel getDestSampleModel(java.awt.image.SampleModel srcSampleModel, int width, int height)
7.7 Convolution Filtering
Convolution filtering is often used to reduce the effects of noise in images or to sharpen the detail in blurred images. Convolution filtering is a form of spatial filtering that computes each output sample by multiplying elements of a kernel with the samples surrounding a particular source sample.
Convolution filtering operates on a group of input pixels surrounding a center pixel. The adjoining pixels provide important information about brightness trends in the area of the pixel being processed.
Convolution filtering moves across the source image, pixel by pixel, placing resulting pixels into the destination image. The resulting brightness of each source pixel depends on the group of pixels surrounding the source pixel. Using the brightness information of the source pixel's neighbors, the convolution process calculates the spatial frequency activity in the area, making it possible to filter the brightness based on the spatial frequency of the area.
Convolution filtering uses a convolve kernel, containing an array of convolution coefficient values, called key elements, as shown in Figure 79. The array is not restricted to any particular size, and does not even have to be square. The kernel can be 1 x 1, 3 x 3, 5 x 5, M
x N,
and so on. A larger kernel size affords a more precise filtering operation by increasing the number of neighboring pixels used in the calculation. However, the kernel cannot be bigger in any dimension than the image data. Also, the larger the kernel, the more computations that are required to be performed. For example, given a 640 x 480 image and a 3 x 3 kernel, the convolve operation requires over five million total multiplications and additions.
The convolution filtering operation computes each output sample by multiplying the key elements of the kernel with the samples surrounding a particular source pixel. For each destination pixel, the kernel is rotated 180 degrees and its key element is placed over the source pixel corresponding with the destination pixel. The key elements are multiplied with the source pixels under them, and the resulting products are summed together to produce the destination sample value.
The selection of the weights for the key elements determines the nature of the filtering action, such as highpass or lowpass. If the values of the key elements are the reciprocal of the number of key elements in the kernel (for example, 1/9 for a 3 x 3 kernel), the result is a conventional lowpass averaging process. If the weights are altered, certain pixels in the kernel will have an increased or decreased influence in the average. Figure 710 shows three example convolve filters, lowpass, highpass, and Laplacian.
Figure 79 Convolve Kernel
Figure 710 Convolve Filter Samples
The lowpass filter, also known as a box filter, attenuates the highspatial frequency components of an image and has little affect on the lowfrequency components. The effect of passing an image through a lowpass filter is a slight blurring. This is caused by attenuating the sharp brightness transitions between edges and makes the image appear to have less detail. See also Section 7.7.2, "Box Filter."
The highpass filter has the opposite effect of the lowpass filter, accentuating the highfrequency components and offering little affect on the lowfrequency components. The effect of passing an image through a highpass filter is a sharper image with increased detail in the areas of brightness transition.
The Laplacian filter is another image detail sharpening filter that works well for noisefree images. This filter subtracts the brightness values of the four neighboring pixels from the central pixel. The result of applying this filter is to reduce the gray level to zero.
7.7.1 Performing the Convolve Operation
The following example code shows a convolve
operation on a single sample dst[x][y]
, which assumes that the kernel is of size M
x N
and has already been rotated through 180 degrees. The kernel's key element is located at position (xKey
, yKey
).
dst[x][y] = 0;
for (int i = xOrigin; i < xOrigin + width; i++) {
for (int j = yOrigin; j < yOrigin + height; j++) {
dst[x][y] += src[x + i][y + j]*kernel[xOrigin + i][yOrigin + j];
}
}
Convolution, or any neighborhood operation, leaves a band of pixels around the edges undefined. For example, for a 3 x 3 kernel, only four kernel elements and four source pixels contribute to the destination pixel located at (0,0). Such pixels are not included in the destination image. A border extension may be added via the BorderExtender
class. The type of border extension can be specified as a RenderingHint
to the JAI.create
method. If no border extension type is provided, a default extension of BorderExtender.BORDER_COPY
will be used to perform the extension. See Section 3.7.3, "Rendering Hints."
The convolve
operation takes one rendered source image and one parameter:
Parameter Type Description ——————— ——————— ——————————————————————————————————————— kernel KernelJAI The convolution kernel. See Section 6.9, "Constructing a Kernel."\
:
The default kernel
is null
.
Listing 75 shows a code sample for a Convolve
operation.
Listing 75 Example Convolve Operation
// Create the kernel.
kernel = new KernelJAI
float[] = { 0.0F, 1.0F, 0.0F,
1.0F, 5.0F, 1.0F,
0.0F, 1.0F, 0.0F };
// Create the convolve operation.
im1 = JAI.create("convolve", im, kernel);
7.7.2 Box Filter
The BoxFilter
operation is a special case of convolve operation in which each source pixel contributes the same weight to the destination pixel. The box filter operation determines the intensity of a pixel in an image by averaging the source pixels within a rectangular area around the pixel. The pixel values of the destination image are defined by the following pseudocode:
int count = width * height; // # of pixels in the box
for (int b = 0; b < numBands; b++) {
int total = 0;
for (int j = yKey; j < yKey + height; j++) {
for (int i = xKey; i < xKey + width; i++) {
total += src[x+i][y+j][b];
}
}
dst[x][y][b] = (total + count/2) / count; // round
}
The BoxFilter
operation uses a lowpass filter that passes (leaves untouched) the low spatial frequency components of the image and attenuates the highfrequency components. In an area of the image that has constant brightness, the brightness values are passed untouched. When the filter passes over an area of sharp black to white transitions, the brightness range is greatly attenuated.
Convolution, like any neighborhood operation, leaves a band of pixels around the edges undefined. For example, for a 3 x 3 kernel, only four kernel elements and four source pixels contribute to the convolution pixel at the corners of the source image. Pixels that do not allow the full kernel to be applied to the source are not included in the destination image. A Border
operation (see Section 7.2, "Adding Borders to Images") may be used to add an appropriate border to the source image to avoid shrinkage of the image boundaries.
The kernel may not be bigger in any dimension than the image data.
The BoxFilter
operation takes one rendered source image and four parameters:
Parameters  Type  Description 

width  Integer  The width of the box. 
height  Integer  The height of the box. 
xKey  Integer  The x position of the key element. 
yKey  Integer  The y position of the key element. 
The width
parameter is required. The remaining parameters may be null
and, if not supplied, default to the following values:
Listing 76 shows a code sample for a BoxFilter
operation.
Listing 76 Example BoxFilter Operation
// Read the arguments.
String fileName = args.length 0 ? args[0] : DEFAULT_FILE;
int width = args.length 1 ?
Integer.decode(args[1]).intValue() : DEFAULT_SIZE;
int height = args.length 2 ?
Integer.decode(args[2]).intValue() : width;
new BoxFilterExample(fileName, width, height);
}
public BoxFilterExample(String fileName, int width, int height)
// Load the image.
RenderedOp src = JAI.create("fileload", fileName);
// Create the BoxFilter operation.
RenderedOp dst = JAI.create("boxfilter", src,
width, height,
width/2, height/2);
7.8 Median Filtering
A median filter is used to remove impulse noise spikes from an image and thus smoothing the image. Impulse noise spikes appear as bright or dark pixels randomly distributed throughout the image. Noise spikes are normally significantly brighter or darker than their neighboring pixels and can easily be found by comparing the median value of a group of input pixels.
The median filter is a neighborhoodbased ranking filter in which the pixels in the neighborhood are ranked in the order of their levels. The median value of the group is then stored in the output pixel. The resulting image is then free of pixel brightnesses that are at the extremes in each input group of pixels.
The noisereducing effect that the median filter has on an image depends on two related things: the spatial extent of the neighborhood (the mask) and the number of pixels involved in the computation. The MedianFilter
operation supports three different mask shapes, a square, a plus, and an Xshape, as shown in Figure 711.
Figure 711 Median Filter Masks
The MedianFilter
operation may also be used to compute the separable median of a 3 x 3 or 5 x 5 region of pixels. The separable median is defined as the median of the medians of each row. For example, if the pixel values in a 3 x 3 window are as follows:
the overall (nonseparable) median value is 5, while the separable median is equal to the median of the three row medians: median(1, 2, 3) = 2, median(5, 6, 7) = 6, and median(4, 8, 9) = 8, yielding an overall median of 6. The separable median may be obtained by specifying a mask of type MEDIAN_MASK_SQUARE_SEPARABLE
.
The MedianFilter
operation takes one rendered source image and two parameters:
Parameters  Type  Default  Description 

maskShape  Integer  MASK_SQUARE  The shape of the mask to be used for Median Filtering 
maskSize  Integer  3  The size (width and height) of the mask to be used in Median Filtering. 
The maskShape
parameter is one of the following:
maskShape  Description 

MEDIAN_MASK_SQUARE  A squareshaped mask. The default. 
MEDIAN_MASK_PLUS  A plusshaped mask. 
MEDIAN_MASK_X  An Xshaped mask. 
MEDIAN_MASK_SQUARE_SEPARABLE  A separable square mask, used for the separable median operation. 
The maskSize
parameter must be 1 (1 x 1) or greater. The default value, if one is not provided, is 3 (3 x 3). For large masks, the noise reduction effect of more pixels used in the computation of the median value reaches a point of diminishing returns. Typical mask sizes are 3 x 3 and 5 x 5.
7.9 Frequency Domain Processing
Images contain spatial details that are seen as brightness transitions, cycling from dark to light and back to dark. The rate at which the transitions occur in an image represent the image's spatial frequency.
An image's spatial frequency can be measured horizontally, vertically, or at any diagonal in between. An image contains many spatial frequencies that, when combined in the correct magnitude and phase, form the complex details of the image.
A frequency transform decomposes an image from its spatial domain form of brightness into a frequency domain form of fundamental frequency components. Each frequency component contains a magnitude and phase value. An inverse frequency transform converts an image from its frequency form back to its spatial form.
7.9.1 Fourier Transform
ImageN supports the most common type of frequency transform, the discrete Fourier transform and its inverse, the inverse discrete Fourier transform. The discrete Fourier transform of an image is a twodimensional process. The result of the transform is a twodimensional array of values, each having two parts: real and imaginary. Each value represents a distinct spatial frequency component. The frequencytransform image has as many values as there are pixels in the source image.
The real portion of the values can be displayed as an image, visually showing the frequency components of the source image. The result is in "wrap around" order, with the zerofrequency point (also known as "DC" for direct current) at the upper left corner and the high frequencies at the center.
7.9.1.1 Discrete Fourier Transform
The DFT
(discrete Fourier transform) operation computes the discrete Fourier transform of an image. A negative exponential is used as the basis function for the transform. The operation supports realtocomplex, complextocomplex, and complextoreal transforms. A complex image must have an even number of bands, with the even bands (0, 2, etc.) representing the real parts and the odd bands (1, 3, etc.) the imaginary parts of each complex pixel.
If an underlying fast Fourier transform (FFT) implementation is used that requires that the image dimensions be powers of 2, the width and height may each be increased to the power of 2 greater than or equal to the original width and height, respectively.
The dft
operation takes one rendered or renderable source image and two parameters.
Parameters  Type  Description 

scalingType  Integer  The type of scaling to perform. One of DFTDescriptor.SCALING_NONE, DFTDescriptor.SCALING_UNITARY, or DFTDescriptor.SCALING_DIMENSIONS. 
dataNature  Integer  The nature of the data. One of DFTDescriptor.REAL_TO_COMPLEX, DFTDescriptor.COMPLEX_TO_COMPLEX, or DFTDescriptor.COMPLEX_TO_REAL. 
The default parameters for this operation are SCALING_NONE
and REAL_TO_COMPLEX
.
The scalingType
parameter defines how the image dimensions may be scaled, as follows:
scalingType  Description 

DFTDescriptor.SCALING_NONE  The transform is not to be scaled (the default). 
DFTDescriptor.SCALING_UNITARY  The transform is to be scaled by the square root of the product of its dimensions. 
DFTDescriptor.SCALING_DIMENSIONS  The transform is to be scaled by the product of its dimensions. 
The dataNature
parameter specifies the nature of the source and destination data, as follows.
dataNature  Description 

DFTDescriptor.REAL_TO_COMPLEX  The source data are real and the destination data complex. 
DFTDescriptor.COMPLEX_TO_COMPLEX  The source and destination data are both complex. 
DFTDescriptor.COMPLEX_TO_REAL  The source data are complex and the destination data real. 
If the source data are complex, the number of bands in the source image must be a multiple of 2. The number of bands in the destination must match that which would be expected given the number of bands in the source image and the specified nature of the source and destination data. If the source image is real, the number of bands in the destination will be twice that in the source. If the destination image is real, the number of bands in the destination will be half that in the source. Otherwise the number of bands in the source and destination must be equal.
The DFT
operation defines a PropertyGenerator
that sets the COMPLEX
property of the image to FALSE
if the dataNature
parameter is COMPLEX_TO_REAL
and to TRUE
if the dataNature
parameter is REAL_TO_COMPLEX
or COMPLEX_TO_COMPLEX
. The value of this property may be retrieved by calling the getProperty() method with COMPLEX
as the property name.
Listing 77 shows a code sample for a DFT
operation.
Listing 77 Example DFT Operation
// Create the ParameterBlock.
ParameterBlock pb = new ParameterBlock();
pb.addSource(src)
pb.add(DFTDescriptor.SCALING_NONE);
pb.add(DFTDescriptor.REAL_TO_COMPLEX);
// Create the DFT operation.
PlanarImage dft = (PlanarImage)JAI.create("dft", pb, null);
// Get the DFT image information.
int width = dft.getWidth();
int height = dft.getHeight();
int numBands = dft.getSampleModel().getNumBands();
int dataType = dft.getSampleModel().getDataType();
// Calculate the cutoff "frequencies" from the threshold.
threshold /= 200.0F;
int minX = (int)(width*threshold);
int maxX = width  1  minX;
int minY = (int)(height*threshold);
int maxY = height  1  minY;
// Retrieve the DFT data.
Raster dftData = dft.getData();
double[] real =
dftData.getSamples(0, 0, width, height, 0, (double[])null);
double[] imag =
dftData.getSamples(0, 0, width, height, 1, (double[])null);
double[] HR = new double[real.length];
double[] HI = new double[imag.length];
double[] LR = new double[real.length];
double[] LI = new double[imag.length];
7.9.1.2 Inverse Discrete Fourier Transform
The IDFT
(inverse discrete Fourier transform) operation computes the inverse discrete Fourier transform of an image. A positive exponential is used as the basis function for the transform. The operation supports realtocomplex, complextocomplex, and complextoreal transforms. A complex image must have an even number of bands, with the even bands (0, 2, etc.) representing the real parts and the odd bands (1, 3, etc.) the imaginary parts of each complex pixel.
If an underlying fast Fourier transform (FFT) implementation is used that requires that the image dimensions be powers of 2, the width and height may each be increased to the power of 2 greater than or equal to the original width and height, respectively.
The IDFT
operation takes one rendered or renderable source image and two parameters.
Parameters  Type  Description 

scalingType  Integer  The type of scaling to perform. One of DFTDescriptor.SCALING_NONE, DFTDescriptor.SCALING_UNITARY, or DFTDescriptor.SCALING_DIMENSIONS. 
dataNature  Integer  The nature of the data. One of DFTDescriptor.REAL_TO_COMPLEX, DFTDescriptor.COMPLEX_TO_COMPLEX, or DFTDescriptor.COMPLEX_TO_REAL. 
The default parameters for this operation are SCALING_DIMENSIONS
and COMPLEX_TO_REAL
.
The scalingType
parameter defines how the image dimensions may be scaled, as follows:
scalingType  Description 

DFTDescriptor.SCALING_NONE  The transform is not to be scaled. 
DFTDescriptor.SCALING_UNITARY  The transform is to be scaled by the square root of the product of its dimensions. 
DFTDescriptor.SCALING_DIMENSIONS  The transform is to be scaled by the product of its dimensions (the default). 
The dataNature
parameter specifies the nature of the source and destination data, as follows.
dataNature  Description 

DFTDescriptor.REAL_TO_COMPLEX  The source data are real and the destination data complex. 
DFTDescriptor.COMPLEX_TO_COMPLEX  The source and destination data are both complex. 
DFTDescriptor.COMPLEX_TO_REAL  The source data are complex and the destination data real. 
If the source data are complex, the number of bands in the source image must be a multiple of 2. The number of bands in the destination must match that which would be expected given the number of bands in the source image and the specified nature of the source and destination data. If the source image is real, the number of bands in the destination will be twice that in the source. If the destination image is real, the number of bands in the destination will be half that in the source. Otherwise the number of bands in the source and destination must be equal.
The IDFT
operation defines a PropertyGenerator
that sets the COMPLEX
property of the image to FALSE
if the dataNature
parameter is COMPLEX_TO_REAL
and to TRUE
if the dataNature
parameter is REAL_TO_COMPLEX
or COMPLEX_TO_COMPLEX
. The value of this property may be retrieved by calling the getProperty() method with COMPLEX
as the property name.
7.9.2 Cosine Transform
The discrete cosine transform (DCT) is similar to the discrete Fourier transform (see Section 7.9.1.1, "Discrete Fourier Transform"). However, the DCT is better at compactly representing very small images. Like the discrete Fourier transform (DFT), the DCT also has an inverse operation, the inverse discrete cosine transform (IDCT).
7.9.2.1 Discrete Cosine Transform (DCT)
The DCT
operation computes the even discrete cosine transform of an image. Each band of the destination image is derived by performing a twodimensional DCT on the corresponding band of the source image.
The DCT
operation takes one rendered or renderable source image and no parameters.
Listing 78 shows a code sample for a DCT operation.
Listing 78 Example DCT Operation
// Load the source image.
RenderedImage src = (RenderedImage)JAI.create("fileload",
fileName);
// Calculate a DCT image from the source image.
ParameterBlock pb = (new ParameterBlock()).addSource(src);
PlanarImage dct = JAI.create("dct", pb, null);
// Get the DCT image data.
int width = dct.getWidth();
int height = dct.getHeight();
int numBands = dct.getSampleModel().getNumBands();
int dataType = dct.getSampleModel().getDataType();
double[] dctData =
dct.getData().getPixels(0, 0, width, height,
(double[])null);
double[] pixels = new double[dctData.length];
7.9.2.2 Inverse Discrete Cosine Transform (IDCT)
The IDCT
operation computes the inverse even discrete cosine transform of an image. Each band of the destination image is derived by performing a twodimensional inverse DCT on the corresponding band of the source image.
The IDCT
operation takes one rendered or renderable source image and no parameters.
Listing 79 shows a code sample for an operation that first takes the discrete cosine transform of an image, then computes the inverse discrete cosine transform.
Listing 79 Example IDCT Operation
// Calculate a DCT image from the source image.
System.out.println("Creating DCT of source image ...");
ParameterBlock pb = (new ParameterBlock()).addSource(src);
PlanarImage dct = JAI.create("dct", pb, null);
// Calculate an IDCT image from the DCT image.
System.out.println("Creating IDCT of DCT of source image ...");
pb = (new ParameterBlock()).addSource(dct);
PlanarImage idct = JAI.create("idct", pb, null);
// Create display image for inverse DCT of DCT of source image.
System.out.println("Creating display image for IDCT of DCT");
pixels = idct.getData().getPixels(0, 0, width, height,
(double[])pixels);
BufferedImage bi = createBI(colorImage, width, height, pixels);
7.9.3 Magnitude Enhancement
The magnitude
operation computes the magnitude of each pixel of a complex image. The source image must have an even number of bands, with the even bands (0, 2, etc.) representing the real parts and the odd bands (1, 3, etc.) the imaginary parts of each complex pixel. The destination image has at most half the number of bands of the source image with each sample in a pixel representing the magnitude of the corresponding complex source sample.
The magnitude values of the destination image are defined by the following pseudocode:
dstPixel[x][y][b] = sqrt(src[x][y][2b]2 + src[x][y][2b + 1]2)
: where the number of bands b varies from zero to one less than the number of bands in the destination image.
For integral image data types, the result is rounded and clamped as needed.
The magnitude
operation takes one rendered or renderable source image containing complex data and no parameters.
Listing 710 shows a code sample for a magnitude
operation.
Listing 710 Example Magnitude Operation
// Calculate a DFT image from the source image.
pb = new ParameterBlock();
pb.addSource(src).add(DFTDescriptor.SCALING_NONE);
PlanarImage dft = JAI.create("dft", pb, null);
// Create the ParameterBlock specifying the source image.
pb = new ParameterBlock();
pb.addSource(dft);
// Calculate the magnitude.
PlanarImage magnitude = JAI.create("magnitude", pb, null);
7.9.4 Magnitudesquared Enhancement
The MagnitudeSquared
operation computes the squared magnitude of each pixel of a complex image. The source image must have an even number of bands, with the even bands (0, 2, etc.) representing the real parts and the odd bands (1, 3, etc.) the imaginary parts of each complex pixel. The destination image has at most half the number of bands of the source image with each sample in a pixel representing the magnitude of the corresponding complex source sample.
The squared magnitude values of the destination image are defined by the following pseudocode:
dstPixel[x][y][b] = src[x][y][2b]2 + src[x][y][2b + 1]2
Where the number of bands b varies from zero to one less than the number of bands in the destination image.
For integral image data types, the result is rounded and clamped as needed.
The MagnitudeSquared
operation takes one rendered or renderable source image containing complex data and no parameters.
7.9.5 Phase Enhancement
The Phase
operation computes the phase angle of each pixel of a complex image. The source image must have an even number of bands, with the even bands (0, 2, etc.) representing the real parts and the odd bands (1, 3, etc.) the imaginary parts of each complex pixel. The destination image has at most half the number of bands of the source image with each sample in a pixel representing the phase angle of the corresponding complex source sample.
The angle values of the destination image are defined by the following pseudocode:
dst[x][y][b] = atan2(src[x][y][2b + 1], src[x][y][2b])
: where the number of bands b varies from zero to one less than the number of bands in the destination image.
For integral image data types, the result is rounded and scaled so the "natural" arctangent range from [𝜋,𝜋) is remapped into the range [0, MAXVALUE). The result for floating point image data types is the value returned by the atan2()
method.
The phase
operation takes one rendered or renderable source image containing complex data and no parameters.
7.9.6 Complex Conjugate
The Conjugate
operation computes the complex conjugate of a complex image. The operation negates the imaginary components of a rendered or renderable source image containing complex data. The source image must contain an even number of bands with the evenindexed bands (0, 2, etc.) representing the real and the oddindexed bands (1, 3, etc.) the imaginary parts of each pixel. The destination image similarly contains an even number of bands with the same interpretation and with contents defined by:
dst[x][y][2*k] = src[x][y][2*k];
dst[x][y][2*k+1] = src[x][y][2*k+1];
: where the index k varies from zero to one less than the number of complex components in the destination image.
The Conjugate
operation takes one rendered or renderable source image containing complex data and no parameters.
7.9.7 Periodic Shift
The PeriodicShift
operation computes the periodic translation of an image. The destination image of the PeriodicShift
operation is the infinite periodic extension of the source image with horizontal and vertical periods equal to the image width and height, respectively, shifted by a specified amount along each axis and clipped to the bounds of the source image. Thus for each band b the destination image sample at location (x,y) is defined by:
if(x < width  shiftX) {
if(y < height  shiftY) {
dst[x][y][b] = src[x + shiftX][y + shiftY][b];
} else {
dst[x][y][b] = src[x + shiftX][y  height + shiftY][b];
}
} else {
if(y < height  shiftY) {
dst[x][y][b] = src[x  width + shiftX][y + shiftY][b];
} else {
dst[x][y][b] = src[x  width + shiftX][y  height +
shiftY][b];
}
}
Where shiftX
and shiftY
denote the translation factors along the x and y axes, respectively.
The PeriodicShift
operation takes one rendered or renderable source image and two parameters.
Parameter  Type  Description 

shiftX  Integer  The displacement in the x direction. 
shiftY  Integer  The displacement in the y direction. 
7.9.8 Polar to Complex
The PolarToComplex
operation computes a complex image from a magnitude and a phase image. The operation creates an image with complexvalued pixels from two images, the respective pixel values of which represent the magnitude (modulus) and phase of the corresponding complex pixel in the destination image.
The source images should have the same number of bands. The first source image contains the magnitude values and the second source image the phase values. The destination will have twice as many bands with the evenindexed bands (0, 2, etc.) representing the real and the oddindexed bands (1, 3, etc.) the imaginary parts of each pixel.
The pixel values of the destination image are defined for a given complex sample by the following pseudocode:
dst[x][y][2*b] = src0[x][y][b]*Math.cos(src1[x][y][b])
dst[x][y][2*b+1] = src0[x][y][b]*Math.sin(src1[x][y][b])
: where the index b varies from zero to one less than the number of bands in the source images.
For phase images with integral data type, it is assumed that the actual phase angle is scaled from the range [PI
, PI
] to the range [0, MAX_VALUE
] where MAX_VALUE
is the maximum value of the data type in question.
The PolarToComplex
operation takes two rendered or renderable source images and no parameters.
7.9.9 Images Based on a Functional Description
The ImageFunction
operation generates an image from a functional description. This operation permits the creation of images on the basis of a functional specification, which is provided by an object that is an instance of a class that implements the org.eclipse.imagen.ImageFunction
interface. In other words, to use this operation, a class containing the functional information must be created and this class must implement the ImageFunction
interface.
The ImageFunction
interface merely defines the minimal set of methods required to represent such a function. The actual implementation of a class implementing this interface is left to the programmer.
For example, if the function you wanted to generate was the negative exponential
exp(x  y)
The org.eclipse.imagen.ImageFunction
implementation would return the following values:

isComplex()
would return false 
getNumElements()
would return 1 
float[] real = new real[width*height];getElements(x, y, width, height, real, null);
and the implementation would initialize the array real
such that
real[j*width + i] = exp(x + i  y + j)
or, equivalently
real[k] = exp(x + (k % width)]  y + (k / width))
: where 0 ≤ k < width*height.
The (x,y) coordinates passed to the ImageFunction.getElements()
methods are derived by applying an optional translation and scaling to the image x and y coordinates. The image x and y coordinates as usual depend on the values of the minimum x and y coordinates of the image, which need not be zero.
Specifically, the function coordinates passed to getElements()
are calculated from the image coordinates as:
functionX = xScale*imageX + xTrans;
functionY = yScale*imageY + yTrans;
The number of bands in the destination image will be equal to the value returned by the ImageFunction.getNumElements()
method unless the ImageFunction.isComplex()
method returns true
, in which case it will be twice that. The data type of the destination image is determined by the SampleModel
specified by an ImageLayout
object provided via a hint. If no layout hint is provided, the data type will default to singleprecision floating point.
The double precision floating point form of the getElements()
method will be invoked if and only if the data type is specified to be double
. For all other data types the single precision form of getElements()
will be invoked and the destination sample values will be clamped to the data type of the image.
The ImageFunction
operation takes seven parameters.
Parameter  Type  Description 

function  ImageFunction  The functional description. 
width  Integer  The image width. 
height  Integer  The image height. 
xScale  Float  The x scale factor. 
yScale  Float  The y scale factor. 
xTrans  Float  The x translation. 
yTrans  Float  The y translation. 
The image width and height are provided explicitly as parameters. These values override the width and height specified by an ImageLayout
if such is provided.
API: org.eclipse.imagen.ImageFunction
boolean isComplex();
int getNumElements();
void getElements(float startX, float startY, float deltaX, float deltaY, int countX, int countY, int element, float[] real, float[] imag);
void getElements(double startX, double startY, double deltaX, double deltaY, int countX, int countY, int element, double[] real, double[] imag);
7.10 Singleimage Pixel Point Processing
Pixel point operations are the most basic, yet necessary image processing operations. The pixel point operations are primarily contrast enhancement operations that alter the gray levels of an image's pixels. Onebyone, the gray level of each pixel in the source image is modified to a new value, usually by a mathematical relationship.
ImageN supports the following singleimage pixel point operations:

Pixel inverting (
Invert
) 
Logarithmic enhancement (
Log
)
7.10.1 Pixel Inverting
The Invert
operation inverts the pixel values of an image. For source images with signed data types, the pixel values of the destination image are defined by the following pseudocode:
dst[x][y][b] = src[x][y][b]
For unsigned data types, the destination values are defined by the following pseudocode:
dst[x][y][b] = MAX_VALUE  src[x][y][b]
Where MAX_VALUE
is the maximum value supported by the system of the data type of the source pixel.
The Invert
operation takes one rendered or renderable source image and no parameters.
Figure 712 shows a simple example of an Invert
operation.
Figure 712 Pixel Inverting
7.10.2 Logarithmic Enhancement
Occasionally, it is desirable to quantize an image on a logarithmic scale rather than a linear scale. The human eye has a logarithmic intensity response but some images are digitized by equipment that quantizes the samples on a linear scale. To make the image better for use by a human observer, these images may be made to have a logarithmic response by the Log
operation.
The Log
operation takes the logarithm of the pixel values of the source image. The pixel values of the destination image are defined by the following pseudocode:
dst[x][y][b] = java.lang.Math.log(src[x][y][b])
For integral image data types, the result is rounded and clamped as needed. For all integral data types, the log of 0 is set to 0. For signed integral data types (short
and int
), the log of a negative pixel value is set to 1. For all floating point data types (float
and double
), the log of 0 is set to Infinity
, and the log of a negative pixel value is set to NaN
.
The Log
operation takes one rendered or renderable source image and no parameters.
Listing 711 shows a code sample for a Log
operation.
Listing 711 Example Log Operation
// Create the ParameterBlock specifying the source image.
pb = new ParameterBlock();
pb.addSource(image);
// Create the Log operation.
RenderedImage dst = JAI.create("log", pb);
7.11 Dual Image Pixel Point Processing
The previous section described pixel point operations for single images. This section deals with pixel point processing on two images, also known as dualimage point processing. Dualimage point processing maps two pixel brightnesses, one from each image, to an output image.
ImageN supports the following dualimage pixel point operations:

Overlay images (
Overlay
operation) 
Image compositing (
Composite
operation)
7.11.1 Overlay Images
The Overlay
operation takes two rendered or renderable source images, and overlays the second source image on top of the first source image. Usually, the images are identical scenes, but may have been acquired at different times through different spectral filters.
The two source images must have the same data type and number of bands. However, their SampleModel
types may differ. The destination image will always have the same bounding rectangle as the first source image, that is, the image on the bottom, and the same data type and number of bands as the two source images. If the two source images don't intersect, the destination will be the same as the first source.
The Overlay
operation is defined by the following pseudocode:
if (srcs[1] contains the point (x, y)) {
dst[x][y][b] = srcs[1][x][y][b];
} else {
dst[x][y][b] = srcs[0][x][y][b];
}
The Overlay
operation takes two rendered or renderable source images and no parameters.
7.11.2 Image Compositing
The Composite
operation merges unrelated objects from two images. The result is a new image that didn't exist before. The Composite
operation combines two images based on their alpha values at each pixel. This is done on a perband basis, and the source images are expected to have the same number of bands and the same data type. The destination image has the same data type as the two sources, but one extra band than the source images, which represents the result alpha channel.
The destination pixel values may be viewed as representing a fractional pixel coverage or transparency factor. Specifically, the Composite
operation implements the PorterDuff "over" rule ^{1}, in which the output color of a pixel with source value and alpha tuples (A, a) and (B, b) is given by:
 ^{1} See Computer Graphics, July 1984 pp. 253259.

a*A + (1  a)*(b*B)
 The output alpha value is given by:

a + (1  a)*b
 For premultiplied sources tuples (a*A, a) and (b*B, b),
 the premultiplied output value is simply:

(a*A) + (1  a)*(b*B)
The color channels of the two source images are supplied via source1
and source2
. The two sources must either both be premultiplied by alpha or not. Alpha channel should not be included in source1
and source2
.
The Composite
operation takes two rendered or renderable source images and four parameters:
Parameter  Type  Description 

source1Alpha  PlanarImage  An alpha image to override the alpha for the first source. 
source2Alpha  PlanarImage  An alpha image to override the alpha for the second source. 
alphaPremultiplied  Boolean  True if alpha has been premultiplied to both sources and the destination. 
destAlpha  Integer  Indicates if the destination image should include an extra alpha channel, and if so, whether it should be the first or last band. One of: CompositeDescriptor.DESTINATION_ALPHA_FIRST CompositeDescriptor.DESTINATION_ALPHA_LAST CompositeDescriptor.NO_DESTINATION_ALPHA 
The alpha channel of the first source images must be supplied via the source1Alpha
parameter. This parameter may not be null. The alpha channel of the second source image may be supplied via the source2Alpha
parameter. This parameter may be null, in which case the second source is considered completely opaque. The alpha images should be singlebanded, and have the same data type as the source image.
The alphaPremultiplied
parameter indicates whether or not the supplied alpha image is premultiplied to both the source images.
The destination image is the combination of the two source images. It has the color channels and one additional alpha channel (the band index depends on the alphaFirst
parameter). Whether the alpha value is premultiplied to the color channels also depends on the value of alphaPremultiplied
(premultiplied if true).
Listing 712 shows a code sample for a composite operation.
Listing 712 Example Composite Operation
// Get the first image.
pb = new ParameterBlock();
pb.add(s1);
RenderedImage src1 = (RenderedImage)JAI.create("jpeg", pb);
// Get the second image
pb = new ParameterBlock();
pb.add(s2);
RenderedImage src2 = (RenderedImage)JAI.create("jpeg", pb);
// Create the ParameterBlock
pb = new ParameterBlock();
pb.addSource(src1);
pb.addSource(src2);
pb.add(new Boolean(false));
pb.add(new Boolean(false));
// Create the composite operation.
RenderedImage dst = (RenderedImage)JAI.create("composite", pb);
7.12 Thresholding
Thresholding, also known as binary contrast enhancement, provides a simple means of defining the boundaries of objects that appear on a contrasting background. The Threshold
operation takes one rendered image, and maps all the pixels of this image whose values fall within a specified range to a specified constant. The range is specified by a low value and a high value.
The pixel values of the destination image are defined by the following pseudocode:
lowVal = (low.length < dstNumBands) ?
low[0] : low[b];
highVal = (high.length < dstNumBands) ?
high[0] : high[b];
const = (constants.length < dstNumBands) ?
constants[0] : constants[b];
if (src[x][y][b] >= lowVal && src[x][y][b] <= highVal) {
dst[x][y][b] = const;
} else {
dst[x][y][b] = src[x][y][b];
}
The Threshold
operation takes one rendered or renderable source image and three parameters:
Parameter  Type  Description 

low  double[]  The low value. 
high  double[]  The high value 
constants  double[]  The constant the pixels are mapped to. 
If the number of elements supplied via the high
, low
, and constants
arrays are less than the number of bands of the source image, the element from entry 0 is applied to all the bands. Otherwise, the element from a different entry is applied to its corresponding band.
The low
parameter defines the lower bound for the threshold
operation for each band of the image. The operation will affect only values greater than or equal to low[0]
in band 0, only values greater than or equal to low[1]
in band 1, and so on. The high
parameter defines the upper bound for the threshold
operation for each band of the image.
A common way to arrive at the optimal values for the low
and high
parameters is to perform an extrema
operation on the image (see Section 9.3, "Finding the Extrema of an Image").
Listing 713 shows a code sample for a threshold
operation in which the three parameters are passed as arguments to the operation.
Listing 713 Example Threshold Operation
// Set up the operation parameters.
PlanarImage src, dst;
Integer [] low, high, map;
int bands;
low = new Integer[bands];
high = new Integer[bands];
map = new Integer[bands];
for (int i = 0; i < bands; i++) {
low[i] = new Integer(args[1]);
high[i] = new Integer(args[2]);
map[i] = new Integer(args[3]);
}
// Create the threshold operation.
pb = new ParameterBlock();
pb.addSource(src);
pb.add(low);
pb.add(high);
pb.add(map);
RenderedImage dst = JAI.create("threshold", pb);