- Image Filters
- Processing an Image with BufferedImageOp
- AffineTransformOp
- ColorConvertOp
- ConvolveOp
- LookupOp
- RescaleOp
- Custom BufferedImageOp
- A Note about Filters Performance
- Summary
Processing an Image with BufferedImageOp
Filtering a BufferedImage can be done onscreen at painting time or offscreen. In both cases, you need a source image and an operation, an instance of BufferedImageOp. Processing the image at painting time is the easiest approach; here is how you might do it:
// createImageOp returns a useful image filter BufferedImageOp op = createImageOp(); // loadSourceImage returns a valid image BufferedImage sourceImage = loadSourceImage(); @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; // Filter the image with a BufferedImageOp, then draw it g2.drawImage(sourceImage, op, 0, 0); }
You can filter an image at painting time by invoking the drawImage(BufferedImage, BufferedImageOp, int, int) method in Graphics2D that filters the source image and draws it at the specified location.
Here is an example of how to preprocess an image by doing all the operations offscreen:
BufferedImageOp op = createImageOp(); BufferedImage sourceImage = loadSourceImage(); BufferedImage destination; destination = op.filter(sourceImage, null);
Calling the filter() method on a BufferedImageOp triggers the processing of the source image and the generation of the destination image. The second parameter, set to null here, is actually the destination image, which, when set to null, tells the filter() method to create a new image of the appropriate size. You can, instead, pass a non-null BufferedImage object as this parameter to avoid creating a new one on each invocation. Doing so can save performance by reducing costly image creations.
The following code example shows how you can optimize a routine applying the same filter on several images of the same size:
BufferedImageOp op = createImageOp(); BufferedImage[] sourceImagesArray = loadImages(); BufferedImage destination = null; for (BufferedImage sourceImage : sourceImagesArray) { // on the first pass, destination is null // so we need to retrieve the reference to // the newly created BufferedImage destination = op.filter(sourceImage, destination); saveImage(destination); }
After the first pass in the loop, the destination will be non-null and filter() will not create a new BufferedImage when invoked. By doing so, we also make sure that the destination is in a format optimized for the filter, as it is created by the filter itself.
Processing an image with Java 2D is an easy task. No matter which method you choose, you will need to write only one line of code. But we haven't seen any concrete BufferedImageOp yet and have just used an imaginary createImageOp() method that was supposedly returning a useful filter. As of Java SE 6, the JDK contains five implementations of BufferedImageOp we can rely on to write our own filters: AffineTransformOp, ColorConvertOp, ConvolveOp, LookupOp, and RescaleOp.
You can also write your own implementation of BufferedImageOp from scratch if the JDK does not fulfill your needs. Before learning how to write your own, let's take a closer look at what the JDK has to offer. Each filter we investigate will be applied to the sample picture shown in Figure 8-3 to give you a better idea of the result.
Figure 8-3 The image used in our filter examples.
The complete source code for all the examples can be found on this book's Web site in the project named ImageOps.