CIS 120 Spring 2008 - Image Processing

Due: 5 PM on February 15, 2008

This week you will write tools for an image processing program. We will provide the framework to load and display images, and you will write several tools that manipulate them in various ways. As always, the Javadocs are your friend and you should consult them liberally. You should only need to interact with Pixels and Manipulators, and you may want to study the code for FlipHorizontal.

You should also get familiar with looking things up in the Java API. In particular, your life will be much easier if you look at the Math class. Note that all of its methods are "static". This means that you never need to instantiate a Math object, but can just call its methods referencing the class name, like Math.min(x,y), or Math.sin(x).

Supplied Code

We're giving you the source code so you can see what's going on internally if you want to. DO NOT MODIFY the supplied code, or your solution will not work with our testers.

Try it out!

Download the code, and open all the files up in DrJava. Our image editor reads and writes PNG image files, so here are two you can download (right click and save to disk):

Interactions

ImageBase ib = new ImageBase("monkey.png");
ib.addButton(new FlipHorizontal());
ib.addButton(new Save());

At this point, you should see the monkey picture, and a small button panel with "Restore", "Flip Horizontal", "Save", and “Do Nothing”. Click the "Flip Horizontal" button and watch what happens. You can click "Restore" to bring back the original image, as it was when you opened it, or click "Save" to save a new copy of the image. You will be writing the code for additional buttons; consult the code for FlipHorizontal to see how buttons work. All of your classes will extend the provided Manipulator class, which does most of the legwork of converting the image into an easily usable form (a 2d array of Pixels).

FlipVertical (file to submit: FlipVertical.java)

By analogy with FlipHorizontal, this class should be easy to write. It should flip the image so that the bottom ends up at the top and the top ends up at the bottom.

RotateLeft (file to submit: RotateLeft.java)

This manipulation should be very simple. Simply rotate the image 90 degrees to the left (counter-clockwise).

Sample:

InvertColor (file to submit: InvertColor.java)

A general explanation of pixel color: Each of the three RGB (Red, Green, Blue) components can be any integer from 0 to 255. If all are 0, the resulting pixel is black (adding no red, green or blue results in black). If all are 255, the resulting pixel is white (adding red, green and blue results in white). If the red component is 255 and the green and blue components are 0, then you will get red. If the red and green components are 255 and the blue component is 0, you will get yellow (you can look up the RGB color model if you want to learn more).

Inverting Colors: To invert the colors of your image, each RGB component of a pixel should be changed to its RGB inverse. For example, if the red value of the original picture is 0, it should be 255 in the inverted picture. If it is 1 originally, it should be 254.

Sample

:

GreenScreen (file to submit: GreenScreen.java)

Replace all "green" pixels in the current picture with the corresponding pixel from the background image newBG. For this problem, treat a pixel as "green" in the following circumstance. Let r be the red component, g be the green component, and b be the blue component of the pixel. Then a pixel is green if

We supply you with a skeleton GreenScreen.java file (Updated 2008 Feb 09 3:05pm). You need to implement just the setNewBackground method. The first image above is a mountain that we'll be using as a background. The second image is a person in front of a green screen. The last one is after replacing all the green background pixels with pixels from the background image. Right click and save all three files in the same directory as your java files.

To check your results, you can get the distance (difference between images) from your solution to our sample. Since floating-point arithematic isn't infinitely precise, the distance probably won't be zero, but it should be a small number. Here are the results from an interaction with our implementation.

> ImageBase ib = new ImageBase("dancer.png");
ib.addButton(new GreenScreen()); 

Hit GreenScreen, type mountain.png, hit ok 

go back to the interactions pane 
it should read FileName: > 
hit enter to clear that line,
 then put 

> Picture e = new Picture("dancerMountain.png"); 
> e.distance(ib.getPicture())

Histogram Stretch (file to submit: HistogramStretch.java)

A histogram stretch ('Auto-Levels' in Photoshop) increases the contrast of an image. For example, an image in which most pixels are a middle gray has low dynamic range, and after a fairly conservative histogram stretch (threshold = 2) it looks clearer.

shelves with low contrast shelves with threshold=2 histogram stretch

The histogram in question is a graph of the frequency of each gray level in the image — how much of each color of gray there is (if we ignore for the moment that it's actually in color). In the sample image, before being stretched, the 'mountain' goes fairly far to the left (the darks are near black), but doesn't go all the way to the right (there isn't much or any white in the image). We want to stretch this slightly narrow range onto the full range from black to white. Afterwards, the 'mountain' will go all the way across, although it may have some gaps (more on that later). (Histogram images from Photoshop.)

stretched histogram

In Brief: The algorithm to perform this stretch (processImage in HistogramStretch) will go, in broad terms, like this:

if the image is zero size, do nothing
calculate the histogram: count the number of pixels at each gray level
using some threshold value (from the user)...
find the low and high ends of the histogram that are above that threshold
if the histogram has no interesting ends (too narrow, too flat), do nothing
remap the image from the narrow range to the full black-to-white range
return the new picture

Suggestion: It's a good idea to break off any large chunks or repeated steps from the above algorithm as separate, private helper methods. For example, you will be remapping many rgb values from one range to another, so it would make sense to write a helper function to do that.

In Detail: Here are the steps of the algorithm in more detail, explaining why we're doing what we're doing.

  1. Calculate the Histogram:
  2. Get the Threshold Value: Look up getNumberFromUser in the Manipulator javadocs.
  3. Find the Histogram Ends:
  4. Remap the Image:

Once the algorithm is implemented, different threshold values can be used to create more or less contrast. For example, an outrageous value of 800 for the threshold produces a very blown-out image. You can again check distances to make sure your implementation is producing a mathematically, as well as visually, similar result.

shelves with a histogram stretch threshold of 800

Congratulations!

You're all done. You should have: