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.
- (Optional but recommended) Download and install the freeward program IrFanView. It can display PBM and PPM and dozens of other image formats. It also lets you inspect image files as text and/or hex codes. Finally, it will convert between image file formats, so you could take a JPG file from your digital camera and save it as a PBM or PPM file to test with your program.
- Did you understand the format of the PGM file? If so, then
look at the file given earlier in the instructions and answer this
question: what does the image look like? You can figure this out
without actually displaying the file! If you can't then
possibly you don't really understand the format -- keep working at it.
- As suggested earlier, copy and paste the two image files (given earlier) into new files and save them, with appropriate extensions. See if you can view them using IrFanView, or using the Viewer.class provided with this assignment. If you can't get this to work, you won't have any way of testing your work.
- If you've already created your small PGM file by hand (as
mentioned earlier) -- view it. If it doesn't look as you
expected, fiddle with it.
- Using the DrJava interactions pane, get familiar with the steps for opening and reading a text file. See lecture slides for coded examples. It is recommended that you use a BufferedReader (why?). Choose a short text file to work with (could be one of the image files you created above, but doesn't have to be). Open the file (i.e., create an object of type BufferedReader), then read the first line and display it. You should be able to tell if it's working right! Read some more lines. What happens when you get to the end of the file? If you can't get this process to work, you're not ready to write a full program to do it!
- Look up the method split of the String class. In the DrJava interactions pane, experiment with using it. Take a string like "CSE143 is too much fun" and get each word as a separate string in an array. How could this help you (yes, really help you) when processing a PBM file?
- Review from previous projects how you convert a string of characters (like "255") into the corresponding int. Could any exceptions be thrown in that process? Previously, you might have ignored such exceptions, but now you are capable of handling them. What about in the other direction? If you need to output the int 255 as the string "255", what does that take?
- Using the DrJava interactions pane, get familiar with the steps
for opening and writing a text file. Be careful when you
write files! Any existing file with the same name is
irretrievably overwritten! Open a file that exists and see if
there's any difference between opening one that doesn't already exist
(don't try any of this with a file you care about!) Write a small
amount of data to the file and close it. Then check the file via
Windows to see if what you thought you did really happened.
Turn in (by 7/21 at 9:00pm):
- (Optional) PPMImage.java and its test case class
- PGMImage.java and its test case class
- ImageProcessor.java and its test case class
- Two (2) screenshots: one of an unscaled picture being viewed with the provided viewer class, and one of a scaled using TimesTwo being viewed with the provided viewer class.
- The PGM image file you created by hand.
- A thorough 1 page technical detailing the design decisions made in you project and other details of you implementation.
Part 2: . . .
PPM images, and more... Think about ways you could transform
an image, other than just doubling its size...