Image Manipulation Requirements
In Chapter 6 we enumerated various display requirements and designed an interface based on these requirements. Let's continue the discussion and extend it to incorporate the image manipulation requirements. These requirements will be the basis for designing interfaces to specify image manipulation methods.
Although image manipulation may mean different things to different people, certain common operations are performed by many imaging applications, including pan, zoom, rotate, and flip. In this chapter we'll consider each of these in turn. First, however, we need a canvas that can display manipulated images. To develop such a canvas, let's consider the following two approaches:
Using the ImageCanvas class. In this case image manipulation must be performed in separate class(es), and the resulting image is displayed on ImageCanvas. This approach is simple and works well for noninteractive operations. In interactive operations, however, the current transformation and other information must be retained. So a separate class is needed to manage such things.
Creating a new class by extending the ImageCanvas class and implementing all the manipulation functions in it. This is not a good solution because not all manipulation functions are required in an application. Even if they are needed, they may not be needed at a given time.
What is desirable is a flexible design that helps build only the desired manipulation functions at runtime. To achieve this, we'll adopt an approach that is a hybrid of the two approaches just presented. In this hybrid approach we'll build a controller class for each manipulation function. This controller class will operate on the image that is to be manipulated in the canvas. With this approach, an application needs to construct only required controller objects at any time.
Another important point to consider is the GUI required for generating manipulation parameters. The GUI and the controller classes must be clearly separate. The reason for this requirement is obvious: An application must be able to choose its own GUI for generating manipulation parameters.
The design pattern here is that the manipulation GUI feeds the data to the controller object, which in turn operates on the image canvas to manipulate the image. Often the manipulation GUI may need to capture some events from the image canvas. For example, mouse events are often needed to get the cursor position.
We'll design our interfaces from a user's point of view. First, let's design the ImageManipulator interface, which will be implemented by a component class to support image manipulation functionality. Such functionality includes pan, zoom, rotate, shear, and flip. For each of the manipulation types, we can define a property, the value of which can be set and retrieved by the clients of ImageManipulator objects.
In addition, we'll allow the client objects to choose an interpolation mechanism through a property called interpolationType. Currently, the AffineTransformOp class supports only two types of interpolation techniques: nearest-neighbor and bilinear. The interpolationType property can assume either of two values: Affine TransformOp.TYPE_BILINEAR and AffineTransformOp.TYPE_NEAREST_NEIGHBOR.
Because images are manipulated through the affine transformation, we need to specify the current transformation of the canvas as a property. The current transformation can be concatenated outside the canvas. The controller objects need to get and set this property. For example, a client program can allow the user to set the rotation angle in an external GUI. This program can send the rotation angle to the rotation controller object, which will generate a transformation matrix for the rotation angle selected by the user. The rotation controller will concatenate this transformation with the current transformation obtained from the image canvas. It will then set the resulting transformation to be the current transformation of the image canvas. Table 7.1 summarizes the image manipulation properties.
Besides the set and get methods for the properties shown in Table 7.1, the ImageManipulator canvas requires two more methods:
A method for applying the transformation
A method for resetting manipulation
Current Transformation
Client objects require the current transformation (i.e., AffineTransform object) for several purposes. Applying the manipulation externally is one. All the manipulation features discussed in this chapter check out the current transformation, concatenate it, and put it back. Ideally, the current transformation should be locked so that only one program updates it at a given time.
TABLE 7.1 Image Manipulation Properties
Property |
Type |
Description |
interpolationType |
int |
Interpolation type; one of AffineTransformOp.TYPE_NEAREST_NEIGHBOR or AffineTransformOp.TYPE_BILINEAR |
magFactor |
double |
Magnification factor |
panOffset |
Point |
Translation |
rotationAngle |
double |
Rotation |
shearFactor |
double |
Shear |
transform |
AffineTransform |
Current transformation |
The current transformation is required for other purposes as well. For example, in region of interest (ROI) computations, the current transformation matrix is needed to convert the region (marked by the user) from user space to image space. So let's specify the current transformation as a property. The set and get methods for this property are
public AffineTransform getTransform()
public void setTransform(AffineTransform atx)
Resetting Manipulation
After you manipulate an image, normally you will start over again. So the client objects need a method for resetting the manipulation to a default value. Let's specify the following method for this purpose:
public void resetManipulation();
Listing 7.3 shows the code for the ImageManipulator interface.
LISTING 7.3 The ImageManipulator interface
public interface ImageManipulator extends ImageDisplay { public void setPanOffset(Point panOffset); public Point getPanOffset(); public void setMagFactor(double magFactor); public double getMagFactor(); public void setRotationAngle(double theta); public double getRotationAngle(); public void setShearFactor(double shear); public double getShearFactor(); public AffineTransform getTransform(); public void setTransform(AffineTransform at); public void applyTransform(AffineTransform atx); public void setInterpolationType(int interType); public int getInterpolationType(); public void resetManipulation(); }
Any image-drawing component can implement ImageManipulator. Keep in mind that the components that implement this interface don't manipulate the image. The manipulation is actually performed by an external program. This canvas only holds the properties, applies the transformation, and paints the image.
Not all types of manipulations are required by an application. By separating the actual manipulation from the component, the image manipulation canvas does not have to carry the code to perform different types of manipulation. Instead, the manipulation code is in separate classes or beans.