PA #1: MIMP

HONOR CODE REMINDER

Programming assignments in this course are to be completed individually. No collaborative work is allowed. Think of the programming assignments as low-stakes take-home exams. See the course syllabus for more details. Talk to your instructor if you have questions about the collaboration policy.

Objectives

The purpose of this assignment is to practice working with two dimensional arrays and implementing Java classes according to an existing specification.

Introduction

Welcome to ImageCorp! You have been hired to work on a ground-breaking new image manipulation application named MIMP: The Madison Image Manipulation Program. MIMP is designed to be a competitor to existing products such as Photoshop or GIMP.

The User Interface Team has already designed a stylish and functional GUI. It will be your job to program the classes that actually store and manipulate image data.

Representing Images

There are many different ways to digitally represent images. One common approach is to maintain a two-dimensional grid of pixels, where each pixel corresponds to the color of a tiny rectangular region of the overall image.

The color of each pixel is represented by its red, green and blue components. Each color component is restricted to be an integer in the range 0 to 255. Under this representation (red=0, green=0, blue=0) corresponds to black, (255, 255, 255) corresponds to white, (255, 0, 0) corresponds to red etc. This approach enables us to represent 256*256*256 = 16,777,216 distinct colors. The individual color components are often referred to as "channels".

The Java Standard libraries include some built in image classes, including java.awt.Image and java.awt.image.BufferedImage. Unfortunately these classes are not ideal for our purposes. They are designed primarily to represent images in a graphical interface, not to support pixel-level manipulation.

For this project you will implement three classes: one representing individual pixels, one representing complete images, and a utility class that is able to perform basic image manipulations such as brightening, rotating etc.

The Pixel Class

Your implementation of the Pixel class should conform to the following UML diagram. Note that Pixel is an immutable type: once a Pixel object has been created it cannot be modified.

(The {readOnly} annotation indicates that the fields should be final.)

Method details:

  1. Constructor – The constructor should automatically clip color values to the appropriate range. In other words, if a Pixel is declared as follows:
    Pixel p = new Pixel(-100, 300, 50);
    The resulting red green and blue color components should be 0, 255 and 50 respectively.
  2. toString – The toString method should return a string with the following format: "(red, green, blue)". The appropriate string for the pixel above would be "(0, 255, 50)". For this method, and all other methods that return strings, your formatting must exactly match the specifications.
  3. getChannel – The getChannel method should take an integer value in the range 0-2, indicating which color channel is requested, 0=red, 1=green, 2=blue, and return the corresponding color value. If any other channel is requested, the return value should be -1. This is a convenience function that makes it possible to loop over channels.

The Image Class

Your implementation of the Image class should conform to the following UML diagram. Note that location (0, 0) is in the upper-left corner of the image.

Method details:

  1. The two-argument constructor should create a completely white image. The five-argument constructor should create an image using the indicated color values. If the provided width or height is less than or equal to zero, both constructors must create a 0x0 image. Color values outside of the allowable range should be clipped according to the logic described for the Pixel class.
  2. getPixel – This method must return null for pixel positions outside of the image boundaries.
  3. setPixel – This method must have no effect for pixel positions outside of the image boundaries or if a null Pixel argument is provided.
  4. toString – The format for the toString method is "<Image width=w height=h>" where w and h are the height and width of the image. For example: "<Image width=640 height=480>" would be the correct string for any image that is 640 pixels wide and 480 pixels high.
  5. toStringDebug – The toStringDebug method is designed to facilitate debugging by returning an easy-to-read string representation of an image. The following string illustrates the correct format for a tiny 4x5 pixel image:
    <Image width=4 height=5>
    	(1, 101, 201)	(2, 102, 202)	(3, 103, 203)	(4, 104, 204)
    	(5, 105, 205)	(6, 106, 206)	(7, 107, 207)	(8, 108, 208)
    	(9, 109, 209)	(10, 110, 210)	(11, 111, 211)	(12, 112, 212)
    	(13, 113, 213)	(14, 114, 214)	(15, 115, 215)	(16, 116, 216)
    	(17, 117, 217)	(18, 118, 218)	(19, 119, 219)	(20, 120, 220)
    
    Use tab characters ("\t") to indent your pixel values and separate the columns in your string.

The ImageTransforms Class

ImageTransforms is a utility class that contains static methods for performing basic image transformations. Note that none of the methods defined in ImageTransforms modify the existing image. In each case a new image is returned that is a result of applying the requested transform.

Method details:

  1. adjustBrightness – This method simply increases or decreases all three color channels by the indicated amount. If the original image above is adjusted by +100, the correct result would be the following:
    <Image width=4 height=5>
    	(101, 201, 255)	(102, 202, 255)	(103, 203, 255)	(104, 204, 255)
    	(105, 205, 255)	(106, 206, 255)	(107, 207, 255)	(108, 208, 255)
    	(109, 209, 255)	(110, 210, 255)	(111, 211, 255)	(112, 212, 255)
    	(113, 213, 255)	(114, 214, 255)	(115, 215, 255)	(116, 216, 255)
    	(117, 217, 255)	(118, 218, 255)	(119, 219, 255)	(120, 220, 255)
    
  2. convertToGrayscale – This method converts an image to grayscale by setting all three color channels to the same value. Most image processing programs accomplish this by computing a weighted average of the three color components. Green is weighed most heavily, reflecting the fact that the human eye is more sensitive to green wavelengths. Your method must use the following weighting scheme: L = 0.299R + 0.587G + 0.114B. Where L is referred to as "luminosity" or "brightness". You can find more information on the Wikipedia Grayscale page.

    Here is the resulting image if we compute the grayscale version of the original 4x5 image above:

    <Image width=4 height=5>
    	(82, 82, 82)	(83, 83, 83)	(84, 84, 84)	(85, 85, 85)
    	(86, 86, 86)	(87, 87, 87)	(88, 88, 88)	(89, 89, 89)
    	(90, 90, 90)	(91, 91, 91)	(92, 92, 92)	(93, 93, 93)
    	(94, 94, 94)	(95, 95, 95)	(96, 96, 96)	(97, 97, 97)
    	(98, 98, 98)	(99, 99, 99)	(100, 100, 100)	(101, 101, 101)
    

    In this example, the upper left pixel has the value 82 because
    0.299 * 1 + 0.587 * 101 + 0.114 * 201 = 82.5.
    Note that the floating point result is truncated rather than rounded. This is the default behavior in Java when a floating point value is assigned to an integer variable.

  3. threshold – This method converts the image to black and white by setting all pixels with a luminosity below the threshold to black, and all other pixels to white. The result of applying a threshold of 90 to our original image would be the following:
    <Image width=4 height=5>
    	(0, 0, 0)	(0, 0, 0)	(0, 0, 0)	(0, 0, 0)
    	(0, 0, 0)	(0, 0, 0)	(0, 0, 0)	(0, 0, 0)
    	(255, 255, 255)	(255, 255, 255)	(255, 255, 255)	(255, 255, 255)
    	(255, 255, 255)	(255, 255, 255)	(255, 255, 255)	(255, 255, 255)
    	(255, 255, 255)	(255, 255, 255)	(255, 255, 255)	(255, 255, 255)
    
  4. rotateLeft – This method rotates the image 90° to the left (counter-clockwise). The result of applying this method to our original image would be the following:
    <Image width=5 height=4>
    	(4, 104, 204)	(8, 108, 208)	(12, 112, 212)	(16, 116, 216)	(20, 120, 220)
    	(3, 103, 203)	(7, 107, 207)	(11, 111, 211)	(15, 115, 215)	(19, 119, 219)
    	(2, 102, 202)	(6, 106, 206)	(10, 110, 210)	(14, 114, 214)	(18, 118, 218)
    	(1, 101, 201)	(5, 105, 205)	(9, 109, 209)	(13, 113, 213)	(17, 117, 217)
    
  5. rotateRight – This method rotates the image 90° to the right (clockwise). The result of applying this method to our original image would be the following:
    <Image width=5 height=4>
    	(17, 117, 217)	(13, 113, 213)	(9, 109, 209)	(5, 105, 205)	(1, 101, 201)
    	(18, 118, 218)	(14, 114, 214)	(10, 110, 210)	(6, 106, 206)	(2, 102, 202)
    	(19, 119, 219)	(15, 115, 215)	(11, 111, 211)	(7, 107, 207)	(3, 103, 203)
    	(20, 120, 220)	(16, 116, 216)	(12, 112, 212)	(8, 108, 208)	(4, 104, 204)
    
  6. mirror – This method flips the image around the vertical axis. The result of applying this method to our original image would be the following:
    <Image width=4 height=5>
    	(4, 104, 204)	(3, 103, 203)	(2, 102, 202)	(1, 101, 201)
    	(8, 108, 208)	(7, 107, 207)	(6, 106, 206)	(5, 105, 205)
    	(12, 112, 212)	(11, 111, 211)	(10, 110, 210)	(9, 109, 209)
    	(16, 116, 216)	(15, 115, 215)	(14, 114, 214)	(13, 113, 213)
    	(20, 120, 220)	(19, 119, 219)	(18, 118, 218)	(17, 117, 217)
    
  7. flip – This method flips the image around the horizontal axis. The result of applying this method to our original image would be the following:
    <Image width=4 height=5>
    	(17, 117, 217)	(18, 118, 218)	(19, 119, 219)	(20, 120, 220)
    	(13, 113, 213)	(14, 114, 214)	(15, 115, 215)	(16, 116, 216)
    	(9, 109, 209)	(10, 110, 210)	(11, 111, 211)	(12, 112, 212)
    	(5, 105, 205)	(6, 106, 206)	(7, 107, 207)	(8, 108, 208)
    	(1, 101, 201)	(2, 102, 202)	(3, 103, 203)	(4, 104, 204)	      
    

Provided Code

The following code has been provided for you. You should not need to modify any of these files:

Submission and Grading

Submission for this assignment is divided into three parts that should be completed in order.

Part A: Readiness Quiz

In order to complete Part A you should first carefully read the project specification. We suggest that you print a paper copy of the document so that you can highlight and take notes as you read. Once you feel confident that you have a good grasp of the project requirements, log into Canvas and complete the Part A quiz. YOU MUST ANSWER ALL QUESTIONS CORRECTLY TO GET ANY CREDIT FOR THIS PART. You may take the quiz as many times as necessary.

Keep in mind that this quiz is considered part of the programming assignment and falls under the requirements of the course collaboration policy. Reading technical documentation is one of the skills we want to develop in this course. This means you should not ask your classmates for the answers to these questions. If you are stuck, feel free to talk to your instructor or a TA.

Part B: Pixel Class

For this part you must submit your completed Pixel class through Web-CAT. You must also provide JUnit tests for all Pixel class methods. Your unit tests must have 100% line coverage. I.e. Every line in the Pixel class must be triggered by at least one test. Checkstyle tests will not be run for this submission.

Web-CAT will base your score on three factors:

  1. Coverage from your own unit tests.
  2. Passing your own unit tests.
  3. Passing the instructor-provided unit tests.

As with Part A, you must pass all of the automated submission tests to receive ANY credit for this part.

Part C: Image and ImageTransforms

For Part C you must submit all three required classes. Your submission for Part C must conform to the course style guide. Web-CAT will run Checkstyle against your submission and deduct points if your code is not formatted correctly.

You are not required to submit unit tests for Part C. However, you are unlikely to finish the project successfully without somehow testing each of your methods. You could accomplish this by writing JUnit tests, or by writing a test driver to exercise each method as you code it. If you need to get help from an instructor or a TA you should plan on bringing your testing code to that meeting.

You will receive at most 50% of the possible points for part C if your code fails any of the instructor unit tests.

Grading

Part A 10%
Part B Web-CAT Correctness/Testing: 10%
Part C Web-CAT Correctness: 50%
Part C Web-CAT Checkstyle: 10%
Part C Instructor grading based on style and code quality: 20%

The portion of your grade labeled "Instructor grading based on style and code quality" will be based on stylistic issues that cannot be checked automatically. These include:

Submission penalties

This assignment will include a penalty for excessive Web-CAT submissions. You are allowed unlimited submissions for part B. For Part C you are allowed ten submissions with no penalty. There will be a deduction of .5 for each additional submission beyond ten.