Assignment 5: Image Magic Due July 21 and 28 at 9:00pm and

In this assignment we will be exploring file input and image processing. In the process, we will also gain experience using exceptions and assertions. The project is to be worked on in pairs with the first part being due on July 21 ar 9:00pm and the second part (with the same partners) being due on July 28 at 9:00pm.

Part 1: Input/Output

In this part we will develop the infrastructure to read in images from files and to write out images. There are two types of files we will be dealing with: black and white images, and color (RGB) images. But before we get to all that, a note on images. . .

Digital Images

Digital images are represented in a computer as a grid of intensity values. Each grid element, indexed by row and column number, is known as a pixel, short for picture element. For a black and white image, each pixel contains one value representing how bright that pixel should be (for our purposes, all intensity values will be represented as values from 0 to 255). A value of 255 is totally white.

For color images, each pixel stores three values, one for each primary color of light, red, green, and blue. Because of the way our eyes work, combining these colors in different amounts creates the illusion of millions of different colors spanning the entire spectrum!

One more thing: I mentioned before that pixels are referred to by their row and column (r,c). This is true but there's a strange convention in computer graphics: the pixel at row 0, column 0 (0,0) is at the upper left corner of the image while the pixel at (height-1, width-1) is at the lower right.

File Formats

An important fact about any file is its "format", that is, how the data is arranged (layed out) and how it should be interpreted.  For any type of file you process, an important question to investigate is what its format is.  Sometimes individual programmers invent a format when they create a file.  Other times, there are standards within an organization or even published international standards.

We will be manipulating files using two standard image formats: PGM and PPM. PGM (Portable Grey Map) is a format for storing grayscale images and PPM (Portable Pixel Map) is used to store color images.  Many, many other image formats exist, such JPG, GIF, BMP, and PNG.  Unlike many image formats, PGM and PPM are text files, that is, the data in the files are ordinary characters.  This means the file can easily be inspected or even created by a human, using text processing tools like Notepad or Wordpad.  It also means we can use the Java Reader interface (or subtypes like BufferedReader) in our programs.

The file format for PGM images is very simple. The first element of the file (that is, the first data) must be the string P2. The next two elements are the number of columns followed by the number of rows followed by the number of levels. (The number of levels is the number of divisions between full black and full white.) When I say "element" I mean a string of alphanumeric characters separated by whitespace. After the number of levels, the intensity for each pixel is given.  Additionally, any line that begins with '#' is a comment. For example, here is a complete PGM file:

P2
# i made this image by hand
10 10
255
0 0 0 0 0 0 0 0 0 0
0 255 0 0 255 0 255 255 255 0
0 255 0 0 255 0 0 255 0 0
0 255 0 0 255 0 0 255 0 0
0 255 255 255 255 0 0 255 0 0
0 255 0 0 255 0 0 255 0 0
0 255 0 0 255 0 0 255 0 0
0 255 0 0 255 0 0 255 0 0
0 255 0 0 255 0 255 255 255 0
0 0 0 0 0 0 0 0 0 0

The PPM format is very similar. Instead of P2 there is P3 and instead of single values for each pixel, there are three values in a row, one for red, one for green, and one for blue. For example:

P3
10 10
255
0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 255 0 255 255 0 255 255 0 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 0 255 0 255 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 0 255 0 255 255
0 255 255 0 255 255 255 255 0 255 255 0 255 255 0 255 255 0 0 255 255 0 255 255 255 0 255 0 255 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 0 255 0 255 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 0 255 0 255 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 0 255 0 255 255
0 255 255 0 255 255 255 255 0 0 255 255 0 255 255 255 255 0 0 255 255 255 0 255 255 0 255 255 0 255
0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255 0 255 255
0 255 255

Try copying and pasting the above text into a text editor and saving the file with a .pgm or .ppm extension respectively ...you should have a viewable image!

As a test of your understanding, create a small PGM file by hand.  It should contain a recognizeable shape or symbol.

Your Task

Create classes called PPMImage and PGMImage in the imageprocessing package. These classes must implement the IImage interface of that package.  The interface defines methods getWidth, getHeight, getPixel, and setPixel.  The concrete classes should have a toString method as usual.  Read the interface comments carefully for more information about all of these.   If appropriate, you could define a class which implements IIimage and which is a common ancestor of PPMImage and PGMImage. 

Note: to keep the size of the first week's work down, you will not be required to do any PPM processing for the first week, nor will you have to turn in a PPMImage class.  But you should keep PPM in mind as you design and program.

Create a class called ImageProcessor, which should implement the IImageProcessor interface in the imageprocessing package.  It has methods produceImage, writeImage, and produceTmesTwo (read the documentation comments for details).  It should be able to read both PPM images and PGM images.  In all cases, something reasonable (having to do with exceptions!) should be done in the case of something going wrong (such as getPixelValues being called before the image has been produced...).

A class for viewing your loaded images is provided along with some sample images. The class ImageViewer is a member of the imageprocessing package. Its constructor takes an Image and pops up a window with the image on the screen. Additionally you can use a number of programs freely available online to produce you own images; I recomend IrfanView.

Some Groundrules

The DelimitedTextFile class you have used in the past would be well-suited for this project -- except you may not use it!  Write the file reading code by hand, using standard classes of the java.io package.  A BufferedReader is recommended for input.   Catch exceptions as needed, and handle them appropriately.

For each concrete .java file that you create, make a corresponding JUnit test class.  Give it the same name as the class being tested, with Test at the end of the name (so, MyFile.java would have a JUnit test class called MyFileTest).  Following Xtreme Programming guidelines, you are encouraged to write the test class while you develop the main class, or even before.  With the test classes, how you name the methods is up to you, as long as you follow the necessary JUnit conventions (see lecture slides and/or Assignment 4).

Warm-Up and Hints

Try these things right away, before writing any Java code.  They will help you, guaranteed.

Turn in (by 7/21 at 9:00pm):

Part 2: . . .

PPM images, and more...  Think about ways you could transform an image, other than just doubling its size...