Contents Previous Next

Programming in Java Advanced Imaging


C H A P T E R3

Programming in Java Advanced Imaging




THIS chapter describes how to get started programming with the Java Advanced Imaging (JAI) API.

3.1 Introduction

An imaging operation within JAI is summarized in the following four steps:

3.2 An Overview of Graphs

In JAI, any operation is defined as an object. An operator object is instantiated with zero or more image sources and other parameters that define the operation. Two or more operators may be strung together so that the first operator becomes an image source to the next operator. By linking one operator to another, we create an imaging graph or chain.

In its simplest form, the imaging graph is a chain of operator objects with one or more image sources at one end and an image sinc (or "user") at the other end. The graph that is created is commonly known as a directed acyclic graph (DAG), where each object is a node in the graph and object references form the edges (see Figure 3-1).



Figure 3-1 An Example DAG

Most APIs simply leave the DAG structure of images and operators implicit. However, JAI makes the notion of a processing graph explicit and allows such graphs to be considered as entities in their own right. Rather than thinking only of performing a series of operations in sequence, you can consider the graph structure produced by the operations. The graph form makes it easier to visualize the operations.

A directed acyclic graph is a graph containing no cycles. This means that if there is a route from node A to node B then there should be no way back. Normally, when creating a graph by instantiating new nodes one at a time, cycles are easily avoided. However, when reconfiguring a graph, you must be careful not to introduce cycles into the graph.

3.3 Processing Graphs

JAI extends rendering independence, which was introduced in the Java 2D API. With rendering independence, you have the ability to describe an image as you want it to appear, independent of any specific instance of it.

In most imaging APIs, the application must know the exact resolution and size of the source image before it can begin any imaging operations on the image. The application must also know the resolution of the output device (computer monitor or color printer) and the color and tonal quality of the original image. A rendering-independent description is concerned with none of these. Rendering-independent sources and operations permit operations to be specified in resolution-independent coordinates.

Think of rendering independence a bit like how a PostScript file is handled in a computer. To display a PostScript file on a monitor or to print the file to a high-resolution phototypesetter, you don't need to know the resolution of the output device. The PostScript file is essentially rendering independent in that it displays properly no matter what the resolution of the output device is.

JAI has a "renderable" mode in which it treats all image sources as rendering independent. You can set up a graph (or chain) of renderable operations without any concern for the source image resolution or size; JAI takes care of the details of the operations.

JAI introduces two different types of graphs: rendered and renderable.


Note: The following two sections, "Rendered Graphs" and "Renderable Graphs," are for advanced JAI users. Most programmers will use JAI's Rendered mode and don't really need to know about the Renderable mode.

3.3.1 Rendered Graphs

Rendered graphs are the simplest form of rendering in JAI. Although Renderable graphs have the advantage of rendering-independence, eliminating the need to deal directly with pixels, Rendered graphs are useful when it is necessary to work directly with the pixels.

A Rendered graph processes images in immediate mode. For any node in the graph, the image source is considered to have been evaluated at the moment it is instantiated and added to the graph. Or, put another way, as a new operation is added to the chain, it appears to compute its results immediately.

A Rendered graph is composed of Rendered object nodes. These nodes are usually instances of the RenderedOp class, but could belong to any subclass of PlanarImage, JAI's version of RenderedImage.

Image sources are objects that implement the RenderedImage interface. These sources are specified as parameters in the construction of new image objects.

Let's take a look at an example of a rendered graph in Listing 3-1. This example, which is a code fragment rather than an entire class definition, creates two constant images and then adds them together.

Listing 3-1 Rendered Chain Example


     import javax.jai.*;
     import javax.jai.widget.*;
     import java.awt.Frame;
     public class AddExample extends Frame {
          // ScrollingImagePanel is a utility widget that
          // contains a Graphics2D (i.e., is an image sink).
          ScrollingImagePanel imagePanel1;
          // For simplicity, we just do all the work in the
          // class constructor.
          public AddExample(ParameterBlock param1,
                            ParameterBlock param2) {
               // Create a constant image
               RenderedOp im0 = JAI.create("constant", param1);
               // Create another constant image.
               RenderedOp im1 = JAI.create("constant", param2);
               // Add the two images together.
               RenderedOp im2 = JAI.create("add", im0, im1);
               // Display the original in a scrolling window
               imagePanel1 = new ScrollingImagePanel(im2, 100, 100);
               // Add the display widget to our frame.
               add(imagePanel1);
          }
     }

The first three lines of the example code specify which classes to import. The classes prefixed with javax.jai are the Java Advanced Imaging classes. The java.awt prefix specifies the core Java API classes.

     import javax.jai.*;
     import javax.jai.widget.*;
     import java.awt.Frame;
The next line declares the name of the program and that it runs in a Frame, a window with a title and border.

     public class AddExample extends Frame {
The next line of code creates a ScrollingImagePanel, which is the ultimate destination of our image:

     ScrollingImagePanel imagePanel1;
Next, a ParameterBlock for each source image is defined. The parameters specify the image height, width, origin, tile size, and so on.

     public AddExample(ParameterBlock param1,
                       ParameterBlock param2) {
The next two lines define two operations that create the two "constant" images that will be added together to create the destination image (see Section 4.7, "Creating a Constant Image").

     RenderedOp im0 = JAI.create("constant", param1);
     RenderedOp im1 = JAI.create("constant", param2);
Next, our example adds the two images together (see Section 6.5.1, "Adding Two Source Images").

     RenderedOp im2 = JAI.create("add", im0, im1);
Finally, we display the destination image in a scrolling window and add the display widget to our frame.

     imagePanel1 = new ScrollingImagePanel(im2, 100, 100);
     add(imagePanel1);
Once pixels start flowing, the graph will look like Figure 3-2. The display widget drives the process. We mention this because the source images are not loaded and no pixels are produced until the display widget actually requests them.



Figure 3-2 Rendered Chain Example

3.3.2 Renderable Graphs

A renderable graph is a graph that is not evaluated at the time it is specified. The evaluation is deferred until there is a specific request for a rendering. This is known as deferred execution; evaluation is deferred until there is a specific request for rendering.

In a renderable graph, if a source image should change before there is a request for rendering, the changes will be reflected in the output. This process can be thought of as a "pull" model, in which the requestor pulls the image through the chain, which is the opposite of the AWT imaging push model.

A renderable graph is made up of nodes implementing the RenderableImage interface, which are usually instances of the RenderableOp class. As the renderable graph is constructed, the sources of each node are specified to form the graph topology. The source of a renderable graph is a Renderable image object.

Let's take a look at an example of a renderable graph in Listing 3-2. This example reads a TIFF file, inverts its pixel values, then adds a constant value to the pixels. Once again, this example is a code fragment rather than an entire class definition.

Listing 3-2 Renderable Chain Example


     // Get renderable source object from a TIFF source.
     // The ParameterBlock `pb0' contains the name
     // of the source (file, URL, etc.). The objects `hints0',
     // `hints1', and `hints2' contain rendering hints and are
     // assumed to be created outside of this code fragment.
     RenderableOp sourceImg = 
               JAI.createRenderable("TIFF", pb0);
     // Set up the parameter block for the first op.
     ParameterBlock pb1 = new ParameterBlock();
     pb1.addSource(sourceImage);
     // Make first Op in Renderable chain an invert.
     RenderableOp Op1 = JAI.createRenderable("invert", pb1);
     // Set up the parameter block for the second Op.
     // The constant to be added is "2".
     ParameterBlock pb2 = new ParameterBlock();
     pb2.addSource(Op1);        // Op1 as the source
     pb2.add(2.0f);             // 2.0f as the constant
     // Make a second Op a constant add operation.
     RenderableOp Op2 = 
               JAI.createRenderable("addconst", pb2);
     // Set up a rendering context.
     AffineTransform screenResolution = ...;
     RenderContext rc = new RenderContext(screenResolution);
     // Get a rendering.
     RenderedImage rndImg1 = Op2.createRendering(rc);
     // Display the rendering onscreen using screenResolution.
     imagePanel1 = new ScrollingImagePanel(rndImg1, 100, 100);

In this example, the image source is a rendering-independent TIFF image. A TIFF RenderableOp is created as a source for the subsequent operations:

     RenderableOp sourceImg = 
               JAI.createRenderable("TIFF", pb0);
Next, a ParameterBlock is set up for the first operation. The parameter block contains sources for the operation and parameters or other objects that the operator may require.

     ParameterBlock pb1 = new ParameterBlock();
     pb1.addSource(sourceImage);
An "invert" RenderableOp is then created with the TIFF image as the source. The invert operation inverts the pixel values of the source image and creates a RenderableImage as the result of applying the operation to a tuple (source and parameters).

     RenderableOp Op1 = JAI.createRenderable("invert", pb1);
The next part of the code example sets up a ParameterBlock for the next operation. The ParameterBlock defines the previous operation (Op1) as the source of the next operation and sets a constant with a value of 2.0, which will be used in the next "add constant" operation.

     ParameterBlock pb2 = new ParameterBlock();
     pb2.addSource(Op1);        // Op1 as the source
     pb2.add(2.0f);             // 2.0f as the constant
The second operation (Op2) is an add constant (addconst), which adds the constant value (2.0) to the pixel values of a source image on a per-band basis. The pb2 parameter is the ParameterBlock set up in the previous step.

     RenderableOp Op2 = 
               JAI.createRenderable("addconst", pb2);
After Op2 is created, the renderable chain thus far is shown in Figure 3-3.



Figure 3-3 Renderable Chain Example

Next, a RenderContext is created using an AffineTransform that will produce a screen-size rendering.

     AffineTransform screenResolution = ...;
     RenderContext rc = new RenderContext(screenResolution);
This rendering is created by calling the RenderableImage.createRendering method on Op2. The createRendering method does not actually compute any pixels, bit it does instantiate a RenderedOp chain that will produce a rendering at the appropriate pixel dimensions.

     RenderedImage rndImg1 = Op2.createRendering(rc);
The Renderable graph can be thought of as a template that, when rendered, causes the instantiation of a parallel Rendered graph to accomplish the actual processing. Now let's take a look at what happens back up the rendering chain in our example:

After the creation of the ScrollingImagePanel, the Renderable and Rendered chains look like Figure 3-4.



Figure 3-4 Renderable and Rendered Graphs after the getImage Call

At this point in the chain, no pixels have been processed and no OpImages, which actually calculate the results, have been created. Only when the ScrollingImagePanel needs to put pixels on the screen are the OpImages created and pixels pulled through the Rendered chain, as done in the final line of code.

     imagePanel1 = new ScrollingImagePanel(rndImg1, 100, 100);

3.3.3 Reusing Graphs

Many times, it is more desirable to make changes to an existing graph and reuse it than to create another nearly identical graph. Both Rendered and Renderable graphs are editable, with certain limitations.

3.3.3.1 Editing Rendered Graphs

Initially, a node in a Rendered graph is mutable; it may be assigned new sources, which are considered to be evaluated as soon as they are assigned, and its parameter values may be altered. However, once rendering takes place at a node, it becomes frozen and its sources and parameters cannot be changed.

A chain of Rendered nodes may be cloned without freezing any of its nodes by means of the RenderedOp.createInstance method. Using the createInstance method, a Rendered graph may be configured and reused at will, as well as serialized and transmitted over a network.

The RenderedOp class provides several methods for reconfiguring a Rendered node. The setParameter methods can be used to set the node's parameters to a byte, char, short, int, long, float, double, or an Object. The setOperationName method can be used to change the operation name. The setParameterBlock method can be used to change the nodes's ParameterBlock.

3.3.3.2 Editing Renderable Graphs

Since Renderable graphs are not evaluated until there is a specific request for a rendering, the nodes may be edited at any time. The main concern with editing Renderable graphs is the introduction of cycles, which must be avoided.

The RenderableOp class provides several methods for reconfiguring a Renderable node. The setParameter methods can be used to set the node's parameters to a byte, char, short, int, long, float, double, or an Object. The setParameterBlock method can be used to change the nodes's ParameterBlock. The setProperty method can be used to change a node's local property. The setSource method can be used to set one of the node's sources to an Object.

3.4 Remote Execution

Up to this point, we have been talking about standalone image processing. JAI also provides for client-server image processing through what is called the Remote Execution model.

Remote execution is based on Java RMI (remote method invocation). Java RMI allows Java code on a client to invoke method calls on objects that reside on another computer without having to move those objects to the client. The advantages of remote execution become obvious if you think of several clients wanting to access the same objects on a server. To learn more about remote method invocation, refer to one of the books on Java described in "Related Documentation" on page xv.

To do remote method invocation in JAI, a RemoteImage is set up on the server and a RenderedImage chain is set up on the client. For more information, see Chapter 12, "Client-Server Imaging."

3.5 Basic JAI API Classes

JAI consists of several classes grouped into five packages:

Now, let's take a look at the most common classes in the JAI class hierarchy.

3.5.1 The JAI Class

The JAI class cannot be instantiated; it is simply a placeholder for static methods that provide a simple syntax for creating Renderable and Rendered graphs. The majority of the methods in this class are used to create a RenderedImage, taking an operation name, a ParameterBlock, and RenderingHints as arguments. There is one method to create a RenderableImage, taking an operation name, a ParameterBlock, and RenderingHints as arguments.

There are several variations of the create method, all of which take sources and parameters directly and construct a ParameterBlock automatically.

3.5.2 The PlanarImage Class

The PlanarImage class is the main class for describing two-dimensional images in JAI. PlanarImage implements the RenderedImage interface from the Java 2D API. TiledImage and OpImage, described later, are subclasses of PlanarImage.

The RenderedImage interface describes a tiled, read-only image with a pixel layout described by a SampleModel and a DataBuffer. Each tile is a rectangle of identical dimensions, laid out on a regular grid pattern. All tiles share a common SampleModel.

In addition to the capabilities offered by RenderedImage, PlanarImage maintains source and sink connections between the nodes of rendered graphs. Since graph nodes are connected bidirectionally, the garbage collector requires assistance to detect when a portion of a graph is no longer referenced from user code and may be discarded. PlanarImage takes care of this by using the Weak References API of Java 2.

Any RenderedImages from outside the API are "wrapped" to produce an instance of PlanarImage. This allows the API to make use of the extra functionality of PlanarImage for all images.

3.5.3 The CollectionImage Class

CollectionImage is the abstract superclass for four classes representing collections of PlanarImages:

3.5.4 The TiledImage Class

The TiledImage class represents images containing multiple tiles arranged into a grid. The tiles form a regular grid, which may occupy any rectangular region of the plane.

TiledImage implements the WritableRenderedImage interface from the Java 2D API, as well as extending PlanarImage. A TiledImage allows its tiles to be checked out for writing, after which their pixel data may be accessed directly. TiledImage also has a createGraphics method that allows its contents to be altered using Java 2D API drawing calls.

A TiledImage contains a tile grid that is initially empty. As each tile is requested, it is initialized with data from a PlanarImage source. Once a tile has been initialized, its contents can be altered. The source image may also be changed for all or part of the TiledImage using its set methods. In particular, an arbitrary region of interest (ROI) may be filled with data copied from a PlanarImage source.

The TiledImage class includes a method that allows you to paint a Graphics2D onto the TiledImage. This is useful for adding text, lines, and other simple graphics objects to an image for annotating the image. For more on the TiledImage class, see Section 4.2.2, "Tiled Image."

3.5.5 The OpImage Class

The OpImage class is the parent class for all imaging operations, such as:

The OpImage is able to determine what source areas are sufficient for the computation of a given area of the destination by means of a user-supplied mapDestRect method. For most operations, this method as well as a suitable implementation of getTile is supplied by a standard subclass of OpImage, such as PointOpImage or AreaOpImage.

An OpImage is effectively a PlanarImage that is defined computationally. In PlanarImage, the getTile method of RenderedImage is left abstract, and OpImage subclasses override it to perform their operation. Since it may be awkward to produce a tile of output at a time, due to the fact that source tile boundaries may need to be crossed, the OpImage class defines a getTile method to cobble (copy) source data as needed and to call a user-supplied computeRect method. This method then receives contiguous source Rasters that are guaranteed to contain sufficient data to produce the desired results. By calling computeRect on subareas of the desired tile, OpImage is able to minimize the amount of data that must be cobbled.

A second version of the computeRect method that is called with uncobbled sources is available to extenders. This interface is useful for operations that are implemented using iterators (see Section 14.4, "Iterators"), which abstract away the notion of tile boundaries.

3.5.6 The RenderableOp Class

The RenderableOp class provides a lightweight representation of an operation in the Renderable space (see Section 3.3.2, "Renderable Graphs"). RenderableOps are typically created using the createRenderable method of the JAI class, and may be edited at will. RenderableOp implements the RenderableImage interface, and so may be queried for its rendering-independent dimensions.

When a RenderableOp is to be rendered, it makes use of the OperationRegistry (described in Chapter 14) to locate an appropriate ContextualRenderedImageFactory object to perform the conversion from the Renderable space into a RenderedImage.

3.5.7 The RenderedOp Class

The RenderedOp is a lightweight object similar to RenderableOp that stores an operation name, ParameterBlock, and RenderingHints, and can be joined into a Rendered graph (see Section 3.3.1, "Rendered Graphs"). There are two ways of producing a rendering of a RenderedOp:

RenderedOps that have not been rendered may have their sources and parameters altered. Sources are considered evaluated as soon as they are connected to a RenderedOp.

3.6 JAI API Operators

The JAI API specifies a core set of image processing operators. These operators provide a common ground for applications programmers, since they can then make assumptions about what operators are guaranteed to be present on all platforms.

The general categories of image processing operators supported include:

The JAI API also supports abstractions for many common types of image collections, such as time-sequential data and image pyramids. These are intended to simplify operations on image collections and allow the development of operators that work directly on these abstractions.

3.6.1 Point Operators

Point operators allow you to modify the way in which the image data fills the available range of gray levels. This affects the image's appearance when displayed. Point operations transform an input image into an output image in such a way that each output pixel depends only on the corresponding input pixel. Point operations do not modify the spatial relationships within an image.

Table 3-1 lists the JAI point operators.

Table 3-1 Point Operators
Operator Description Reference
Absolute
Gives the mathematical absolute value of the pixel values of a source image.
page 170
Add
Adds the pixel values of two source images on a per-band basis.
page 160
AddCollection
Takes a collection of rendered images and adds every set of pixels, one from each source image of the corresponding position and band.
page 162
AddConst
Adds one of a set of constant values to the pixel values of a source image on a per-band basis.
page 161
AddConstToCollection Takes a collection of rendered images and an array of double constants, and for each rendered image in the collection adds a constant to every pixel of its corresponding band.
page 163
And
Performs a bitwise logical AND between the pixel values of the two source images on a per-band basis.
page 152
AndConst
Performs a bitwise logical AND between the pixel values of a source image with one of a set of per-band constants.
page 153
BandCombine
Computes an arbitrary linear combination of the bands of a source image for each band of a destination image, using a specified matrix.
page 135
BandSelect
Copies the pixel data from a specified number of bands in a source image to a destination image in a specified order.
page 178
Clamp
Sets all the pixel values below a "low" value to that low value, and sets all the pixel values above a "high" value to that high value.
page 177
ColorConvert
Performs a pixel-by-pixel color conversion of the data in a source image.
page 134
Composite
Combines two images based on their alpha values at each pixel.
page 235
Constant
Defines a multi-banded, tiled rendered image with constant pixel values.
page 119
Divide
Divides the pixel values of one first source image by the pixel values of another source image on a per-band basis.
page 165
DivideByConst
Divides the pixel values of a source image by a constant.
page 166
DivideComplex
Divides two images representing complex data.
page 167
DivideIntoConst
Divides a constant by the pixel values of a source image.
page 166
Exp
Takes the exponential of the pixel values of an image.
page 171
Invert
Inverts the pixel values of an image.
page 233
Log
Takes the logarithm of the pixel values of an image.
page 233
Lookup
Performs general table lookup on an image.
page 197
MatchCDF
Matches pixel values to a supplied cumulative distribution function (CDF).
page 195
Max
Computes the pixelwise maximum value of two images.
page 150
Min
Computes the pixelwise minimum value of two images.
page 151
Multiply
Multiplies the pixel values of two source images on a per-band basis.
page 168
MultiplyComplex
Multiplies two images representing complex data.
page 169
MultiplyConst
Multiplies the pixel values of a source image with a constant on a per-band basis.
page 169
Not
Performs a bitwise logical NOT operation on each pixel of a source image on a per-band basis.
page 158
Or
Performs a bitwise logical OR between the pixel values of the two source images on a per-band basis.
page 154
OrConst
Performs a bitwise logical OR between the pixel values of a source image with a constant on a per-band basis.
page 155
Overlay
Overlays one image on top of another image.
page 234
Pattern
Defines a tiled image consisting of a repeated pattern.
page 76
Piecewise
Performs a piecewise linear mapping of the pixel values of an image.
page 194
Rescale
Maps the pixel values of an image from one range to another range.
page 192
Subtract
Subtracts the pixel values of the second source image from the first source image on a per-band basis.
page 163
SubtractConst
Subtracts one of a set of constant values from the pixel values of a source image on a per-band basis.
page 164
SubtractFromConst
Subtracts the pixel values of a source image from one of a set of constant values on a per-band basis.
page 164
Threshold
Maps all the pixel values of an image that fall within a given range to one of a set of per-band constants.
page 237
Xor
Performs a bitwise logical XOR between the pixel values of two source images on a per-band basis.
page 156
XorConst
Performs a bitwise logical XOR between the pixel values of a source image with a constant.
page 157

3.6.2 Area Operators

The area operators perform geometric transformations, which result in the repositioning of pixels within an image. Using a mathematical transformation, pixels are located from their x and y spatial coordinates in the input image to new coordinates in the output image.

There are two basic types of area operations: linear and nonlinear. Linear operations include translation, rotation, and scaling. Non-linear operations, also known as warping transformations, introduce curvatures and bends to the processed image.

Table 3-2 lists the JAI area operators.

Table 3-2 Area Operators
Operator Description Reference
Border Adds a border around an image.
page 183
BoxFilter Determines the intensity of a pixel in an image by averaging the source pixels within a rectangular area around the pixel.
page 216
Convolve Computes each output sample by multiplying elements of a kernel with the samples surrounding a particular source sample.
page 213
Crop Crops an image to a specified rectangular area.
page 191
MedianFilter A non-linear filter that is useful for removing isolated lines or pixels while preserving the overall appearance of an image.
page 218

3.6.3 Geometric Operators

Geometric operators allow you to modify the orientation, size, and shape of an image. Table 3-3 lists the JAI geometric operators.

Table 3-3 Geometric Operators
Operator Description Reference
Affine
Performs (possibly filtered) affine mapping between a source and a destination image.
page 262
Rotate
Rotates an image about a given point by a given angle.
page 260
Scale
Translates and resizes an image.
page 258
Shear
Shears an image horizontally or vertically.
page 273
Translate
Copies an image to a new location in the plane.
page 256
Transpose
Flips or rotates an image.
page 271
Warp
Performs (possibly filtered) general warping on an image.
page 275

3.6.4 Color Quantization Operators

Color quantization, also known as dithering, is often used to reduce the appearance of amplitude contouring on monochrome frame buffers with fewer than eight bits of depth or color frame buffers with fewer than 24 bits of depth. Table 3-4 lists the JAI color quantization operators.

Table 3-4 Color Quantization Operators
Operator Description Reference
ErrorDiffusion
Performs color quantization by finding the nearest color to each pixel in a supplied color map.
page 174
OrderedDither
Performs color quantization by finding the nearest color to each pixel in a supplied color cube and "shifting" the resulting index value by a pseudo-random amount determined by the values of a supplied dither mask.
page 172

3.6.5 File Operators

The file operators are used to read or write image files. Table 3-5 lists the JAI file operators.

Table 3-5 File Operators
Operator Description Reference
AWTImage
Imports a standard AWT image into JAI.
page 113
BMP
Reads BMP data from an input stream.
page 106
Encode
Encodes an image file in any one of the recognized formats.
page 342
FileLoad
Reads an image from a file.
page 99
FileStore
Writes an image to a given file in a specified format using the default encoding parameters of that format.
page 341
Format
Reformats an image.
page 115
FPX
Reads FlashPix data from an input stream.
page 105
GIF
Reads GIF data from an input stream.
page 106
JPEG
Reads a standard JPEG (JFIF) file.
page 106
PNG
Reads a PNG input stream.
page 107
PNM
Reads a standard PNM file, including PBM, PGM, and PPM images of both ASCII and raw formats.
page 112
Stream
Reads java.io.InputStream files.
page 99
TIFF
Reads TIFF 6.0 data from an input stream.
page 100
URL
Reads an image by way of a Uniform Resource Locator (URL).
page 114

3.6.6 Frequency Operators

Frequency operators are used to decompose an image from its spatial-domain form into a frequency-domain form of fundamental frequency components. Operators also are available to perform an inverse frequency transform, in which the image is converted from the frequency form back into the spatial form.

JAI supports several frequency transform types. The most common frequency transform type is the Fourier transform. JAI uses the discrete form known as the discrete Fourier transform. The inverse discrete Fourier transform can be used to convert the image back to a spatial image. JAI also supports the discrete cosine transform and its opposite, the inverse discrete cosine transform.

Table 3-6 lists the JAI frequency operators.

Table 3-6 Frequency Operators
Operator Description Reference
Conjugate
Generates the complex conjugate image of a complex image.
page 228
DCT
Discrete cosine transform coding.
page 224
DFT
Computes the discrete Fourier transform of an image.
page 220
IDCT
Inverse discrete cosine transform
page 225
IDFT
Computes the inverse discrete Fourier transform of an image.
page 223
ImageFunction
Generates an image from a functional description.
page 229
Magnitude
Computes the magnitude of each pixel of an image.
page 226
MagnitudeSquared
Computes the squared magnitude of each pixel of an image.
page 227
PeriodicShift
Computes the periodic translation of an image.
page 228
Phase
Computes the phase angle of each pixel of an image.
page 227
PolarToComplex
Computes a complex image from a magnitude and a phase image.
page 229

3.6.7 Statistical Operators

Statistical operators provide the means to analyze the content of an image. Table 3-7 lists the JAI statistical operators.

Table 3-7 Statistical Operators
Operator Description Reference
Extrema
Scans an image and finds the image-wise maximum and minimum pixel values for each band.
page 298
Histogram
Scans a specified region of an image and generates a histogram based on the pixel values within that region of the image.
page 300
Mean
Scans a specified region of an image and computes the image-wise mean pixel value for each band within the region.
page 297

3.6.8 Edge Extraction Operators

The edge extraction operators allow image edge enhancement. Edge enhancement reduces an image to show only its edge details. Edge enhancement is implemented through spatial filters that detect a specific pixel brightness slope within a group of pixels in an image. A steep brightness slope indicates the presence of an edge.

Table 3-8 lists the JAI edge extraction operators.

Table 3-8 Edge Extraction Operators
Operator Description Reference
GradientMagnitude
Computes the magnitude of the image gradient vector in two orthogonal directions.
page 305

3.6.9 Miscellaneous Operators

The miscellaneous operators do not fall conveniently into any of the previous categories. Table 3-9 lists the JAI miscellaneous operators.

Table 3-9 Miscellaneous Operators
Operator Description Reference
Renderable
Produces a RenderableImage from a RenderedImage.
page 117

3.7 Creating Operations

Most image operation objects are created with some variation on the following methods:

For a renderable graph:
There are four variations on methods for creating operations in the Renderable mode, as listed in Table 3-10.

Table 3-10 JAI Class Renderable Mode Methods
Method Parameters Description
createRenderable
opName
parameterBlock
Creates a RenderableOp that represents the named operation, using the sources and parameters specified in the ParameterBlock.
createRenderableNS
opName
parameterBlock
The same as the previous method, only this version is non-static.
createRenderable-Collection
opName
parameterBlock
Creates a Collection that represents the named operation, using the sources and parameters specified in the ParameterBlock.
createRenderable-CollectionNS
opName
parameterBlock
The same as the previous method, only this version is non-static.

For example:


RenderableOp im = JAI.createRenderable("operationName",
                                 paramBlock);


The JAI.createRenderable method creates a renderable node operation that takes two parameters:

For a rendered graph:
There are a great many more variations on methods for creating operations in the Rendered mode, as listed in Table 3-11. The first five methods in the table take sources and parameters specified in a ParameterBlock. The remaining methods are convenience methods that take various numbers of sources and parameters directly.

Table 3-11 JAI Class Rendered Mode Methods
Method Parameters Description
create
opName
parameterBlock
hints
Creates a RenderedOp that represents the named operation, using the sources and parameters specified in the ParameterBlock, and applying the specified hints to the destination. This method is appropriate only when the final results return a single RenderedImage.
createNS
opName
parameterBlock
hints
The same as the previous method, only this version is non-static.
createCollection
opName
parameterBlock
hints
Creates a Collection that represents the named operation, using the sources and parameters specified in the ParameterBlock, and applying the specified hints to the destination. This method is appropriate only when the final results return a Collection.
createCollectionNS
opName
parameterBlock
hints
The same as the previous method, only this version is non-static.
create
opName
parameterBlock
Creates a RenderedOp with null rendering hints.
create
opName
param
Creates a RenderedOp that takes one parameter.
create
opName
param1
param2
Creates a RenderedOp that takes two parameters. There are two variations on this method, depending on the parameter data type (Object or int).
create
opName
param1
param2
param3
Creates a RenderedOp that takes three parameters. There are two variations on this method, depending on the parameter data type (Object or int).
create
opName
param1
param2
param3
param4
Creates a RenderedOp that takes four parameters. There are two variations on this method, depending on the parameter data type (Object or int).
create
opName
renderedImage
Creates a RenderedOp that takes one source image.
create
opName
Collection
Creates a RenderedOp that takes one source collection.
create
opName
renderedImage
param
Creates a RenderedOp that takes one source and one parameter. There are two variations on this method, depending on the parameter data type (Object or int).
create
opName
renderedImage
param1
param2
Creates a RenderedOp that takes one source and two parameters. There are two variations on this method, depending on the parameter data type (Object or float).
create
opName
renderedImage
param1
param2
param3
Creates a RenderedOp that takes one source and three parameters. There are three variations on this method, depending on the parameter data type (Object, int, or float).
create
opName
renderedImage
param1
param2
param3
param4
Creates a RenderedOp that takes one source and four parameters. There are four variations on this method, depending on the parameter data type (Object, int, or float).
create
opName
renderedImage
param1
param2
param3
param4
param5
Creates a RenderedOp that takes one source and five parameters. There are three variations on this method, depending on the parameter data type (Object, int, or float).
create
opName
renderedImage
param1
param2
param3
param4
param5
param6
Creates a RenderedOp that takes one source and six parameters. There are two variations on this method, depending on the parameter data type (Object or int).
create
opName
renderedImage1
renderedImage2
Creates a RenderedOp that takes two sources.
create
opName
renderedImage1
renderedImage2
param1
param2
Creates a RenderedOp that takes two sources and four parameters.
createCollection
opName
parameterBlock
Creates a Collection with null rendering hints.

Two versions of the create method are non-static. These methods may be used with a specific instance of the JAI class. These methods method should only be used when the final result returned is a single RenderedImage. However, the source(s) supplied may be a collection of images or a collection of collections.


     RenderedOp im = JAI.createNS("operationName", source, param1,
                                param2)

The rendering hints associated with this instance of JAI are overlaid with the hints passed to this method. That is, the set of keys will be the union of the keys from the instance's hints and the hints parameter. If the same key exists in both places, the value from the hints parameter will be used.

Many of the JAI operations have default values for some of the parameters. If you wish to use any of the default values in an operation, you do not have to specify that particular parameter in the ParameterBlock. The default value is automatically used in the operation. Parameters that do not have default values are required; failure to supply a required parameter results in a NullPointerException.

3.7.1 Operation Name

The operation name describes the operator to be created. The operation name is a string, such as "add" for the operation to add two images. See Section 3.6, "JAI API Operators," for a list of the operator names.

The operation name is always enclosed in quotation marks. For example:

     "Mean"
     "BoxFilter"
     "UnsharpMask"
The operation name parsing is case-insensitive. All of the following variations are legal:

     "OrConst"
     "orConst"
     "ORconst"
     "ORCONST"
     "orconst"

3.7.2 Parameter Blocks

The parameter block contains the source of the operation and a set parameters used by the operation. The contents of the parameter block depend on the operation being created and may be as simple as the name of the source image or may contain all of the operator parameters (such as the x and y displacement and interpolation type for the translate operation).

Parameter blocks encapsulate all the information about sources and parameters (Objects) required by the operation. The parameters specified by a parameter block are objects.

These controlling parameters and sources can be edited through the setParameterBlock method to affect specific operations or even the structure of the rendering chain itself. The modifications affect future RenderedImages derived from points in the chain below where the change took place.

There are two separate classes for specifying parameter blocks:

The parameter block must contain the same number of sources and parameters as required by the operation. For example, the Add operation requires two source images and no parameters. The addConst operator requires one source and a parameter specifying the constant value. If the sources and parameters do not match the operation requirements, an exception is thrown. However, when the ParameterBlockJAI class is used, if the required parameter values are not specified, default parameter values are automatically inserted when available.

3.7.2.1 Adding Sources to a Parameter Block

Sources are added to a parameter block with the addSource() method. The following example creates a new ParameterBlock named pb and then the addSource() method is used to add the source image (im0) to the ParameterBlock.


     ParameterBlock pb = new ParameterBlock();
     pb.addSource(im0);

To add two sources to a parameter block, use two addSource() methods.


     ParameterBlock pb = new ParameterBlock();
     pb.addSource(im0);
     pb.addSource(im1);

3.7.2.2 Adding or Setting Parameters

As described before, there are two separate classes for specifying parameter blocks: ParameterBlock and ParameterBlockJAI. Both classes work very much alike, except for two differences: ParameterBlockJAI automatically provides default parameter values and allows setting parameters by name; ParameterBlock does not.

ParameterBlock
The operation parameters are added to a ParameterBlock with the ParameterBlock.add() method. The following example adds two values (150 and 200) to the ParameterBlock named pb, which was created in the previous example.


     pb.add(150);
     pb.add(200);

The add() method can be used with all of the supported data types: byte, short, integer, long, float, and double. When using the ParameterBlock object, all parameters that an operation requires must be added, else the operation will fail.


API: java.awt.image.renderable.ParameterBlock

adds an image to end of the list of sources. The image is stored as an object to allow new node types in the future.

adds a Byte to the list of parameters.

adds a Short to the list of parameters.

adds a Integer to the list of parameters.

adds a Long to the list of parameters.

adds a Float to the list of parameters.

adds a Double to the list of parameters.

ParameterBlockJAI
Since the ParameterBlockJAI object already contains default parameter values at the time of construction, the parameters must be changed (or set) with the ParameterBlockJAI.set(value, index) methods rather than the add() method. The add() methods should not be used since the parameter list is already long enough to hold all of the parameters required by the OperationDescriptor.

Listing 3-3 shows the creation of a ParameterBlockJAI intended to be passed to a rotate operation. The rotate operation takes four parameters: xOrigin, yOrigin, angle, and interpolation. The default values for xOrigin and yOrigin are 0.0F (for both). In this example, these two values are not set, as the default values are sufficient for the operation. The other two parameters (angle and interpolation) have default values of null and must therefore be set. The source image must also be specified.

Listing 3-3 Example ParameterBlockJAI


     // Specify the interpolation method to be used
     interp = Interpolation.create(Interpolation.INTERP_NEAREST);
     // Create the ParameterBlockJAI and add the interpolation to it
     ParameterBlockJAI pb = new ParameterBlockJAI();
     pb.addSource(im);                 // The source image
     pb.set(1.2F, "angle");            // The rotation angle in radians
     pb.set(interp, "interpolation");  // The interpolation method


API: javax.media.jai.ParameterBlockJAI

sets a named parameter to a byte value.

sets a named parameter to a char value.

sets a named parameter to an int value.

sets a named parameter to a short value.

sets a named parameter to a long value.

sets a named parameter to a float value.

sets a named parameter to a double value.

sets a named parameter to an Object value.

3.7.3 Rendering Hints

The rendering hints contain a set of hints that describe how objects are to be rendered. The rendering hints are always optional in any operation.

Rendering hints specify different rendering algorithms for such things as antialiasing, alpha interpolation, and dithering. Many of the hints allow a choice between rendering quality or speed. Other hints turn off or on certain rendering options, such as antialiasing and fractional metrics.

There are two separate classes for specifying rendering hints:

3.7.3.1 Java AWT Rendering Hints

Table 3-12 lists the rendering hints inherited from java.awt.RenderingHints.

Table 3-12 Java AWT Rendering Hints
Key Value Description
Alpha_Interpolation
Alpha_Interpolation_
Default

Rendering is done with the platform default alpha interpolation.
Alpha_Interpolation_
Quality

Appropriate rendering algorithms are chosen with a preference for output quality.
Alpha_Interpolation_Speed
Appropriate rendering algorithms are chosen with a preference for output speed.
Antialiasing
Antialias_Default
Rendering is done with the platform default antialiasing mode.
Antialias_Off
Rendering is done without antialiasing.
Antialias_On
Rendering is done with antialiasing
Color_Rendering
Color_Render_Default
Rendering is done with the platform default color rendering.
Color_Render_Quality
Appropriate rendering algorithms are chosen with a preference for output quality.
Color_Render_Speed
Appropriate rendering algorithms are chosen with a preference for output speed.
Dithering
Dither_Default
Use the platform default for dithering.
Dither_Disable
Do not do dither when rendering.
Dither_Enable
Dither with rendering when needed.
FractionalMetrics
FractionalMetrics_Default
Use the platform default for fractional metrics.
FractionalMetrics_Off
Disable fractional metrics.
FractionalMetrics_On
Enable fractional metrics.
Interpolation
Interpolation_Bicubic
Perform bicubic interpolation.
Interpolation_Bilinear
Perform bilinear interpolation.
Interpolation_Nearest_
Neighbor

Perform nearest-neighbor interpolation.
Rendering
Render_Default
The platform default rendering algorithms will be chosen.
Render_Quality
Appropriate rendering algorithms are chosen with a preference for output quality.
Render_Speed
Appropriate rendering algorithms are chosen with a preference for output speed.
Text_Antialiasing
Text_Antialias_Default
Text rendering is done using the platform default text antialiasing mode.
Text_Antialias_Off
Text rendering is done without antialiasing.
Text_Antialias_On
Text rendering is done with antialiasing.

To set the rendering hints, create a RenderingHints object and pass it to the JAI.create method you want to affect. Setting a rendering hint does not guarantee that a particular rendering algorithm, will be used; not all platforms support modification of the rendering code.

In the following example, the rendering preference is set to quality.


     qualityHints = new
                    RenderingHints(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);

Now that a RenderingHints object, qualityHints, has been created, the hints can be used in an operation using a JAI.create method.

3.7.3.2 JAI Rendering Hints

Each instance of a JAI object contains a set of rendering hints that will be used for all image or collection creations. These hints are merged with any hints supplied to the JAI.create method; directly supplied hints take precedence over the common hints. When a new JAI instance is constructed, its hints are initialized to a copy of the hints associated with the default instance. The hints associated with any instance, including the default instance, may be manipulated using the getRenderingHint, setRenderingHints, and clearRenderingHints methods. As a convenience, getRenderingHint, setRenderingHint, and removeRenderingHint methods are provided that allow individual hints to be manipulated. Table 3-13 lists the JAI rendering hints.

Table 3-13 JAI Rendering hints
Key Value Description
HINT_BORDER_EXTENDER
BorderExtenderZero
Extends an image's border by filling all pixels outside the image bounds with zeros.
BorderExtenderConstant
Extends an image's border by filling all pixels outside the image bounds with constant values.
BorderExtenderCopy
Extends an image's border by filling all pixels outside the image bounds with copies of the edge pixels.
BorderExtenderWrap
Extends an image's border by filling all pixels outside the image bounds with copies of the whole image.
BorderExtenderReflect
Extends an image's border by filling all pixels outside the image bounds with copies of the whole image.
HINT_IMAGE_LAYOUT
Width
The image's width.
Height
The image's height
MinX
The image's minimum x coordinate.
MinY
The image's minimum y coordinate
TileGridXOffset
The x coordinate of tile (0, 0).
TileGridYOffset
The y coordinate of tile (0, 0).
TileWidth
The width of a tile.
TileHeight
The height of a tile.
SampleModel The image's SampleModel.
ColorModel
The image's ColorModel.
HINT_INTERPOLATION
InterpolationNearest Perform nearest-neighbor interpolation.
InterpolationBilinear Perform bilinear interpolation.
InterpolationBicubic
Perform bicubic interpolation.
InterpolationBicubic2
Perform bicubic interpolation.
HINT_OPERATION_BOUND
OpImage.OP_COMPUTE_
BOUND

An operation is likely to spend its time mainly performing computation.
OpImage.OP_IO_BOUND An operation is likely to spend its time mainly performing local I/O.
OpImage.OP_NETWORK_
BOUND

An operation is likely to spend its time mainly performing network I/O.
HINT_OPERATION_REGISTRY
Key for OperationRegistry object values.
HINT_PNG_EMIT_SQUARE_
PIXELS

True
Scale non-square pixels read from a PNG format image file to square pixels.
False
Do not scale non-square pixels.
HINT_TILE_CACHE
capacity
The capacity of the cache in tiles.
elementCount
The number of elements in the cache.
revolver
Offset to check for tile cache victims.
multiplier
Number of checks to make for tile cache victims.

Listing 3-4 shows an example of image layout rendering hints being specified for a Scale operation. The image layout rendering hint specifies that the origin of the destination opimage is set to 200 x 200.

Listing 3-4 Example of JAI Rendering Hints


     // Create the parameter block for the scale operation.
     ParameterBlock pb = new ParameterBlock();
         pb.addSource(im0);      // The source image
         pb.add(4.0F);           // The x scale factor
         pb.add(4.0F);           // The y scale factor
         pb.add(interp);         // The interpolation method
     // Specify the rendering hints.
         layout = new ImageLayout();
         layout.setMinX(200);
         layout.setMinY(200);
         RenderingHints rh =
                 new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
     // Create the scale operation.
     PlanarImage im2 = (PlanarImage)JAI.create("scale", pb, layout)



Contents Previous Next

Programming in Java Advanced Imaging


Copyright © 1999, Sun Microsystems, Inc. All rights reserved.

Casa de Bender