JAI IO

One of the most useful aspects of JAI is the ability to easily read and write image data. Images can be loaded into a PlanarImage if they are contained in a formatted image file, a nonformatted image file, or a BufferedImage. Likewise, the data contained in a PlanarImage can be written into a file as either formatted or unformatted data or the PlanarImage can be converted into a BufferedImage.

File Operator

There are an assortment of operators for reading a specific image format such as BMP, GIF, FPX, JPEG, PNG, PNM, and TIFF (see Table 6.11).

Note

The IO for JAI is built on the Image IO package discussed in Chapter 5, so there shouldn't be a discrepancy between the file formats the Image IO package can decode/encode and the file formats the JAI package can decode/encode.


In Table 6.11, the list of IO operators is presented. Note that many of them allow optional encoder or decoder objects in order to provide more control over reading and writing images of specific formats.

Table 6.11. Summary of File Operators
Operator Parameter Block Format/Description
AWTImage
add(java.awt.Image image);

The AWTImage operator converts a java.awt.Image into a PlanarImage.
BMP
add(com.sun.media.jai.codec.SeekableStream 
stream);

The BMP operator decodes the bmp image contained in stream.
Encode
addSource(PlanarImage pi);
add(java.io.OutputStream stream);
add(String format);
add(com.sun.media.jai.codec.ImageEncodeParam 
param);

The Encode operator encodes pi onto stream using given format and encoding parameters. The default value for format is "tiff", and the default value for param is null.
FileLoad
add(String filename);
add(com.sun.media.jai.codec.ImageDecodeParam 
param);
add(boolean checkFileLocally);

The FileLoad operator decodes the image contained in file filename using the given decoding parameters. The image format isn't specified. When loading remote files, checkFileLocally should be set to false; otherwise, an IllegalArgumentException will be thrown when the file isn't found on the local file system. The default value for param is null, and the default value for checkFileLocally is true.
FileStore
addSource(PlanarImage pi);
add(String filename);
add(String format);
add(com.sun.media.jai.codec.ImageEncodeParam 
param);

The FileStore operator encodes pi into file filename using given format and encoding parameters. The default value for format is "tiff" and the default value for param is null.
FPX
add(com.sun.media.jai.codec.SeekableStream 
stream);
add(com.sun.media.jai.codec.FPXDecodeParam param);

The FPX operator decodes the fpx image contained in stream. The default value of param is null.
GIF
add(com.sun.media.jai.codec.SeekableStream 
stream);

The GIF operator decodes the gif image contained in stream.
IIP
add(String url);
add(int[] subImages);
add(float filter);
add(float contrast);
add(Rectangle2D.Float sourceROI);
add(AffineTransform transform);
add(float aspectRatio);
add(Rectangle2DFloat destROI);
add(int rotation);
add(String mirrorAxis);
add(ICC_Profile iccprofile);
add(int jpegquality);
add(int jpegtable);

The IIP operator creates a java.awt.image.RenderedImage or a java.awt.image.renderable.RenderableImage based on the data received from the Internet Imaging Protocol (IIP) server. It can optionally apply a sequence of operations to the created image. Refer to the IIP specifications found at http://www.digitalimaging.org for more complete information on this operator.
IIPResolution
add(String url);
add(int resolution);
add(int subImage);

The IIPResolution operator requests from the IIP server an image located at url with a resolution level of resolution. It then creates a java.awt.image.RenderedImage based on the data received from the server. The default value for resolution is IIPResolutionDescriptor.MAX_RESOLUTION, and the default value for subImage is 0.
JPEG
add(com.sun.media.jai.codec.SeekableStream 
stream);

The JPEG operator decodes the jpeg image contained in stream.
PNG
add(com.sun.media.jai.codec.SeekableStream 
stream);
add(com.sun.media.jai.codec.PNGDecodeParam param);

The PNG operator decodes png images contained in stream. The default value of param is null.
PNM
add(com.sun.media.jai.codec.SeekableStream 
stream);
add(com.sun.media.jai.codec.PNGDecodeParam param);

The PNM operator decodes the pnm image contained in stream. The default value of param is null.
Stream
add(com.sun.media.jai.codec.SeekableStream 
stream);
add(com.sun.media.jai.codec.ImageDecodeParam 
param);

The Stream operator decodes the image contained in stream. The image format isn't specified. The default value of param is null.
TIFF
add(com.sun.media.jai.codec.SeekableStream 
stream);
add(com.sun.media.jai.codec.TIFFDecodeParam 
param);
add(int page);

The TIFF operator decodes page page of the tiff image contained in stream. The default value of param is null, and the default value of page is 0.
URL
add(java.net.URL url);
add(com.sun.media.jai.codec.ImageDecodeParam 
param);

The URL operator decodes the image contained in url. The image format isn't specified. The default value of param is null.

There are also three main operators for reading images without specifying an image format. They are the FileLoad, Stream, and URL operators. These operators examine the image bytes to decode the image format and then call the appropriate operator—that is, TIFF, GIF, and so on. Listing 6.13 shows all three operators being used to read the same image data. Writing formatted image data is done by using the FileStore and Encode operators, which write out image data of a specified format to a file or to a stream, respectively.

Listing 6.13 ImageLoadTester
package ch6;

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.awt.image.renderable.ParameterBlock;
import javax.media.jai.RenderedOp;
import javax.media.jai.JAI;
import com.sun.media.jai.codec.SeekableStream;

/**
   ImageLoadTester -- loads an image using the JAI class' create method.
   This method uses 3 different operator names to load the image
   in 3 different manners, namely "stream", "fileload" and "url"

   It then prints out the properties of the loaded image
 */
public class ImageLoadTester extends JFrame {

    public ImageLoadTester(String filename) {
        SeekableStream stream = null;
        URL url = null;

        try {
            url = new URL("file:"+filename);
            stream = SeekableStream.wrapInputStream(url.openStream(),
                                                    false);
        }
        catch (IOException ioe) {
            System.err.println("IOException: " + ioe.getMessage());
        }

        params = new ParameterBlock();
        params.add(stream);
        RenderedOp streamImage = JAI.create("stream", params);

        params = new ParameterBlock();
        params.add(filename);
        RenderedOp fileImage = JAI.create("fileload", params);

        params = new ParameterBlock();
        params.add(url);
        RenderedOp urlImage = JAI.create("url", params);

        //Display Image Properties
        String[] props = streamImage.getPropertyNames();
        for (int i=0;i<props.length;i++) {
            System.out.print("Property: " + props[i] + ", ");
            System.out.println(streamImage.getProperty(props[i]));
        }

        getContentPane().setLayout(new GridLayout(1, 3));
        getContentPane().add(new ch6Display(streamImage));
        getContentPane().add(new ch6Display(fileImage));
        getContentPane().add(new ch6Display(urlImage));

        pack();
        show();
    }

    public static void main(String[] args) {
       if (args.length != 1)
            System.err.println("USAGE:  ImageLoadTester filename");
        else
            new ImageLoadTester(args[0]);
    }
    private ParameterBlock params;
}
						

Reading Unformatted Images

If there is no defined format for an image or the JAI decoders cannot decode the image's format, you'll need to convert the raw pixel data into a PlanarImage yourself. Listing 6.14 illustrates how to read in a series of tiles concatenated into a single image. A common situation in medical imaging occurs when a series of slices are concatenated into a single image file. In this listing, the tiles are assumed to be composed of a single band containing float data.

Listing 6.14 FloatViewer
package ch6;

import javax.media.jai.*;
import java.awt.*;
import java.awt.color.*;
import java.awt.image.*;
import java.awt.image.renderable.*;
import javax.swing.*;
import java.io.*;

/**
   FloatViewer.java - reads a image file composes of float values,
   scales it for display purposes and displays it.

   This image file can be composed of any number of different images
   as long as they are the same dimensions (referred to as tileWidth
   and tileHeight);
*/
public class FloatViewer extends JFrame {
    public FloatViewer(String filename, String tileWidth,
                       String tileHeight) {
        File f = new File(filename);
        if ( !(f.exists()) ) {
            System.err.println("File: " + filename + " does not exist ");
            System.exit(1);
        }
        this.tileWidth = Integer.parseInt(tileWidth);
        this.tileHeight = Integer.parseInt(tileHeight);

        float[][] dataArray = getData(f);
        PlanarImage inputImage = getTiledImage(dataArray);

        double scaleFactor = findScale(inputImage);
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(inputImage);
        pb.add(new double[] {1.0/scaleFactor});
        pb.add(new double[] {0.0});

        RenderedOp scaledImage = JAI.create("rescale", pb);
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(new JScrollPane(new ch6Display(scaledImage)),
                             BorderLayout.CENTER);

        show();
        pack();
    }

    /**
       provides a scale factor for displaying the image data.
    */
    private double findScale(PlanarImage pi) {
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(pi);
        PlanarImage pi2 = JAI.create("extrema", pb);
        double[] maximum = (double[])pi2.getProperty("maximum");
        double maxValue = maximum[0];
        return maxValue/2.0;
    }

    /**
       reads the image data from the file and stores it in an
       array of javax.media.jai.DataBufferFloat elements
       with each tile being stored in a
       separate javax.media.jai.DataBufferFloat element.
    */
    private float[][] getData(File file) {
        numberFloats = (int)(file.length()/4); //4 bytes per float
        numberFloatsPerTile = tileWidth*tileHeight;
        numberTiles = numberFloats/numberFloatsPerTile;

        float[][] floatArray = null;
        floatArray = new float[numberTiles][numberFloats];

        try {
            FileInputStream fis = new FileInputStream(file);
            BufferedInputStream bis;
            bis = new BufferedInputStream(fis, numberFloatsPerTile*4);
            DataInputStream dis = new DataInputStream(bis);
            for (int i=0;i<numberTiles;i++) {
                for (int j=0;j<numberFloatsPerTile;j++)
                    floatArray[i][j] = dis.readFloat();
            }
            dis.close();
        }
        catch (IOException ioe) {
            System.err.println("IO Exception: " + ioe.getMessage());
            System.exit(1);
        }
        return floatArray;
    }

    /**
       creates a TiledImage from an array of
       javax.media.jai.DataBufferFloats
    */
    private PlanarImage getTiledImage(float[][] dataArray) {
        int numberHorizontalTiles;
        int numberVerticalTiles;

        // calculate number of tiles per column and per row
        double tmp = Math.sqrt((double)numberTiles);
        numberVerticalTiles = (int)tmp;
        numberHorizontalTiles = numberTiles/numberVerticalTiles;
        while (numberHorizontalTiles*numberVerticalTiles < numberTiles)
            numberHorizontalTiles++;

        imageWidth = numberHorizontalTiles*tileWidth;
        imageHeight = numberVerticalTiles*tileHeight;

        SampleModel inputImageSM;
        int dt = DataBuffer.TYPE_FLOAT;
        inputImageSM = RasterFactory.createBandedSampleModel(dt,
                                                             tileWidth,
                                                             tileHeight,
                                                             1);

        ColorModel inputImageCM;
        inputImageCM = PlanarImage.createColorModel(inputImageSM);

        /*
          create TiledImage -- note that the tile dimensions come from
          the SampleModel
        */
        TiledImage ti = new TiledImage(0, 0,
                                       imageWidth, imageHeight,
                                       0, 0,
                                       inputImageSM,
                                       inputImageCM);

        // now load the data into the TiledImage
        WritableRaster wr;
        int index = 0;
        for (int j=0;j<numberVerticalTiles;j++)
            for (int i=0;i<numberHorizontalTiles;i++)
                if (index < numberTiles) {
                    wr = ti.getWritableTile(i, j);
                    wr.setPixels(i*tileWidth, j*tileHeight,
                                 tileWidth, tileHeight,
                                 dataArray[index]);
                    index++;
                }
        return ti;
    }


    static public void main(String[] args) {
        if (args.length != 3) {
            System.err.print("USAGE:  FloatViewer ");
            System.err.println("floatImageFile tileWidth tileHeight");
        }
        else
            new FloatViewer(args[0], args[1], args[2]);
    }

    private int numberFloats;
    private int numberTiles;
    private int numberFloatsPerTile;
    private int tileWidth;
    private int tileHeight;
    private int imageWidth;
    private int imageHeight;
}
						

Caution

Running Listing 6.14 might cause an OutOfMemoryError error to occur. The easiest way to avoid this is to increase the amount of memory allocated to the JVM. In order to do this, use something like the following:

java –Xms32m –Xmx128m classFilename

This increases the initial memory allocation from 4 megabytes to 32 megabytes and the maximum memory allocation from 16 megabytes to 128 megabytes.


Converting to and from Images and Buffered Images

As we have previously discussed, some of the functionality contained within the PlanarImage class is available in a BufferedImage, although usually to a lesser extent. Other PlanarImage features don't exist in the BufferedImage at all. Thus, you need to perform some type of conversion on a BufferedImage in order to use it with the JAI package. To do this, convert the BufferedImage to a RenderedImageAdapter class using the following constructor:

public RenderedImageAdapter(RenderedImage src)

Because the RenderedImageAdapter class is a subclass of the PlanarImage class, it can be used wherever the PlanarImage class is expected. (Basically it is a PlanarImage with no sources.) When creating a RenderedImageAdapter in this manner, the data from the BufferedImage is copied so subsequent changes to the BufferedImage won't affect the RenderedImageAdapter.

Note

Because both a BufferedImage and a PlanarImage implement the RenderedImage interface, they can both be referred to as a RenderedImages. The RenderedImageAdaptor is “idempotent” however, meaning that a BufferedImage will be converted to a PlanarImage, but a PlanarImage will be unchanged.


To go from a PlanarImage to a BufferedImage, you can simply use the PlanarImage's getAsBufferedImage method, which returns a copy of the PlanarImage's data contained in a BufferedImage.

In some cases, you will have a java.awt.Image object that needs to be converted into a PlanarImage. In this case you can use the AWTImage operator mentioned in Table 6.11.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset