Homework #4 CMSC 131
Due Oct 25, 6:00 pm Object-Oriented Programming I
Type of Homework: Closed Fall 2004


In this homework you will learn about the Picture interface and get practice writing implementations of Picture classes.


So far you have used classes that allow you to manipulate images. In this homework you will design and implement several image processing classes, similar to the ones presented in previous homeworks. For example, you will design a class that allows us to turn a color image into a gray-scale image. More details about the program are provided in the Specifications section. You may want to take a look at the Sample Run section before you read the detailed description.

This homework will be graded as follows:


Code Distribution

Before you read these specifications, you should access the files associated with this homework by checking out the project labeled p4. The project provides all the classes (in the form of a library) associated with the picture infrastructure, the driver of the program (Driver.java), some sample classes, the time log file, and several jpeg images.

You can see the processing associated with all the classes you are to implement by executing the main() method associated with the Driver.java file. The output generated by this driver is the one you will find in the Sample Run section.

Understanding Pictures and Pixels

A digital picture (also called an image) is stored as a two-dimensional array of pixels, which are little spots of color. Colors are represented as a mixture of the three primary colors red, green and blue (denoted R, G, and B, respectively). By varying the amount of red, green and blue in any one pixel, you can create any of a wide range of colors. The smoothly varying levels of these color components allow for realistic looking images.

In the CMSC 131 picture library each pixel's color component is represented as a double that ranges from 0.0 (dark) to 1.0 (bright). So, the RGB triple (0,0,0) is black, (1,1,1) is white, (1,0,0) is red, and so on. We can combine these to form other RGB triples. For example, (1,1,0) is red+green = yellow, and (1,0,1) is red+blue = magenta, and so on. We can also use values between 0 and 1, for example (0.5, 0.5, 0.5) is a medium shade of gray.

The collection of pixels that form a picture are arranged in a 2-dimensional grid pattern. Each picture has a width and a height, measured in terms of the number of pixels in this grid. Each individual pixel can be specified by giving its index, which indicates its row and column numbers in this grid. (Rows run horizontally and columns run vertically.) In Java (and most other image processing systems) pixel indices start with (0,0) in the upper left corner. The row indices range from 0 at the top to height-1 at the bottom. The column indices range from 0 at the left to width-1 at the right. Row indices increment by one as you move one pixel down, and column indices increment by one as you move one pixel to the right. The figure below shows the layout of an image of height h and width w.

How Pictures are Made

Now, let's look at the actual classes in the library and their role::

We have provided Javadoc documentation for these classes. In addition, you should check out the code distribution for this homework. Several of the classes in the description below, can be found in the code distribution.

The Picture interface is key, as it is the basis for defining a new picture. It specifies three methods that any Picture class must implement. In particular, the interface consists of the following three public methods:

At first you may be surprised that just implementing these three little methods is sufficient to define an image. You might ask "where is the 2-dimensional grid of pixels?" The answer is that you do not need to create it. The grid is (implicitly) generated within the PictureUtil.show(Picture p) method, which is provided for you by the picture library. Here is a (somewhat simplified) explanation of how it works.

The utility PictureUtility.show(p) is given a single argument, the picture p to be displayed. The object p can come from any class that implements the Picture interface. First, show(p) invokes p.getWidth() and p.getHeight() to determine the dimensions of the new image, and asks Java to create an image of this size. (Java creates the 2-dimensional grid.) Then show(p) proceeds to fill in the pixel colors of this image, row-by-row and column-by-column. Whenever it needs to know the color of some pixel, say at column x and row y, it invokes p.getColor(x, y), and this method returns the desired color. When all the pixels have been filled in, it then asks Java to display the image on your screen.

Thus, in order to make a picture of your own, you just need to create an class that implements this interface. When you create a class that implements the Picture interface, you are promising that your class will define these three methods: getWidth, getHeight, and getColor.

If this seems a bit abstract, see the Examples of Picture Objects below for some concrete examples.

Your Assignment

For this homework, you will write six different classes. Each creates a picture by implementing the following Picture interface. Please see sample run section for examples of each of these operations.


Sample Run

The following shows the results of executing the driver (Driver.java). From left-to-right, and top-to-bottom they show the following operations, when applied to the original images: (1) the original image, (2) with green and red components selected, (3) with green and blue components selected, (4) rotated, (5) black and white, (6) a second image, (7) blended, (8) offset by (20,45), and (9) embossed. Note that the window titles are generated automatically by the picture library.

Challenge Problem

Remember that you are not required to implement the following problem. Please visit the course web page for information regarding challenge problems. IMPORTANT: If you decide to complete the challenge problem you must provide its implementation in a separate file called Challenge.java.

For this challenge problem, you will implement a single class that creates a new picture of your own design. It can be based on any number of source images and achieve any effect you like. Your solution must satisfy the following requirements:

A variable number of gold star points will be awarded, based upon our subjective appraisal of the degree of creativity and effort that went into your class:


Submit your project using the submit project option associated with Eclipse. Remember to complete your time log before submitting your homework. Remember to include your name and student ID number in your time log. We need this for grading purposes, and we may deduct points if this information is missing.

Examples of Picture Objects

Let's look at some of the examples of Picture objects. Observe that each class definition contains the specification "implements Picture". This is how Java knows that it can be used for generating images.

First, RedSquare is extremely simple. It has a fixed width (150) and height (150), and every pixel is defined to be red. (It makes use of a the constant RED, defined in the class PictureColor. It also has constants for some other common colors, such as WHITE, BLACK, BLUE, YELLOW, GRAY, and so on.)

Next, FrenchFlag is a little more interesting. First, it has a constructor that allows the class user to adjust the width. The height is set to 3/4 (75%) of the width. Then, the getColor returns blue in the leftmost third (that is, for x < width/3), white in the center third (for x from width/3 to 2*width/3), and red in the rightmost third.

Next, let us consider how to modify the pixel values of an existing base image. The class Inverse is given a base image in its constructor, and it creates an image of the same size, but each pixel is replaced with its photographic complement, by replacing each color component c, which ranges from 0.0 up to 1.0, with (1.0 - c). Thus, it now ranges from 1.0 down to 0.0. For example, in the image below, the yellowish tones of the cat's fur might be the RGB color (1.0, 1.0, 0.0), which would be mapped to its complement (0.0, 0.0, 1.0), which is blue.

Finally, the class FlipLeftRight transforms an image by creating a mirror image (flipped left to right). It has a constructor that is given (a reference to) the original image, and stores this in the instance variable "base." It uses the base's height and width as its own. In order to generate the color of a given pixel (x,y), it accesses the pixel of the same row (y) in the base image, but it effectively reverses the column (x) by subtracting x from the base's width-1.

To see how this works, think about the the color that will be assigned to a pixel along the leftmost column of our image (column 0). Given x=0, getColor computes newX as (width - 1 - x) = (width - 1). This index corresponds to the rightmost column of the base image. It then invokes base's getColor on newX and y. This returns the color of the rightmost pixel on the same row, and we return this same color. In short, the color we return for the leftmost column of our image, is the same as the color for rightmost column of base. By a similar token, when x = (width - 1) (the rightmost column of our image) we access column (width - 1 - x) = 0 (the leftmost column) of base. Extrapolating to all the intermediate columns of this row, we can see that this effectively produces a mirror image of this row of base. By doing this for all rows, we get a mirror image of the base image.