2. Building an Image Processor

For this example, we'll build a tool extension of YOPS that manipulates images. First, let's define the idea of a tool in YOPS. A tool is an object that can receive events from YOPS and provide methods to operate on objects in the YOPS interface (e.g., graphics components, panels with images, etc.).

Tools do not have to implement a specific interface or extend a particular class. The capabilities of tools are determined using Java reflection. This is what allows students write simple methods that do interesting things with images and other graphical components.

So to create an Image Processor tool, we need to create a class called ImageProcessor and add it to a YOPS object. To have this tool operate on images in panels, we will provide simple methods that YOPS can reflect on and use. Here is a shell ImageProcessor class for this example:

import yops.*;

public class ImageProcessor {
   public static void main(String args[]) {
      new YOPS(new ImageProcessor(), 250, 400, 3);
   }
}

Using another YOPS constructor, we create a YOPS object with 3 panels, each with a width of 250 and a height of 400, but also with an Image Processor tool added in. This code produced the following YOPS window:

(Screen Shot)

So now we have a YOPS object with a tool whose button is poorly labeled and doesn't do anything. To fix the labeling, we need to override toString of the ImageProcessor class as that determines what text appears on the button of the tool.

import yops.*;

public class ImageProcessor {
   public String toString() {
      return "Image Processor";
   }

   public static void main(String args[]) {
      new YOPS(new ImageProcessor(), 250, 400, 3);
   }
}

To provide some functionality to our Image Processor tool, we need to write methods that YOPS can reflect on and understand how to use. Once such a method is found, YOPS will add the method to the Methods menu.

In order to understand how to write these methods it's important to know the major objects YOPS is concerned with. The major types of objects in the YOPS interface are:

  • GraphicsPanel objects which are the panels that are created and displayed in the YOPS window
  • Image objects which are bitmaps of Color objects which are contained in GraphicsPanel objects
  • Color objects which represent each pixel in an Image object as red, green, and blue int color components.
  • Color components, represented as integers, or the individual red, green, and blue component of each color object in an Image.
When writing methods that YOPS can reflect on, YOPS will look at the parameters and the return type of a method to determine if it knows how to use it. Specifically, parameters must be of the types described above and the return types must match. For instance, if we want to write a method that takes an Image and turns it entirely black, we can write a very simple method for the ImageProcessor:
public int allBlack(int c) {
   return 0;
}

Because this method has an int parameter and an int return type, when the method name is selected from the Methods menu in YOPS, it will be invoked for every color component of an Image, passing in the value for every red, green, and blue color component through the parameter c. The value that is returned then replaces the value of that color component in another Image. For this example, every color component of every Color object in an Image object is replaced with the value of 0, making every Color object have a red value of 0, a green value of 0, and a blue value of 0, which corresponds to black.

As this method has only one parameter, the values passed in through the parameter c correspond to the color components of the left-most panel (the first panel). Consequently, the values returned are stored as color components in the panel immediately to the right of the last panel used for input (the second panel in this case). So adding this method to our ImageProcessor, and exeucting in YOPS by going to Methods > allBlack will produce the following result:

(Screen Shot)

If we want to write a method that produces a blue image rather than black, we can't do this by modifying all the color components as we did before (well, not in a "clean" manner anyway). This would be easier if we could manipulate the Color objects which represent the pixels. So we only need to create a method called allBlue which operates on Color objects and returns Color objects for YOPS to recognize it. Futher, let's have it turn the third panel blue instead of the second. Here is allBlue for our ImageProcessor:

// Need to add 'import java.awt.Color;' to class
public Color allBlue(Color c1, Color c2) {
   return new Color(0, 0, 255);
}

Because this method has two Color parameters, for every pixel (Color object) in the first and second image this method is invoked passing the corresponding Color objects, at the same location in the images, as parameter values. The Color object that is returned is assigned to the same location in the third image (the panel immediately to the right of the last panel used for input). As the Color object we construct for each pixel corresponds to blue (0 for red, 0 for green, full intensity for blue), thus every pixel of the third image is blue. Invoking this method from the Methods menu in YOPS produces the following result:

(Screen Shot)

The same idea applies for writing methods that operate on Image and GraphicsPanel objects. However, the return types of these methods need to be void. These methods are intended to mutate the Image or GrahpicsPanel as appropriate. However, by using multiple parameters of the same type, one could use a first Image parameter as input and the second Image parameter as output by mutating the object referred to by the second parameter.

To this point we've only been working with blank panels. However, to YOPS all panels, whether blank or with an image loaded, look and work the same. YOPS can pre-load an image for us and we can operate on it just the same. For instance, that takes a pre-existing image and turns it purple-ish. We modify the YOPS constructor call in the Image Processor class as follows to load an image of Brookings Hall:

new YOPS(new ImageProcessor(), 250, 400, 3, "brookings.jpg");

The we can write a method thata operates on each pixel of the Image loaded into the first panel by substracts out the green component, leaving us with an image of red and blue intensities that will be purple-ish. To do so, we have the following method for the ImageProcessor class:

public Color purplish(Color c) {
   int red = c.getRed();
   int blue = c.getBlue();
   return new Color(red, 0, blue);
}
Which when run with the modified YOPS constructor, will produce the following:

(Screen Shot)

Download our example ImageProcessor.java

Continue to 3. Building a Line Tool >

Updates

10/4/2006: Our first release of YOPS is available for download at SourceForge.