Pixmap


Images are stored by computers in a variety of formats, such as gif, jpg, tiff, and png. These formats differ in how faithfully they represent the original picture, how well they can be compressed to reduce the space each image takes up, or how well they can be copied from on type of computer to another. However, no matter what format the image is stored in, it can always be represented of as a mapping of (x, y) pixel position to a color (the range of colors may be restricted to values of grey or just black and white). Thus, for the remainder of this project, we will refer to all formats of digital images as pixmaps.

This programming project involves manipulating pixmaps. Your program will be able to read in gif, jpg, and png image formats, and perform several operations on these images including invert, reflect, expand, and emboss.

Tasks and Files

  1. Background
  2. Classes to Review
  3. Completing New Image Manipulation Commands
  4. Extra Credit
  5. Grading and Submitting

To start, check out the program files using Ambient as assignments/01_pixmap.

Classes To Review

java.awt.Color

This class represents the Color of a pixel as three values ranging from 0 to 255, one each for the level of red, green, and blue that combined to create the color you see on the computer screen. For the purposes of this program, you need to understand how to construct a Color, but you don't really need to understand the internal workings of the class. For more information about why colors are represented using combinations of red, green, and blue, see this reference online.

Pixmap

This class represents an image, i.e., a 2D-grid of Colors. A black-and-white image can be represented as a 2-dimensional grid of Colors that are only either WHITE or BLACK (or just 1 and 0). Gray-scale images can be represented using just a number that is the shade of grey between prefectly black and perfectly white. For example, numbers from 0 to 255 can represent 256 different shades of gray. Color images are represented by Colors in which each one has three color values: a red value, a green value, and a blue value. For example, three numbers from 0 to 255 can represent the different shades of the red, green, and blue values.

For black-and-white images, a bitmap is a 2-D grid of 0's and 1's, where a 0 corresponds to white and a 1 corresponds to black. For example, the following is a bitmap which represents a 9x8 black-and-white picture of a `<' sign. The bitmap of 0's and 1's on the left is as an image on the right (enlarged eight times).

0 0 0 0 0 1 1 0
0 0 0 0 1 1 0 0
0 0 0 1 1 0 0 0
0 0 1 1 0 0 0 0
0 1 1 0 0 0 0 0
0 0 1 1 0 0 0 0
0 0 0 1 1 0 0 0
0 0 0 0 1 1 0 0
0 0 0 0 0 1 1 0

For gray-scale images, a bitmap is a 2-D grid of numbers from 0 to 255 representing the shades of gray. The value 255 is white, and the value 0 is black. For example, the gray-scale bitmap below is of size 4x6 and the pixels get a little lighter as the row numbers increase and a lot lighter as the column numbers increase. The last column is much different from the previous columns as the graphic shows.

0 10 30 45 95 200
1 11 32 47 97 215
1 11 32 47 97 235
3 12 34 49 99 255

For color images, a bitmap is a 2-D grid of numbers from 0 to 255, with three numbers representing a pixel. For example, the color bitmap below shows the 3x6 image displayed on the right. Each row has two pixels of red represented by the three numbers 248 0 0 followed by two pixels of green represented by the three numbers 0 252 0, followed by two pixels of yellow represented by the three numbers 248 252 0.

248 0 0 248 0 0 0 252 0 0 252 0 248 252 0 248 252 0
248 0 0 248 0 0 0 252 0 0 252 0 248 252 0 248 252 0
248 0 0 248 0 0 0 252 0 0 252 0 248 252 0 248 252 0

A Pixmap object can be used to access or set the color of an individual pixel and to access or set the total size of the image. Methods that you will use include:

Note, you can only change the image displayed on the screen by calling one of the last two methods described above (the ones that start with set). If you make changes to a copy of the Pixmap, they will not be visible on the screen.

Negative

This class is an example of a completed Pixmap Command that determines the negative a Pixmap in a straightforward manner, by subtracting each color in the image from the brightest possible color, white. For example, in a black-and-white bitmap where each bit must be inverted: 0's are changed to 1's and vice versa. The images below are inverted versions of each other.

 0 0 0 0 0 1 1 0        1 1 1 1 1 0 0 1
 0 0 0 0 1 1 0 0        1 1 1 1 0 0 1 1
 0 0 0 1 1 0 0 0        1 1 1 0 0 1 1 1
 0 0 1 1 0 0 0 0        1 1 0 0 1 1 1 1
 0 1 1 0 0 0 0 0        1 0 0 1 1 1 1 1
 0 0 1 1 0 0 0 0        1 1 0 0 1 1 1 1
 0 0 0 1 1 0 0 0        1 1 1 0 0 1 1 1
 0 0 0 0 1 1 0 0        1 1 1 1 0 0 1 1
 0 0 0 0 0 1 1 0        1 1 1 1 1 0 0 1

In a color pixmap, the same principle is used: each value (red, green, and blue) is subtracted from its highest possible level, 255.

Main

You can execute the Pixmap application by running the class Main. The Open button allows you to browse your file system and load images. The other buttons are bound to the various image manipulation commands. Initially, only Reverse Colors is functional. You will complete the code to make the Expand, Mirror Vertically, Mirror Horizontally, Emboss, Blur, and Detect Edges commands functional.

Code you will write

  1. Add mirror functionality
    1. Complete the execute method of class MirrorVertically.

      An image can be reflected along an axis (usually horizontal, vertical, or diagonal). Reflecting along a vertical axis through the middle of an image is the same as reversing the contents of each row (similarly, reflecting along the horizontal axis is the same as reversing each column). For example, the images below are vertical reflections of each other.

      0 0 0 0 0 1 1 0        0 1 1 0 0 0 0 0
      0 0 0 0 1 1 0 0        0 0 1 1 0 0 0 0
      0 0 0 1 1 0 0 0        0 0 0 1 1 0 0 0
      0 0 1 1 0 0 0 0        0 0 0 0 1 1 0 0
      0 1 1 0 0 0 0 0        0 0 0 0 0 1 1 0
      0 0 1 1 0 0 0 0        0 0 0 0 1 1 0 0
      0 0 0 1 1 0 0 0        0 0 0 1 1 0 0 0
      0 0 0 0 1 1 0 0        0 0 1 1 0 0 0 0
      0 0 0 0 0 1 1 0        0 1 1 0 0 0 0 0
      

    2. Complete the execute method of class MirrorHorizontally.

      The original image above (the < symbol) is unchanged if it is reflected in a horizontal line through the middle of the image since the image has symmetry in this direction. Mirroring the letter Y, however, is diagrammed below: the image on the left, when reflected in a horizontal line though the middle of the image, results in the image on the right. Note that each column of numbers is reversed.

           1 0 0 0 1            0 0 1 0 0
           0 1 0 1 0            0 0 1 0 0
           0 0 1 0 0            0 0 1 0 0
           0 0 1 0 0            0 1 0 1 0
           0 0 1 0 0            1 0 0 0 1
      

  2. Add emboss effect

    Complete the execute method in the class Emboss.

    Transform the image such that it looks like it has been indented into paper (embossed) by greying out most of the image except where there are areas of high contrast. This effect is accomplished by shifting the image, inverting it, and then adding the shifted, inverted, version to the original. The amount by which you shift the image determines how deeply indented it appears. The fact that most of the image similarly colored means that it will be mostly grey when you are finished. Edges, areas of high contrast, will stand out more. To shift an image, simply move its pixels slightly (for example, over 2 and down 2). To add two images together, add the individual color values together and then take the average (i.e., divide by two).

  3. Add expand functionality

    Complete the execute method in the class Expand.

    The method already prompts the user for the scale factors and correctly grows the pixmap to the new, expanded, size by expanding it horizontally, vertically, or in both directions using the setSize method. Your task is to fill in the now expanded image with a copy of the colors from the original image. To do this, take each single color value and copy it so that it represents a rectangle of color in the expanded image whose width and height is the scale factors given by the user.

    If you are not careful about the order in which copy the colors, you will destroy the original image by changing its colors before you have copied them into the correct new position in the expanded image. Thus, to start, you might want to create a separate pixmap and simply copy the colors into it and then copy them back into the original pixmap.

    However, extra credit will be given for solutions done without an extra copy of the pixmap. Expanding an image in place, i.e., without using an auxiliary pixmap, requires some planning. In the figure below, an image is shown partially expanded by three vertically, and by two horizontally. By beginning the expansion in the lower right corner as shown, the image can be expanded in place --- that is without the use of an auxiliary array or another Pixmap object.

  4. Complete the Image Processing operations, Blur and EdgeDetect

    Image processing operations use the current pixel and its neighboring pixels to calculate its new color value. In these operations, you must make a copy of the original image from which to gather your neighborhoods so that the effects do not cascade as you compute a new value for each pixel. For example, the diagrams below represent part of a grey scale image: a 3-neighborhood and a 5-neighborhood of the middle pixel whose grey-scale value is 28. You do not need to worry about neighborhoods with an even width or height.

    Suppose we wanted to write an image processing operation to replace the middle pixel's color by the median of the values in its neighborhood. The nine values in the 3-neighborhood are (10 10 12 25 25 28 28 32 32). The median, or middle, value is 25 --- there are four values above 25 and four values below 25. The values in the 5-neighborhood are (10 10 10 10 10 10 12 12 12 18 18 18 25 25 25 25 25 25 32 32 32 32 32 32 32), and again the median value is 25 because there are 12 values above and 12 values below 25. The easiest way to find the median of a list of values is to sort them and take the middle element.

    Applying a 3 x 3 median-filter to the image on the left below results in the image on the right (these images look better on the screen than they do on paper).

    What happens at the edges, though? The source pixel will not have neighbors on at least one side, so it will not have a "complete" neighborhood. Rather than dealing with this problem, you can just leave all the edge pixels values unmodified.

    1. Complete the execute method of class Blur

      The color of a pixel is calculated by sampling the neighboring elements in the pixmap and taking the the average, not the median as shown in the example above. Applying this operation should give your image a soft, slightly impressionist feel. 

    2. Complete the execute method of class EdgeDetect

      The color of a pixel is determined by scaling the pixel values to use the largest values possible. If max and min are the maximum and minimum color values in the neighborhood, the scaled value of a pixel should be calculated as follows:

      newColorValue = (oldColorValue - min) * maxPossiblePixelValue / (max - min)

      Applying this operation adds contrast to an image, especially around the edges within the image.

Extra Credit

Make sure you note in your README file any extra credit you attempt.

Grading and Submit

Submit your project as 01_pixmap, using the Ambient menu within Eclipse. Always be sure to submit all java files, even if you did not modify them. You must also submit a README too according to the directions given on the assignment page.