T-Th 9:05 |
CS 1110: Introduction to Computing Using Java Spring 2012 |
|||||||||||||||||||||||||||||||||||||||||||||||
Main
About: Announcements Staff Consultants Times & Places Calendar Materials: Texts DrJava VideoNote Terminology Handouts: Lectures Assignments Labs Assessment: Grading Exams Quizzes Resources: CMS Piazza (link) Piazza (about) AEWs FAQ Java API Style Guide Academic Integrity |
ImagesDue to CMS by Thursday, April 19th This purpose of this assignment is to to learn how images are stored in memory, and write methods to manipulate them (much like Photoshop does). You will also learn a little bit about how graphical applications in Java are structured. You will also learn how to use helper methods and constants to structure your code so that it is much more readable. Read this entire document carefully. Budget your time wisely. In particular, you should get started on this assignment immediately. This assignment is due two days after the second prelim, and there is no room to grant extensions. So if you wait to the last minute, you will have to deal with this assignment and the prelim at the same time. Do not do this! Pace yourself, working on at least one method a day (there are six in total when you are done). Reading these InstructionsIn the instructions below, we provide a lengthy aside on application design. We refer to certain classes in the application as model, view, or controller. This is the subject of a lecture that has been unfortunately delated until after the second prelim. You may still find this dicussion interesting, which is why it is provided. With that said, some of you may just find this dicussion more confusing than helpful. It is also extremely long, and you do not need to follow all of it to do this assignment. If you find the section on application design confusing, or you just want to get started with the assignment, feel free to skip over it and start with the discussion of ImageArray instead. Even skipping over the section on application design, these instructions are quite long. You should spend at least one evening simply reading these instructions while looking over the existing classes. Do not write any code until you understand how everything fits together. Table of Contents
Before You Get StartedAcademic IntegrityWe have given this assignment before. It is a good one, and students really like it. Do not share your code with others. Do not obtain or look at a copy of the earlier solution or a version being done by another student. Such cheating helps no one, especially you, and it makes more unnecessary work for us. As with the last assignment, it is highly unlikely that your code for this assignment will look exactly like someone else's. Once gain, we will actively be using Moss to check for instances of cheating and plagiarism. Anyone caught using files that are obviously from a previous semester will be prosecuted, with the end result perhaps being to fail the course. Collaboration PolicyYou may do this assignment with one other person. If you are going to work together, then form your group on CMS as soon as possible. If you do this assignment with another person, you must work together. It is against the rules for one person to do some programming on this assignment without the other person sitting nearby and helping. With the exception of your CMS-registered partner, you may not look at anyone else's code or show your code to anyone else, in any form what-so-ever. Assignment Source CodeThe first thing to do in this assignment is to download the zip file A6.zip from this link. You should unzip it and put the contents in a new directory. This zip file contains the following:
All of these classes are explained in more detail below. The
only class that you will need to modify is Running the Application
In order to run the application, the only file that you need to open in DrJava
is
To run the application — once you have opened and compiled When you work with this application, the left image will not change; it is the original image. The right image will change as you click buttons. First, see what buttons invert, transpose, and hor reflect do. After any series of clicks on these, you can always click the button restore to get back the original file. If you wish, you can also try the buttons ver reflect, fuzzify, and put in jail. However, they will not do anything unless you write code to make them work. We have some sample images, if you need some images to play with. In addition, we have provided you with some output examples so that you can see what each of the buttons should do when you are done with the assignment. Getting HelpIf you do not know where to start, or if you are completely lost, please see someone immediately. This is a slightly more complex assignment than the previous ones. You may talk to the course instructor, a TA, or a consultant. See the staff page for more information. Do not wait until the last minute, particularly since this assignment is due the same week as the second prelim. Understanding the
|
8 bits | 8 bits | 8 bits | 8 bits |
alpha | red | green | blue |
Suppose we have the green component (in binary) g = 01101111
and blue component
b = 00000111
, and suppose we want to put them next to each other in a single integer,
so that it looks like this in binary:
0110111100000111
This number can be computed using g*28 + b
, but this calculation is inefficient.
Java has an operation that shifts bits to the left, filling the vacated spots with 0's. We give three
examples, using 16-bit binary numbers.
0000000001101111 << 1
is0000000011011110
0000000001101111 << 2
is0000000110111100
0000000001101111 << 8
is0110111100000000
Secondly, operation |
can be used to "or" individual bits together:
0110111100000000 |
| |
and | 0011 |
| |
|
0000000010111110 |
1010 |
||||
is | 0110111110111110 |
is | 1011 |
Therefore, we can put an alpha component alpha
and red-green-blue components
(r, g, b)
together into a single int value (which is 32 bits) using this expression:
(alpha << 24) | (r << 16) | (g << 8) | b
To see this in action, take a look at method ImageProcessor.invert
. For each pixel,
the method extracts the 4 components of the pixel. It then inverts the red, green, and blue
components, reconstructs the pixel using the above formula, and stores the new pixel back in the
image.
ImagePanel
To understand this class, read this section with class ImagePanel
open in DrJava.
A JPanel
is a component that can be placed in a JFrame
. We want a
JPanel
that will contain one Image object. This is the motivation for a class
ImagePanel
that extends JPanel
.
The field image
of ImagePanel
contains (the name of) the Image
object. The constructor places a value in image
and also sets the size and "preferred size"
of the ImagePanel
to the dimensions of the image; this preferred size is used by the
system to determine the size of the JFrame
when laying out the window.
Method paint
is called whenever the system wants to redraw the panel (perhaps it was
covered and is now no longer covered); our method paint calls g.drawImage
to draw the
image. Finally, method changeImageTo
is called whenever our program determines that
the image has been changed (e.g. after inverting the image). Take a look at the method body.
How does one learn to write all this code properly? When faced with doing something like this, most people will read the API specifications and then start with other programs that do something similar and modify them to fit their needs (as we did).
ImageFrame
This class is a JFrame, whichc is associated with a window on your monitor. Since we want a window
(that contains two versions of an image), class ImageGUI
extends JFrame
.
This is a somewhat involved class because it lays out all of the visual components. Take a look
at the following components of class ImageGUI (there are others, which you need not look at now).
The fields originalPanel
and currentPanel
contain the panels for the
original and manipulated images.
There are two constructors. One is given an image. The other has no parameters; it gets the image using a dialog with the user, where the user can navigate on their hard drive and choose which image to work with. This is similar to obtaining a file to read, which you learned about in lab.
setUp
Both constructors call this private method. The method puts the buttons into the JFrame
(using two arrays and a loop to streamline some of the repetitive stuff). We will learn about this
later in class. It then adds a labeled text area, which you will use later. Next, provided that
there is an image, it creates two panels with the image in them and adds them to the JFrame, using
the call add(BorderLayout.EAST, imagebox);
. Finally, it fixes the window location,
makes the JFrame
visible, and "packs" and repaints it.
setDefaultCloseOperation
The call of this method ear the end of setUp fixes the small buttons in the JFrame
so
that clicking the "close" button causes the window to disappear and the program to terminate.
Eventually, you may wish to read Chapter 17 of the text for more information on placing components in a JFrame. There is also a lot of information about GUIs in the lectures on the ProgramLive CD.
ImageProcessor
This class provides all the methods for manipulating the image given to it as an ImageArray
in the constructor. The constructor stores the image in field originalIm and stores a copy of it in
currentIm
.
As the image is manipulated, object currentIm
changes. It can be restored to its
original state by copying field originalIm
into currentIm
. That is
what procedure restore
(near the end of the class) does.
The other methods in this class implement the various buttons in the application. When a button
is clicked, the application calls the appropriate procedure. The procedures invert
,
hreflect
, transpose
, and restore
are complete.
Procedure invert inverts
the image (makes a negative out of a positive, and vice versa).
Note how it processes each pixel; it retrieves it, then computs what its new color components
should be, and finally places the changed pixel back into currentIm
.
Your goal in this assignment is to implement the procedures corresponding to the other buttons.
ImageGui
Now that you know the other classes, ImageGui
actually does very little. It has
a method called main
. This is the method that Java calls when you use the command
run ImageGui
. In this method, it creates instances of the classes above and links
them together (e.g. makes them fields of one another), so that they can function properly.
This class also links the buttons in ImageFrame
with the procedures implementing
them in ImageProcessor
. It does this by providing a listener. We will
talk about listeners later in class. However, you do not need to worry about them right now.
The hardest part of this assignment is simply understanding how all of the classes fit together.
However, the only class that you need to modify is ImageProcessor
. For this assignment,
you need to implement the methods vreflect
, putInJail
, monochromify
,
vignette
, hide
, and reveal
. The method fuzzify
is not required, though we will award extra credit (for students who earn at least a 90 on the
rest of the assignment) for implementing this procedure.
You must write a complete and thorough specification of any method you introduce. It is best to write the specification before you write the method body, because it helps you as you program. This is standard practice in this course. It is a severe error not to do so, and points will be deducted for missing or inappropriate specifications.
You need not write loop invariants, and if you do, we will not grade them. But we encourage you to write them for each loop so that you get used to thinking in terms of the invariant and the four questions discussed in class.
Before you write any code, you should be aware of our guidelines for testing. Using a JUnit
testing class is difficult because you can't easily get access a picture. You do not have to use one.
But this means you will need other creative ways of testing and debugging your code, such as
print statements. You will find the function ImageArray.toString(pixel)
to be
quite helpful in getting a readable String representation of a pixel.
If you add print statements to your code, please remember to remove them before submitting. We will deduct points if you do not remove them.
For a method that consists of several steps, do not write the whole method and then compile and test. Generally you will have many syntax errors and you will waste time. Instead, compile after every line of code you write, and test what you have done if it is feasible to do so.
vreflect
This method should reflect the image around its vertical middle. Look at method hreflect to get an idea about how to do it. This should be relatively straight-forward.
putInJail
To the right you can see the effect of clicking button put in jail. This method draws a
red boundary and vertical bars. This will happen when you implement method putInJail according
to its specifications.
We have given you helper procedure drawHBar
; use it. In the same way,
implement a private procedure to draw a vertical bar. Do not forget its specification.
When finished, open a picture, click buttons put in jail, transpose, and then put in jail again for a nice effect.
monochromify
In this method, you will change the image from color to either grayscale or sepia. The exact one
to chooise depends on the vlaue of parameter c
, which is either 0 or 1. However, do
not NOT use integer constants 0 and 1; instead use the static final fields GRAY
and SEPIA
(The names of variables holding constants are chosen to be all-capitals by
convention). This illustrates the values of constants (e.g. final fields). Using these mnemonic
names rather than the actual numbers makes the program more readable.
Which ever change you are to make, you should first convert the image to grayscale. You do this by calculating the overall brightness of each pixel using a combination of the original red, green, and blue values, defined by:
brightness = 0.3 * red + 0.6 * green + 0.1 * blue
Once you have this value, you set each of the three color components (red, green, and blue) to this
same brightness value.
To simulate a sepia toned photograph (a process used to increase the longevity of photographic prints),
further darken the green and blue channels, setting green to 0.6 * brightness
and blue
to 0.4 * brightness
, producing a reddish-brown tone. As a handy quick test, white pixels
stay white for grayscale, and black pixels stay black for both grayscale and sepia tone.
This manipulation requires that you extract the alpha, red, green, and blue components from each pixel,
construct the new pixel value, and store it. Look at procedure invert
as well as the
discussion on RGB to see how this is done.
vignette
Camera lenses &mdash particularly ones from the early days of photography &mdash block some of the light that should be focused at the edges of a photograph, producing a darkening toward the corners of the image known as "vignetting". This is a distinctive feature of old photographs. In this method, you simulate this effect using a simple formula. Pixel values (in red, green, and blue separately) are multiplied by the following value:
1 - (distance to center)2 / (half of image diagonal)2
Like the monochromification, this requires unpacking and repacking the color components of each pixel. However, for this operation you will also need to know the row and column of the pixel you are processing, so that you can compute its distance from the center of the image.
The last official part of the assignment is the most involved. Steganography, according to Wikipedia, "is the art and science of writing hidden messages in such a way that no one apart from the intended recipient even realizes there is a hidden message." This is different from cryptography, where the existence of the message is not disguised but the content is obscured. Quite often, steganography deals with messages hidden in pictures.
To hide a message, each character of the message is hidden in one or two pixels of an image by modifying the red, green, and blue values of the pixel(s) so slightly that the change is not noticeable to the eye.
Each character is represented using the American Standard Code for Information Interchange (ASCII)
as a three-digit integer. We allow only ASCII characters;
all the obvious characters you can type on your keyboard are ASCII characters, but it does not
support certain foreign characters or Kanji. For the normal letters, digits, and other keyboard
characters like $ and @, you can get its ASCII representation by casting the char to int. For
example, (int)'B'
evaluates to 66, and (int)'k'
evaluates to 107.
We can hide character 'k'
in a pixel whose RGB values are 199, 222, and 142 by
changing each color component so that its least significant digit contains a digit of the
integer representation 107 of 'k'
:
Original Pixel | Pixel with 'k' hidden |
|||||
Red | Green | Blue | hide 'k' , which is 107 |
Red | Green | Blue |
199 | 222 | 142 | → | 191 | 220 | 147 |
This change in each pixel is so slight that it will not -cannot- be noticed just by looking at the image.
Decoding the message, the reverse process, requires extracting the last digit of each
color value of a pixel and forming the ASCII value of a character from the three extracted values.
In the above diagram to the right, extract 1, 0, and 7 to form 107, and cast this integer to
char
. Extracting the message does not change the image. The message stays
in the image forever.
You will write code to hide characters of a message m in the pixels of an image in row-major order, starting with pixel 0, 1, 2, ... Think about the following issues and solve them.
First, you need some way to recognize that the image actually contains a message. Thus, you need to hide characters in pixels 0, 1, 2, ... that have little chance of appearing in a real image. You cannot be sure that an image without a message does not start with the pixels resulting from hiding the characters, but the chances should be extremely small. You should have a beginning marker of at least two pixels that indicates that this file contains a message.
Next, you have to know where the message ends. You can do this in several ways. You can hide the length of the message in the first pixels in some way (how many pixels can that take?). You can hide some unused character at the end of the message. Or you can use some other scheme. You may assume that the message has fewer than one million characters.
Finally, the largest value of a color component (e.g. blue) is 255. Suppose the blue component is 252 and you try to hide 107 in this pixel; the blue component should be changed to 257, but this impossible because a color components are < 255. Think about this problem, and come up with at least two ways to solve the problem; implement one of them.
As you can see, this part of the assignment is less defined than the previous ones. You get to solve some little problems yourself. Part of this assignment will be to document and discuss your solutions.
You should decide how you will solve the problems mentioned above. As you design and implement this
part, write a short essay that documents at least 2 solutions to each of the three problems mentioned
above. This essay should discuss their advantages and disadvantages, and indicate what your solutions
are. Advantages could be: fewest number of pixels used in hiding an image, least time spent in hiding
a message -whatever. When you are finished with this assignment, insert this essay as a
comment at the beginning of class ImageProcessor
.
In writing this essay, please make it readable. For example, we should not have to scroll horizontally to read it, and it should have paragraphs. Fifteen lines of uninterrupted text is not readable.
Do not explain anything in terms of what if-statements and assignments you would write and the like. Your reasoning should be at a higher level than that. Use English, not code.
At the end of the essay, state precisely the format of a hidden message. Tell us what the beginning marker is, how you encode length, and so on.
You should feel free to discuss this part of the problem with the course staff. They will not tell you how to solve these problems, but they will discuss your ideas with you.
getPixels(n)
Before you can do any steganography at all, you must complete (and test) function
getPixels(n)
in class ImageProcessor
. The purpose of this extra method
is to help you with debugging the method hide
. From experience, we know that students
who do not test hide
properly can have great difficulty with this part of the assignment
and do not even complete it correctly. After writing this function, you can type this in the
interactions pane to print the first 9 pixels (we also give the output):
ImageGui b= new ImageGUI();
b.getPixels(9)
"pixels of current image: (104, 160, 161) (101, 157, 156) (098, 153, 147)
(100, 154, 141) (108, 154, 141)
(101, 142, 124) (075, 108, 089) (045, 072, 053) (041, 059, 045)"
You can look at function ImageGUI.getPixels
and see that it calls the one you wrote.
hide
and reveal
You should complete the body of procedures hide and reveal in class ImageProcessor. These two methods will hide a message and reveal the message in the image. When you design method reveal, make sure it attempts to extract the message only if its presence is detected. Feel free to introduce other methods as they are needed, but make them private because they are to be called not by a user but only by your program.
Debugging hide and reveal can be difficult. We encourage writing helper methods to keep code simple and short in hide and reveal. You get to decide which ones to write as you design and implement this assignment. Be sure to specify each method appropriately in a comment before it.
Do not assume that you can debug simply by calling hide and then reveal to see whether the message comes out correctly. Instead, debug method hide and then write and debug reveal. Start with short messages to hide (1, 2, or 3 characters) and check that the pixels contain the message.
When hide and reveal are done, try hiding and revealing a long message (e.g. 1,000, 2,000, or 5,000 characters). Notice any difference in the time to hide and the time to reveal? Can you figure out why? We will discuss this in class at the appropriate time.
Clicking the button Save saves the image in the current directory with name foobar.png
(if the file does not exist). You can hide a message, save, and then reload the image with the
message still there. If we had used extension jpg
, it would not have worked; in
the jpg
format, file compression may cause the image to be altered when saving it.
Procedure writeImage
at the end of ImageProcessor.java
writes the file.
fuzzify
You do not have to implement fuzzify
. However, we will award extra credit if you do.
Extra credit is meant to challenge students that have mastered the rest of the assignment. Therefore,
we will only award extra credit if you make at least 90 on the rest of the assignment.
This method works like the blur tool in Photoshop (for those of you familiar with that program). This
method replaces each pixel of the current image (except those on the edge) by the average of itself
and its 8 neighbors. For example, it replaces the red component by the average of the 9 red components,
and similarly for the blue and green components. Do not average the alpha component or otherwise change it.
Follow the directions given in the java file. Again, look at method
invert
to see how to
pick out the color components from a pixel and to build a new pixel.
As in transpose, the method will have two nested loops, and each iteration of the inner loop will process one pixel. Do not write all the code to process a pixel in the inner loop; instead, write another, private, procedure that processes the pixel and, in the body of the inner loop, call the new procedure. This is done to keep each method simple and short. Also, you can initially stub in the private method (e.g. have it set the pixel to carnelian, RGB (179, 27, 27) ) to facilitate constructing and testing one part of your code at a time, in a top-down, stepwise-refined fashion.
The only file that you should modify is ImageProcessor.java
. At the very top of
this file, you should use non-JavaDoc coments (/* */, not /** */) to provide the following
information:
In addition, you should make sure that class A5
is indented properly. Do this
by hitting tab on every line in DrJava, so that it will auto-indent for you.
Upload the file ImageProcessor.java
onto CMS by the due date: Thursday, April 17th at 11:59PM
You do not need to submit any of the other source code files. Do not submit any files with the extension/suffix
.java~ (with the tilde) or .class. It will help to set the preferences in your operating system so that extensions
always appear.