Home : Course Map : Chapter 11 : Java : Supplements :
AffineTransform Filter
JavaTech
Course Map
Chapter 11

Introduction
Image Class
Image Loading
  Demo 1 Demo 2  
Pixels/Transparency
  Demo 3
Pixel Handling
  Demo 4  
Demo 5
Exercises

    Supplements
Java 2D Imaging
BufferedImage
Creating Buf.Image
Pixel Handling
  Demo 1 Demo 2
Filters
  Convolutions
     Demo 3
  AffineTransforms
     Demo 4
  LookupTable
     Demo 5 Demo 6
  Rescale
     Demo 7
  Color Conversion
     Demo 8
  Custom
     Demo 9
Exercises
Java Adv Imaging
AWT Flicker:
  Override Update
     Demo 1  Demo 2
  Clipping
     Demo 3
  Double Buffer
     Demo 4

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

The AffineTransforms map 2D structures in one space to another space while maintaining straight lines and the parallelism in the original image. The operations include translation, scaling, flipping, rotation, and shearing.

In Chapter 6: Supplements: Java 2D review we discussed the application of affine transforms to shapes drawn with the Graphics2D tools. Here we see that such transforms can be applied to images.

An AffineTransformOp object uses an instance of AffineTransform to apply the transform to a source image to create a new destination image (they must be different BufferedImage objects.) For example, the following code shows how to apply a shearing operation to an image:   

  AffineTransform shearer = AffineTransform.getShearInstance (shx, shy);
  AffineTransFormOp shear_op = new AffineTransformOp (shearer, interpolation );
  BufferedImage dest_image = shearOp.filter (source_image, null);

Each point in the source image at (x,y) will move to (x + shx*y, y + shy * x) in the destination image. After an transform, a pixel in a destination image will usually not correspond directly to a particular pixel in the source. So the transforms require an interpolation algorithm to determine the colors of the destination pixels.

The interpolation setting in the second argument of the constructor allow you to choose among a nearest neighbor algorithm, a bilinear interpolation algorithm, and a bicubic interpolation algorithm became effective with Java 5.0. The AffineTransformOp class offers three static constants to choose from for the interpolation setting:

  • AffineTransformOp.TYPE_NEAREST_NEIGHBOR - The nearest neighbor technique applies the color of the nearest transformed source pixel to the destination pixel.

  • AffineTransformOp.TYPE_BILINEAR - The bilinear interpolation instead uses a combination of colors from a set of transformed source pixels around the position of the destination pixel. See the description of a bilinear algorithm in the specification for javax.media.jai.InterpolationBilenear.

  • AffineTransformOp.TYPE_BICUBIC -this is a new algorithm implemented in J2SE5.0 that uses a "performs interpolation using the piecewise cubic polynomial". See the description of a bicubic algorithm in the specification for javax.media.jai.InterpolationBicubic.

If the value of the interpolation argument in the AffineTransformOp constructor is set to null, then the nearest neightbor algorithm is used.

Note also that these transforms can result in cutting off some parts of the source image that extend past the borders of the destination image. Also, some operations, such as a rotation, can leave some areas black where no image data remains.

The TransformsApplet shown below demonstrates the shear transform operation on an image. The user enters the x and y shear factors via two sliders.

TransformApplet
Resources: saturnVoyager.jpg

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.event.*;
import java.awt.geom.*;

/** Demonstrate an affine transform of an image. **/
public class TransformApplet extends JApplet
                          implements ChangeListener {

  BufferedImage fSrcImage, fDstImage;
  ImageIcon fDstIcon;
  JLabel fShxLabel, fShyLabel;
  JSlider fShxSlider,fShySlider;
  double fShxFactor = 0.0;
  double fShyFactor = 0.0;

  /**
    * Build the interface with the source and filter output image
    * displayed side by side.
   **/
  public void init () {

    setLayout (new BorderLayout ());

    fSrcImage = getBufImage ("saturnVoyager.jpg");
    if (fSrcImage == null) {
        System.out.println ("Error in reading image file!");
        return;
    }

    JPanel control_panel = new JPanel(new GridLayout (2,1));
    control_panel.add (fShxLabel =
                       new JLabel ("shx 0 ",SwingConstants.RIGHT) );
    control_panel.add (fShxSlider =
                       new JSlider (Adjustable.HORIZONTAL, 0, 10, 0));
    fShxSlider.addChangeListener (this);

    control_panel.add (fShyLabel =
                       new JLabel ("shy 0 ",SwingConstants.RIGHT) );
    control_panel.add (fShySlider =
                       new JSlider (Adjustable.HORIZONTAL, 0, 10, 0));
    fShySlider.addChangeListener (this);

    // Transform image with default settings.
    transformFilter();

    // Use ImageIcon objects to hold the two images
    ImageIcon src_icon = new ImageIcon (fSrcImage);
    fDstIcon = new ImageIcon (fDstImage);

    // Put the icons on labels
    JLabel src_display = new JLabel (src_icon);
    JLabel dst_display = new JLabel (fDstIcon);

    // And then display the labels on the scroll panes
    JScrollPane src_pane = new JScrollPane (src_display);
    JScrollPane dst_pane = new JScrollPane (dst_display);

    // Use a JSplitPane to show the source and destination
    // images side by side.
    JSplitPane split_pane =
      new JSplitPane (JSplitPane.HORIZONTAL_SPLIT,
                      true, src_pane, dst_pane);

    split_pane.setResizeWeight (0.5);
    split_pane.setContinuousLayout (true);

    // Add the DrawingPanel to the contentPane.
    add (split_pane, BorderLayout.CENTER);
    add (control_panel, BorderLayout.SOUTH);

  } // init

  /**
    * This class implements ChangeListener for the
    * slider. So the ChangeEvents come here when the
    * slider is moved.
   **/
  public void stateChanged (ChangeEvent evt) {

    // Use the label to show the numerical value of the slider.
    fShxLabel.setText ("shx " + fShxSlider.getValue ());

    // Get the values from the slider and set a new scale factor.
    fShxFactor = fShxSlider.getValue ();

    // Use the label to show the numerical value of the slider.
    fShyLabel.setText ("shy " + fShySlider.getValue ());

    // Get the values from the slider and set a new scale factor.
    fShyFactor = fShySlider.getValue ();

    // Transform the image.
    transformFilter();

    // Reset the destination images with the modified version.
    fDstIcon.setImage(fDstImage);

    // Repaint to show the modified image.
    repaint ();

  } // stateChanged

  /** Create the filter to execute an affine transform amd
    * apply it to the source image.
   **/
  void transformFilter () {

    // Some extreme combinations of filters can result in
    // invalid operations so catch the resulting exceptions
    // and reset images to the default.
    try {
      AffineTransform shearer =
        AffineTransform.getShearInstance (fShxFactor, fShyFactor);
      AffineTransformOp shear_op =
        new AffineTransformOp (shearer, null);
      fDstImage = shear_op.filter (fSrcImage, null);
    }
    catch (Exception e) {
      System.out.println("Shearing exception = " + e);
      // Reset back to default
      AffineTransform shearer =
        AffineTransform.getShearInstance (0.0, 0.0);
      AffineTransformOp shear_op =
        new AffineTransformOp (shearer, null);
      fDstImage = shear_op.filter (fSrcImage, null);
    }
  } // transformFilter

  /**
    *  Download the image file and convert to a
    *  BufferedImage object.
   **/
  BufferedImage getBufImage (String image_name){

    // Get the image
    Image img = getImage (getCodeBase(), image_name);

    // and use a MediaTracker to load it before converting it to
    // a BufferedImage.
    try {
      MediaTracker tracker = new MediaTracker (this);
      tracker.addImage (img,0);
      tracker.waitForID (0);
    } catch (InterruptedException e) { return null; }

    int width = img.getWidth (this);
    int height= img.getHeight (this);

    BufferedImage buffered_image =
      new BufferedImage (width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = buffered_image.createGraphics ();
    g2.drawImage (img,0 ,0, null);

    return buffered_image;

  } // getBufImage

} // class TransformApplet

It will be left as an exercise to demonstrate the other types of affine transforms.

References & Web Resources

 

Latest update: March 8, 2006

              Tech
Fractals
Fractal Drawing
   Demo 1
Fractal Draw Code
Fractal Images
  Demo 2
Image Processing
  Demo 3
Histogram Image
  Demo 4
Exercises

           Physics
Calibration/SysError
SimWithCal/SysErr
  Demo 1
Analysis
  Demo 2
Examples

Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.