Monday, 30 July 2007

Image Display

JAI uses the Java 2D BufferedImage model for displaying images. The BufferedImage manages an image in memory and provides ways to store pixel data, interpret pixel data, and to render the pixel data to a Graphics2D context.

The display of images in JAI may be accomplished in several ways. First, the drawRenderedImage() call on Graphics2D may be used to produce an immediate rendering. Another method is to instantiate a display widget that responds to user requests such as scrolling and panning, as well as expose events, and requests image data from a RenderedImage source. This technique allows image data to be computed on demand.

* int getWidth()
returns the width of the rendered image.

* int getHeight()
returns the height of the rendered image.

* ScrollingImagePanel(RenderedImage im, int width, int height)
constructs a ScrollingImagePanel of a given size for a given RenderedImage.

* void setOrigin(int x, int y)
sets the image origin to a given (x, y) position. The scrollbars are updated appropriately.

* void setCenter(int x, int y)
sets the image center to a given (x, y) position. The scrollbars are updated appropriately.

* ImageCanvas(RenderedImage im, boolean drawBorder)
constructs an ImageCanvas to display a RenderedImage.

* ImageCanvas(java.awt.image.RenderedImage im)
constructs an ImageCanvas to display a RenderedImage.

* void set(RenderedImage im)
changes the source image to a new RenderedImage.

* void paint(java.awt.Graphics g)
paint the image onto a Graphics object.

* void setOrigin(int x, int y)
sets the origin of the image at x,y.

* int getXOrigin()
returns the x coordinate of the image origin.

* int getYOrigin()
returns the y coordinate of the image origin.

Tuesday, 24 July 2007

Reading Image Files

JAI directly supports several of the most common image file formats, BMP, FPX, GIF, JPEG, PNG, PNM, TIFF.

An image file usually has at least two parts: a file header and the image data. The header contains fields of pertinent information regarding the following image data. At the very least, the header must provide all the information necessary to reconstruct the original image from the stored image data. The image data itself may or may not be compressed.

For most image types, JAI offers the option of reading an image data file as a java.io.File object or as one of the subclasses of java.io.InputStream.

The Stream operation reads an image from a SeekableStream.
// Load the source image from a Stream.
RenderedImage im = JAI.create("stream", stream);


The FileLoad operation reads an image from a file.
// Load the source image from a file.
RenderedImage src = (RenderedImage)JAI.create("fileload", fileName);


The TIFF operation reads TIFF data from a TIFF SeekableStream.

For TIFF Palette color images, the colorMap always has entries of short data type, the color black being represented by 0,0,0 and white by 65536,65536,65536. To display these images, the default behavior is to dither the short values down to 8 bits. The dithering is done by calling the decode16BitsTo8Bit method for each short value that needs to be dithered.

A TIFF file may contain more than one Image File Directory (IFD). Each IFD defines a subfile, which may be used to describe related images. To determine the number of images in a TIFF file, use the TIFFDirectory.getNumDirectories() method.

API: com.sun.media.jai.codec.TIFFDecodeParam
* void setDecodePaletteAsShorts(boolean decodePaletteAsShorts)
if set, the entries in the palette will be decoded as shorts and no short-to-byte lookup will be applied to them.

* boolean getDecodePaletteAsShorts()
returns true if palette entries will be decoded as shorts, resulting in a output image with short datatype.

* byte decode16BitsTo8Bits(int s)
returns an unsigned 8-bit value computed by dithering the unsigned 16-bit value.

API: com.sun.media.jai.codec.TIFFDirectory
* static int getNumDirectories(SeekableStream stream)
returns the number of image directories (subimages) stored in a given TIFF file, represented by a SeekableStream.

API: com.sun.media.jai.codec.TIFFField
* int getTag()
returns the tag number, between 0 and 65535.

* int getType()
returns the type of the data stored in the IFD.

* int getCount()
returns the number of elements in the IFD.

API: com.sun.media.jai.codec.TIFFDirectory
* TIFFDirectory(SeekableStream stream, int directory)
constructs a TIFFDirectory from a SeekableStream.

* TIFFDirectory(SeekableStream stream, long ifd_offset)
constructs a TIFFDirectory by reading a SeekableStream.

* int getNumEntries()
returns the number of directory entries.

* TIFFField getField(int tag)
returns the value of a given tag as a TIFFField, or null if the tag is not present.

* boolean isTagPresent(int tag)
returns true if a tag appears in the directory.

* int[] getTags()
returns an ordered array of ints indicating the tag values.

* TIFFField[] getFields()
returns an array of TIFFFields containing all the fields in this directory.

* byte getFieldAsByte(int tag, int index)
returns the value of a particular index of a given tag as a byte.

* byte getFieldAsByte(int tag)
returns the value of index 0 of a given tag as a byte.

* long getFieldAsLong(int tag, int index)
returns the value of a particular index of a given tag as a long.

* long getFieldAsLong(int tag)
returns the value of index 0 of a given tag as a long.

* float getFieldAsFloat(int tag, int index)
returns the value of a particular index of a given tag as a float.

* float getFieldAsFloat(int tag)
returns the value of a index 0 of a given tag as a float.

* double getFieldAsDouble(int tag, int index)
returns the value of a particular index of a given tag as a double.

* double getFieldAsDouble(int tag)
returns the value of index 0 of a given tag as a double.


Example of Reading a BMP Image

// Wrap the InputStream in a SeekableStream.
InputStream is = new FileInputStream(filename);
SeekableStream s = SeekableStream.wrapInputStream(is, false);

// Create the ParameterBlock and add the SeekableStream to it.
ParameterBlock pb = new ParameterBlock();
pb.add(s);

// Perform the BMP operation
op = JAI.create("BMP", pb);


Example of Reading a PNM Image

// Create the ParameterBlock.
InputStream image = new FileInputStream(filename);
ParameterBlock pb = new ParameterBlock();
pb.add(image);

// Create the PNM operation.
op = JAI.create("PNM", pb);


Example of Reading an AWT Image

// Create the ParameterBlock.
ParameterBlock pb = new ParameterBlock();
pb.add(image);

// Create the AWTImage operation.
PlanarImage im = (PlanarImage)JAI.create("awtImage", pb);


Example of Reading a URL Image

// Define the URL to the image.
url = new URL("http://webstuff/images/duke.gif");

// Read the image from the designated URL.
RenderedOp src = JAI.create("url", url);


Example of Converting a Rendered Image to Renderable

// Derive the RenderableImage from the source RenderedImage.
ParameterBlock pb = new ParameterBlock();
pb.addSource(src);
pb.add(null).add(null).add(null).add(null).add(null);

// Create the Renderable operation.
RenderableImage ren = JAI.createRenderable("renderable", pb);


Example Constant Operation

// Create the ParameterBlock.
Byte[] bandValues = new Byte[1];
bandValues[0] = alpha1;
pb = new ParameterBlock();
pb.add(new Float(src1.getWidth())); // The width
pb.add(new Float(src1.getHeight())); // The height
pb.add(bandValues); // The band values

// Create the constant operation.
PlanarImage afa1 = (PlanarImage)JAI.create("constant", pb);

Streams

The new seekable classes are used to cache the image data being read so that methods can be used to seek backwards and forwards through the data without having to re-read the data. This is especially important for image data types that are segmented or that cannot be easily re-read to locate important information.

Class: SeekableStream
Extends: InputStream
Implements: DataInput
An abstract class that combines the functionality of InputStream and RandomAccessFile, along with the ability to read primitive data types in little-endian format.

Class: FileSeekableStream
Extends: SeekableStream
Implements SeekableStream functionality on data stored in a File.

Class: ByteArraySeekableStream
Extends: SeekableStream
Implements SeekableStream functionality on data stored in an array of bytes.

Class: SegmentedSeekableStream
Extends: SeekableStream
Provides a view of a subset of another SeekableStream consisting of a series of segments with given starting positions in the source stream and lengths. The resulting stream behaves like an ordinary SeekableStream.

Class: ForwardSeekableStream
Extends: SeekableStream
Provides SeekableStream functionality on data from an InputStream with minimal overhead, but does not allow seeking backwards. ForwardSeekableStream may be used with input formats that support streaming, avoiding the need to cache the input data.

Class: FileCacheSeekableStream
Extends: SeekableStream
Provides SeekableStream functionality on data from an InputStream with minimal overhead, but does not allow seeking backwards. ForwardSeekableStream may be used with input formats that support streaming, avoiding the need to cache the input data. In circumstances that do not allow the creation of a temporary file (for example, due to security consideration or the absence of local disk), the MemoryCacheSeekableStream class may be used.

Class: MemoryCacheSeekableStream
Extends: SeekableStream
Provides SeekableStream functionality on data from an InputStream, using an in-memory cache to allow seeking backwards. MemoryCacheSeekableStream should be used when security or lack of access to local disk precludes the use of FileCacheSeekableStream.

A complete list of the methods to read data:
readInt: Reads a signed 32-bit integer
readIntLE: Reads a signed 32-bit integer in little-endian order
readShort: Reads a signed 16-bit number
readShortLE: Reads a 16-bit number in little-endian order
readLong: Reads a signed 64-bit integer
readLongLE: Reads a signed 64-bit integer in little-endian order
readFloat: Reads a 32-bit float
readFloatLE: Reads a 32-bit float in little-endian order
readDouble: Reads a 64-bit double
readDoubleLE: Reads a 64-bit double in little-endian order
readChar: Reads a 16-bit Unicode character
readCharLE: Reads a 16-bit Unicode character in little-endian order
readByte: Reads an signed 8-bit byte
readBoolean: Reads a Boolean value
readUTF: Reads a string of characters in UTF (Unicode Text Format)
readUnsignedShort: Reads an unsigned 16-bit short integer
readUnsignedShortLE: Reads an unsigned 16-bit short integer in little-endian order
readUnsignedInt: Reads an unsigned 32-bit integer
readUnsignedIntLE: Reads an unsigned 32-bit integer in little-endian order
readUnsignedByte: Reads an unsigned 8-bit byte
readLine: Reads in a line that has been terminated by a line-termination character.
readFully: Reads a specified number of bytes, starting at the current stream pointer
read(): Reads the next byte of data from the input stream.

In addition to the familiar methods from InputStream, the methods getFilePointer() and seek(), are defined as in the RandomAccessFile class. The canSeekBackwards() method returns true if it is permissible to seek to a position earlier in the stream than the current value of getFilePointer(). Some subclasses of SeekableStream guarantee the ability to seek backwards while others may not offer this feature in the interest of efficiency for those users who do not require backward seeking.

Several concrete subclasses of SeekableStream are supplied in the com.sun.media.jai.codec package. Three classes are provided for the purpose of adapting a standard InputStream to the SeekableStream interface. The ForwardSeekableStream class does not allow seeking backwards, but is inexpensive to use. The FileCacheSeekableStream class maintains a copy of all of the data read from the input in a temporary file; this file will be discarded automatically when the FileSeekableStream is finalized, or when the JVM exits normally.

The FileCacheSeekableStream class is intended to be reasonably efficient apart from the unavoidable use of disk space. In circumstances where the creation of a temporary file is not possible, the MemoryCacheSeekableStream class may be used. The MemoryCacheSeekableStream class creates a potentially large in-memory buffer to store the stream data and so should be avoided when possible. The FileSeekableStream class wraps a File or RandomAccessFile. It forwards requests to the real underlying file. FileSeekableStream performs a limited amount of caching to avoid excessive I/O costs.

A convenience method, wrapInputStream is provided to construct a suitable SeekableStream instance whose data is supplied by a given InputStream. The caller, by means of the canSeekBackwards parameter, determines whether support for seeking backwards is required.

Multi-resolution Renderable Images

The MultiResolutionRenderableImage class produces renderings based on a set of supplied RenderedImages at various resolutions.

* public MultiResolutionRenderableImage(Vector renderedSources, float minX, float minY, float height)
constructs a MultiResolutionRenderableImage with given dimensions from a Vector of progressively lower resolution versions of a RenderedImage.

* RenderedImage createScaledRendering(int width, int height, RenderingHints hints)
returns a rendering with a given width, height, and rendering hints.

* RenderedImage createDefaultRendering()
returns a 100-pixel high rendering with no rendering hints.

* RenderedImage createRendering(RenderContext renderContext)
returns a rendering based on a RenderContext.

* Object getProperty(String name)
gets a property from the property set of this image.

* String[] getPropertyNames()
returns a list of the properties recognized by this image.

* float getWidth()
returns the floating-point width of the RenderableImage.

* float getHeight()
returns the floating-point height of the RenderableImage.

* float getMinX()
returns the floating-point minimum x coordinate of the RenderableImage.

* float getMaxX()
returns the floating-point maximum x coordinate of the RenderableImage.

* float getMinY()
returns the floating-point minimum y coordinate of the RenderableImage.

* float getMaxY()
returns the floating-point maximum y coordinate of the RenderableImage.

Image Pyramid

The ImagePyramid class implements a pyramid operation on a RenderedImage. Supposing that we have a RenderedImage of 1024 x 1024, we could generate ten additional images by successively averaging 2 x 2 pixel blocks, each time discarding every other row and column of pixels. We would be left with images of 512 x 512, 256 x 256, and so on down to 1 x 1.

The downSampler is a chain of operations used to derive the image at the next lower resolution level from the image at the current resolution level.

The upSampler is a chain of operations used to derive the image at the next higher resolution level from the image at the current resolution level.

The differencer is a chain of operations used to find the difference between an image at a particular resolution level and the image obtained by first down sampling that image then up sampling the result image of the down sampling operations.

The combiner is a chain of operations used to combine the resulting image of the up sampling operations and the different image saved to retrieve an image at a higher resolution level.

* ImagePyramid(RenderedImage image, RenderedOp downsampler, RenderedOp upSampler, RenderedOp differencer, RenderedOp combiner)
constructs an ImagePyramid object. The parameters point to the last operation in each chain. The first operation in each chain must not have any source images specified; that is, its number of sources must be 0.

* ImagePyramid(RenderedOp downSampler, RenderedOp upSampler, RenderedOp differencer, RenderedOp combiner)
constructs an ImagePyramid object.

* public RenderedImage getImage(int level)
returns the image at the specified resolution level.

* public RenderedImage getDownImage()
returns the image at the next lower resolution level, obtained by applying the downSampler on the image at the current resolution level.

* public RenderedImage getUpImage()
returns the image at the previous higher resolution level.

* public RenderedImage getDiffImage()
returns the difference image between the current image and the image obtained by first down sampling the current image then up sampling the result image of down sampling.

Other Image classes

A RemoteImage is a sub-class of PlanarImage which represents an image on a remote server.

The CollectionImage class is an abstract superclass for classes representing groups of images. Examples of groups of images include pyramids (ImagePyramid), time sequences (ImageSequence), and planar slices stacked to form a volume (ImageStack).

* CollectionImage()
the default constructor.

* CollectionImage(java.util.Collection images)
constructs a CollectionImage object from a Vector of ImageJAI objects.

The ImageSequence class represents a sequence of images with associated timestamps and a camera position. It can be used to represent video or time-lapse photography.

* ImageSequence(Collection images)
constructs a class that represents a sequence of images from a collection of SequentialImage.

The ImageStack class represents a stack of images, each with a defined spatial orientation in a common coordinate system. This class can be used to represent CT scans or seismic volumes.

* ImageStack(Collection images)
constructs an ImageStack object from a collection of CoordinateImage.

* ImageJAI getImage(Coordinate coordinate)
returns the image associated with the specified coordinate.

* Coordinate getCoordinate(ImageJAI image)
returns the coordinate associated with the specified image.

An image MIP map is a stack of images with a fixed operational relationship between adjacent slices. Given the highest-resolution slice, the others may be derived in turn by performing a particular operation. Data may be extracted slice by slice or by special iterators.

The ImageMIPMap class takes the original source image at the highest resolution level, considered to be level 0, and a RenderedOp chain that defines how the image at the next lower resolution level is derived from the current resolution level.

* ImageMIPMap(RenderedImage image, AffineTransform transform, Interpolation interpolation)
This constructor assumes that the operation used to derive the next lower resolution is a standard affine operation.

* ImageMIPMap(RenderedImage image, RenderedOp downSampler)
This constructor specifies the downSampler, which points to the RenderedOp chain used to derive the next lower resolution level.

* ImageMIPMap(RenderedOp downSampler)
This constructor specifies only the downSampler.

Snapshot Image

The SnapshotImage class represents the main component of the deferred execution engine. A SnapshotImage provides an arbitrary number of synchronous views of a possibly changing WritableRenderedImage. SnapshotImage is responsible for stabilizing changing sources to allow deferred execution of operations dependent on such sources.

This implementation of SnapshotImage makes use of a doubly-linked list of Snapshot objects. A new Snapshot is added to the tail of the list whenever createSnapshot() is called.

* SnapshotImage(PlanarImage source)
constructs a SnapshotImage from a PlanarImage source.

* Raster getTile(int tileX, int tileY)
returns a non-snapshotted tile from the source.

* void tileUpdate(java.awt.image.WritableRenderedImage source, int tileX, int tileY, boolean willBeWritable)
receives the information that a tile is either about to become writable, or is about to become no longer writable.

* PlanarImage createSnapshot()
creates a snapshot of this image.

* void dispose()
provides a hint that an image will no longer be accessed from a reference in user space.

Tiled Image

A tile represents all of the storage for its spatial region of the image. If an image contains three bands, every tile represents all three bands of storage. The use of tiled images improves application performance by allowing the application to process an image region within a single tile without bringing the entire image into memory.

TiledImage provides a straightforward implementation of the WritableRenderedImage interface, taking advantage of that interface's ability to describe images with multiple tiles. The tiles of a WritableRenderedImage must share a SampleModel, which determines their width, height, and pixel format.

The contents of a TiledImage are defined by a single PlanarImage source, provided either at construction time or by means of the set() method.

TiledImage also supports direct manipulation of pixels by means of the getWritableTile method. This method returns a WritableRaster that can be modified directly. Such changes become visible to readers according to the regular thread synchronization rules of the Java virtual machine; JAI makes no additional guarantees. When a writer is finished modifying a tile, it should call the releaseWritableTile method. A shortcut is to call the setData() method, which copies a rectangular region from a supplied Raster directly into the TiledImage.

A final way to modify the contents of a TiledImage is through calls to the createGraphics() method. This method returns a GraphicsJAI object that can be used to draw line art, text, and images in the usual AWT manner.

TiledImage does not actually cause its tiles to be computed until their contents are demanded. Once a tile has been computed, its contents may be discarded if it can be determined that it can be recomputed identically from the source. The lockTile() method forces a tile to be computed and maintained for the lifetime of the TiledImage.

* TiledImage(Point origin, SampleModel sampleModel, int tileWidth, int tileHeight)
constructs a TiledImage with a SampleModel that is compatible with a given SampleModel, and given tile dimensions. The width and height are taken from the SampleModel, and the image begins at a specified point.

* TiledImage(SampleModel sampleModel, int tileWidth, int tileHeight)
constructs a TiledImage starting at the global coordinate origin.

* TiledImage(int minX, int minY, int width, int height, int tileGridXOffset, int tileGridYOffset, SampleModel sampleModel, ColorModel colorModel)
constructs a TiledImage of a specified width and height.

* void setData(Raster r)
sets a region of a TiledImage to be a copy of a supplied Raster. The Raster's coordinate system is used to position it within the image.

* void setData(Raster r, ROI roi)
sets a region of a TiledImage to be a copy of a supplied Raster. The Raster's coordinate system is used to position it within the image.

* WritableRaster getWritableTile(int tileX, int tileY)
retrieves a particular tile from the image for reading and writing.

* Raster getTile(int tileX, int tileY)
retrieves a particular tile from the image for reading only.

* boolean isTileWritable(int tileX, int tileY)
returns true if a tile has writers.

* boolean hasTileWriters()
returns true if any tile is being held by a writer, false otherwise.

* void releaseWritableTile(int tileX, int tileY)
indicates that a writer is done updating a tile.

* void set(RenderedImage im)
overlays a given RenderedImage on top of the current contents of the TiledImage.

* void set(RenderedImage im, ROI roi)
overlays a given RenderedImage on top of the current contents of the TiledImage.

* Graphics2D createGraphics()
creates a Graphics2D object that can be used to paint text and graphics onto the TiledImage.

The TileCache interface provides a central place for OpImages to cache tiles they have computed. The tile cache is created with a given capacity (measured in tiles). By default, the tile capacity for a new tile cache is 300 tiles. The default memory capacity reserved for tile cache is 20M bytes.

* static TileCache createTileCache(int tileCapacity, long memCapacity)
constructs a TileCache with the given tile capacity in tiles and memory capacity in bytes.

* static TileCache createTileCache()
constructs a TileCache with the default tile capacity in tiles and memory capacity in bytes.

* void setTileCache(TileCache tileCache)
sets the TileCache to be used by this JAI instance. The tileCache parameter will be added to the RenderingHints of this JAI instance.

* TileCache getTileCache()
returns the TileCache being used by this JAI instance.

A pattern tile consists of a repeated pattern. The pattern operation defines a pattern tile by specifying the width and height; all other layout parameters are optional, and when not specified are set to default values. Each tile of the destination image will be defined by a reference to a shared instance of the pattern.

Planar Image

The PlanarImage class is the main class for defining two-dimensional images. The PlanarImage implements the java.awt.image.RenderedImage interface, which describes a tiled, read-only image with a pixel layout described by a SampleModel and a DataBuffer. The TiledImage and OpImage subclasses manipulate the instance variables they inherit from PlanarImage, such as the image size, origin, tile dimensions, and tile grid offsets, as well as the Vectors containing the sources and sinks of the image.

All non-JAI RenderedImages that are to be used in JAI must be converted into PlanarImages by means of the RenderedImageAdapter class and the WriteableRenderedImageAdapter class. The wrapRenderedImage() method provides a convenient interface to both add a wrapper and take a snapshot if the image is writable. The standard PlanarImage constructor used by OpImages performs this wrapping automatically. Images that already extend PlanarImage will be returned unchanged by wrapRenderedImage().

* PlanarImage()
creates a PlanarImage.

* static PlanarImage wrapRenderedImage(RenderedImage im)
wraps an arbitrary RenderedImage to produce a PlanarImage.

* PlanarImage createSnapshot()
creates a snapshot, that is, a virtual copy of the image's current contents.

* Raster getData(Rectangle region)
returns a specified region of this image in a Raster.

* int getWidth()
returns the width of the image.

* int getHeight()
returns the height of the image.

* int getMinXCoord()
returns the X coordinate of the leftmost column of the image.

* int getMaxXCoord()
returns the X coordinate of the rightmost column of the image.

* int getMinYCoord()
returns the X coordinate of the uppermost row of the image.

* int getMaxYCoord()
returns the X coordinate of the bottom row of the image.

* Rectangle getBounds()
returns a Rectangle indicating the image bounds.

* int getTileWidth()
returns the width of a tile.

* int getTileHeight()
returns the height of a tile.

* int tilesAcross()
returns the number of tiles along the tile grid in the horizontal direction. Equivalent to getMaxTileX() - getMinTileX() + 1.

* int tilesDown()
returns the number of tiles along the tile grid in the vertical direction. Equivalent to getMaxTileY() - getMinTileY() + 1.

Monday, 23 July 2007

Image Load --- 1

The example in JAI program needs WindowContainer, which I downloaded from
http://swjscmail1.java.sun.com/cgi-bin/wa?A2=ind0104&L=jai-interest&D=0&P=28364

JAI supports several image data types, so the DataBuffer class has the following subclasses,
* DataBufferByte - stores data internally as bytes (8-bit values)
* DataBufferShort - stores data internally as shorts (16-bit values)
* DataBufferUShort - stores data internally as unsigned shorts (16-bit values)
* DataBufferInt - stores data internally as integers (32-bit values)
* DataBufferFloat - stores data internally as single-precision floating-point values.
* DataBufferDouble - stores data internally as double-precision floating-point values.

JAI also supports a large number of image data formats, so the SampleModel class provides the following types of sample models:
* ComponentSampleModel - used to extract pixels from images that store sample data in separate data array elements in one bank of a DataBuffer object.
* ComponentSampleModelJAI - used to extract pixels from images that store sample data such that each sample of a pixel occupies one data element of the DataBuffer.
* BandedSampleModel - used to extract pixels from images that store each sample in a separate data element with bands stored in a sequence of data elements.
* PixelInterleavedSampleModel - used to extract pixels from images that store each sample in a separate data element with pixels stored in a sequence of data elements.
* MultiPixelPackedSampleModel - used to extract pixels from single-banded images that store multiple one-sample pixels in one data element.
* SinglePixelPackedSampleModel - used to extract samples from images that store sample data for a single pixel in one data array element in the first bank of a DataBuffer object.
* FloatComponentSampleModel - stores n samples that make up a pixel in n separate data array elements, all of which are in the same bank in a DataBuffer object. This class supports different kinds of interleaving.

The combination of a DataBuffer object, a SampleModel object, and an origin constitute a meaningful multi-pixel image storage unit called a Raster. The Raster class has methods that directly return pixel data for the image data it contains.

There are two basic Raster types:
* Raster - represents a rectangular array of pixels. This is a "read-only" class that only has get methods.
* WritableRaster - extends Raster to provide pixel writing capabilities.

There are separate interfaces for dealing with each raster type:
* The RenderedImage interface assumes the data is read-only and, therefore, does not contain methods for writing a Raster.
* The WriteableRenderedImage interfaces assumes that the image data can be modified.

A ColorModel class provides a color interpretation of pixel data provided by the image's sample model. The combination of a Raster and a ColorModel define a BufferedImage.

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.

There are two separate classes for specifying rendering hints:
* java.awt.RenderingHints - contains rendering hints that can be used by the Graphics2D class, and classes that implement BufferedImageOp and Raster.
* javax.media.jai.JAI - provides methods to define the RenderingHints keys specific to JAI.

Creating Operations

There are four variations on methods for creating operations in the Renderable mode,
* createRenderable
* createRenderableNS
* createRenderable-Collection
* createRenderable-CollectionNS

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

The operation name is always enclosed in quotation marks. The operation name parsing is case-insensitive.

The parameter block contains the source of the operation and a set parameters used by the operation.

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.

There are two separate classes for specifying parameter blocks:
* java.awt.image.renderable.ParameterBlock - the main class for specifying and changing parameter blocks.
* javax.media.jai.ParameterBlockJAI - extends ParameterBlock by allowing the use of default parameter values and the use of parameter names.

Adding Sources to a Parameter BlocK
Example:
ParameterBlock pb = new ParameterBlock();
pb.addSource(im0);
pb.addSource(im1);

There are two separate classes for specifying parameter blocks: ParameterBlock and ParameterBlockJAI.
ParameterBlockJAI automatically provides default parameter values and allows setting parameters by name; ParameterBlock does not.

API: java.awt.image.renderable.ParameterBlock
The operation parameters are added to a ParameterBlock with the ParameterBlock.add() method.

Since the ParameterBlockJAI object already contains default values for the parameters at the time of construction, the parameters must be changed (or set) with the ParameterBlockJAI.set(value, index) methods rather than the add() method.

JAI API Operators

The general categories of image processing operators supported include:
* Point Operators
* Area Operators
* Geometric Operators
* Color Quantization Operators
* File Operators
* Frequency Operators
* Statistical Operators
* Edge Extraction Operators
* Miscellaneous Operators

Basic JAI API Classes

JAI consists of several classes grouped into four packages:
* javax.media.jai - contains the "core" JAI interfaces and classes
* javax.media.jai.iterator - contains special iterator interfaces and classes, which are useful for writing extension operations
* javax.media.jai.operator - contains classes that describe all of the image operators
* javax.media.jai.widget - contains interfaces and classes for creating simple image canvases and scrolling windows for image display

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.

CollectionImage is the abstract superclass for four classes representing collections of PlanarImages:
* ImageStack - represents a set of two-dimensional images lying in a common three-dimensional space, such as CT scans or seismic volumes. The images need not lie parallel to one another.
* ImageSequence - represents a sequence of images with associated time stamps and camera positions. This class can be used to represent video or time-lapse photography.
* ImagePyramid - represents a series of images of progressively lesser resolution, each derived from the last by means of an imaging operator.
* ImageMIPMap - represents a stack of images with a fixed operational relationship between adjacent slices.

The TiledImage class represents images containing multiple tiles arranged into a grid.
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.

The OpImage class is the parent class for all imaging operations, such as:
* AreaOpImage - for image operators that require only a fixed rectangular source region around a source pixel to compute each destination pixel
* PointOpImage - for image operators that require only a single source pixel to compute each destination pixel
* SourcelessOpImage - for image operators that have no image sources
* StatisticsOpImage - for image operators that compute statistics on a given region of an image, and with a given sampling rate
* UntiledOpimage - for single-source operations in which the values of all pixels in the source image contribute to the value of each pixel in the destination image
* WarpOpImage - for image operators that perform an image warp
* ScaleOpImage - for extension operators that perform image scaling requiring rectilinear backwards mapping and padding by the resampling filter dimensions
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.

The RenderableOp class provides a lightweight representation of an operation in the Renderable space. 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.

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

Wednesday, 18 July 2007

Start of JAI programming

Four steps of JAI programming:
1. Get the image data from file or data source;
2. Define the imaging graph;
3. Evaluate the graph using one of three execution models: Rendered execution model, Renderable execution model and Remote execution model;
4. Process the result: save, display, print or send.

Monday, 9 July 2007

Java Advanced Imaging API

The JAI API builds on the foundation of the Java 2D API. The JAI API adds the following concepts:
* Multi-tiled images
* Deferred execution
* Networked images
* Image property management
* Image operators with multiple sources
* Three-dimensional image data

The JAI API is heavily dependent on the abstractions defined in the Java 2D API.

JAI introduces two new data classes,

Class: DataBufferFloat
Extends: DataBuffer

Class: DataBufferDouble
Extends: DataBuffer

DataBufferFloat Class
DataBufferFloat(int size)
DataBufferFloat(int size, int numBanks)
ataBufferFloat(float[] dataArray, int size) //The array must be large enough to hold size elements.
DataBufferFloat(float[] dataArray, int size, int offset) //Only the elements between offset and (offset + size - 1) are available for use by this DataBuffer. The array must be large enough to hold (offset + size) elements.
DataBufferFloat(float[][] dataArray, int size)
DataBufferFloat(float[][] dataArray, int size, int[] offsets)

DataBufferDouble Class
DataBufferDouble(int size)
DataBufferDouble(int size, int numBanks)
DataBufferDouble(double[] dataArray, int size)
DataBufferDouble(double[] dataArray, int size, int offset)
DataBufferDouble(double[][] dataArray, int size)
DataBufferDouble(double[][] dataArray, int size, int[] offsets)

Java Image Data

Image Data Classes,

Class: DataBuffer
Extends: Object

Class: Raster
Extends: Object

Class: SampleModel
Extends: Object

Class: WriteableRaster
Extends: Raster

The basic unit of image data storage is the DataBuffer. The DataBuffer is a kind of raw storage that contains all of the samples for the image data but does not maintain a notion of how those samples can be put together as pixels. The information about how the samples are put together as pixels is contained in a SampleModel. The SampleModel class contains methods for deriving pixel data from a DataBuffer. Together, a DataBuffer and a SampleModel constitute a meaningful multi-pixel image storage unit called a Raster.

A Raster has methods that directly return pixel data for the image data it contains.
* Raster - a read-only object that has only accessors
* WritableRaster - A writable object that has a variety of mutators

There are separate interfaces for dealing with each raster type. The RenderedImage interface assumes that the data is read-only and does not contain methods for writing a Raster. The WritableRenderedImage interface assumes that the image data is writeable and can be modified.

Data from a tile is returned in a Raster object. A tile is not a class in the architecture; it is a concept. In the Java 2D API, the implementation of the WritableRenderedImage (BufferedImage) is defined to have a single tile. This, the getWritableTile method will return all the image data. Other methods that relate to tiling will return the correct degenerative results.

RenderedImages do not necessarily maintain a Raster internally. Rather, they can return requested rectangles of image data in the form of a (Writable)Raster (through the getData, getRect, and get(Writable)Tile methods). This distinction allows RenderedImages to be virtual images, producing data only when needed. RenderedImages do, however, have an associated SampleModel, implying that data returned in Rasters from the same image will always be written to the associated DataBuffer in the same way.

The Java 2D BufferedImage also adds an associated ColorModel, which is different from the SampleModel. The ColorModel determines how the bands are interpreted in a colorimetric sense.

Sunday, 8 July 2007

Java 2D API --- the rendereds

The Rendered layer is designed to work in concert with the Renderable layer. The Rendered layer is comprised of sources and operations for device-specific representations of images or renderings. The Rendered layer is primarily defined by the RenderedImage interface. Sources such as BufferedImage implement this interface.

Operators in this layer are simply RenderedImages that take other RenderedImages as sources. Chains, therefore, can be constructed in much the same manner as those of the Renderable layer. A sequence of RenderedImages is instantiated, each taking the last RenderedImage as a source.

Interface: RenderedImage

Class: BufferedImage
Extends: Image
Implements: WritableRenderedImage

Class: WritableRenderedImage
Extends: RenderedImage

A rendered image represents a virtual image with a coordinate system that maps directly to pixels. A Rendered image does not have to have image data associated with it, only that it be able to produce image data when requested. The BufferedImage class, which is the Java 2D API's implementation of RenderedImage, however, maintains a full page buffer that can be accessed and written to. Data can be accessed in a variety of ways, each with different properties.

Java 2D API --- the renderables

Renderable layer is for simplified image manipulation and it won't directly process image pixels. However, in many cases it is either necessary or desirable to work with pixels and the Rendered layer is used for this purpose.

The renderable layer is primarily defined by the RenderableImage interface. A chain in this layer is a chain of RenderableImages. Specifically, it is a chain of RenderableImageOps (a class that implements RenderableImage), ultimately sourced by a RenderableImage.

There is only one RenderableImageOp class. It is a lightweight, general purpose class that takes on the functionality of a specific operation through a parameter provided at instantiation time. That parameter is the name of a class that implements a ContextualRenderedImageFactory (known as a CRIF, for short). Each instantiation of RenderableImageOp derives its specific functionality from the named class. In this way, the Renderable layer is heavily dependent on the Rendered layer.

Interface: RenderableImage
OR ContextualRenderedImage-Factory
Extends: RenderedImageFactory

Class: ParameterBlock
Extends: Object
Implements: Cloneable, Serializable

Class: RenderableImageOp
Extends: Object
Implements: RenderableImage

Class: RenderableImageProducer
Extends: Object
Implements: ImageProducer, Runnable

Class: RenderContext
Extends: Object
Implements: Cloneable

A Renderable chain is constructed by instantiating each successive RenderableImageOp, passing in the last RenderableImage as the source in the ParameterBlock. This chain can then be requested to provide a number of renderings to specific device spaces through the getImage method. This chain, once constructed, remains editable.

Saturday, 7 July 2007

Java 2D API --- the beginning

The Java 2D API extends AWT's capabilities for both two-dimensional graphics and imaging. The concepts of rendered and renderable images contained in the Java 2D API are essential to JAI.

Rendering independence for images is a poorly understood topic because it is poorly named. The more general problem is "resolution independence," the ability to describe an image as you want it to appear, but independent of any specific instance of it. Resolution is but one feature of any such rendering. Others are the physical size, output device type, color quality, tonal quality, and rendering speed. A rendering-independent description is concerned with none of these.

The Java AWT API architecture provides for two integrated imaging layers: renderable and rendered.

Structurally, the Renderable layer is lightweight. It does not directly handle pixel processing. Rather, it makes use of operator objects from the Rendered layer. This is possible because the operator classes from the Rendered layer can implement an interface (the ContextualRenderedImageFactory interface) that allows them to adapt to different contexts.Many programmers will directly employ the Rendered layer, but the Renderable layer provides advantages that greatly simplify imaging tasks.

The renderable layer allows for the construction of a chain of operators (RenderableImageOps) connected to a RenderableImage source. The end of this chain represents a new RenderableImage source. The implication of this is that RenderableImageOps must implement the same interface as sources: RenderableImageOp implements RenderableImage.

Friday, 6 July 2007

AWT for images --- Push Model

I almost forgot why I use bsc. To remind myself, I put a note here. BSC = Budget SuperComputer.

AWT push model I do not understand why it is called push model. Anyway, let me carry on, here are some major classes, Image, ImageProducer, ImageConsumer, and ImageObserver.

It is from java.awt class package. An Image object is obtained from some source.

Image im = getToolkit().getImage(ImageFileName);

The ultimate destination for a filtered image is an AWT Image object, created by a call to, for example, Component.createImage(). Once this consumer image has been created, it can by drawn upon the screen by calling Image.getGraphics() to obtain a Graphics object (such as a screen device), followed by Graphics.drawImage().

AWT Imaging was largely designed to facilitate the display of images in a browser environment. In this context, an image resides somewhere on the network. There is no guarantee that the image will be available when required, so the AWT model does not force image filtering or display to completion. The model is entirely a push model. An ImageConsumer can never ask for data; it must wait for the ImageProducer to "push" the data to it. Similarly, an ImageConsumer has no guarantee about when the data will be completely delivered; it must wait for a call to its ImageComplete() method to know that it has the complete image. An application can also instantiate an ImageObserver object if it wishes to be notified about completion of imaging operations.

Cake club

I could not reject the nice taste cake, so I joined the cake club. It is not possible to be a taster all the time, so I have to look for some recipe to make a nice cake or relative product for others.

Search for cake ......
http://www.sofeminine.co.uk/m/food/chocolate-cake-recipe.html

sofeminine, I just cannot believe this. So many good names, why it chose to use this. So many different names for cake, make me completely lost.

Now I have to add a TAG of bakery.

Java programming

C, I've used it in my PhD and most of my study. I like it and I think it is quite good. Now it is time to have a look another option, Java. Is it good? Let me try.

It is quite simliar as C/C++, so it is no point to introduce it here. A good book to read is The Complete Reference Java. If you do not know C/C++, again The Complete Reference C and The Complete Reference C++.

My reference for JAI is http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/