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.
The purpose of this assignment is to practice working with two dimensional arrays and implementing Java classes according to an existing specification.
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.
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.
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:
Pixel p = new Pixel(-100, 300, 50);The resulting red green and blue color components should be 0, 255 and 50 respectively.
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.
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.
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:
Pixel
class.
getPixel
– This method must
return null
for pixel positions outside of
the image boundaries.
setPixel
– This method must have
no effect for pixel positions outside of the image
boundaries or if a null
Pixel
argument is provided.
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.
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.
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:
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)
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.
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)
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)
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)
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)
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)
MimpLauncher
class below.main
for
starting the MIMP application. Submission for this assignment is divided into three parts that should be completed in order.
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.
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:
As with Part A, you must pass all of the automated submission tests to receive ANY credit for this part.
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.
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:
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.