Due: Thursday, October 29th in class
In this lab, you'll write a program that extracts one image that is hidden inside another. You'll then create a second program to encode your own secret images. And finally, you'll make a few more image filters.
Consider the following image:
Clearly this is a picture of fruit... or is it? Look closely. See anything? Chances are nothing about this picture seems amiss. But things are not always as they seem...
Steganography is the science (or possibly art) of hiding one image within another. In this case, secret.bmp has a different, unrelated image hidden within it. The encoding used here is actually not all that complex. This image was created from the image fruit.bmp, although it is unlikely you'd be able to see a difference; the only change lies in the lowest order bit of each color channel.
What does that mean? Well, suppose one pixel in the original fruit.bmp image had a red value of 13. Recall that in binary this number would be expressed as 00001101. (Note: I've written 8 digits, since we've seen that color channels can go from 0 to 255 which is 11111111 in binary.) Now this same pixel in secret.bmp is either going to be 00001101 (the last digit kept as a 1) or 00001100 (the last digit switched to a 0). In other words, the pixel with a red value of 13 in fruit has either a red value of 13 or 12 in secret.
How did I choose whether to change the last bit to a 0 or not? I decided based on whether the corresponding pixel in the image I was encoding had a little red (0) or a lot of red (1). Similarly for the other color channels. The end result was that I kept the overall look of fruit the same (any pixel changed by at most 1 in any given color channel) and yet I've encoded 3 bits of data per pixel. Let's decode it!
Create a program called Decode.py.
Decode.java should read in the file secret.bmp (linked above and also in the course media folder). Create a new Image based on secret.bmp in which you zero out all but the lowest order bit in any color channel. That is, all odd color values should be set to 1 and all even color values should be set to 0. For example, if one pixel is given by color values (143, 210, 51) then in the new image the same pixel should have color values (1, 0, 1). We've just extracted the lowest order bit. Display this image.
Not so impressive, right? It pretty much looks like a solid black image. Not so surprising, since no pixel has a brightness greater than 1 in any given channel. What we need to do now is brighten this up. We'll do that by multiplying all color values by 255. So 0, which was representing dark, will stay dark. But 1, which was representing bright, will become very bright. For example, the color of the pixel given above should now be (255, 0, 255).
Now display this. If you see a recognizable image (that isn't fruit), congratulations! You've decoded your first secret image!
Before you turn this in, make sure you ask the user for the filename to decode.
Now your goal is to create the program that does the encoding. Make a new program called Encode.py. Encode.py should read in two images of your choice. One will be the message image, and one will be the base image. To encode, you'll want to first zero out the smallest bit in each color channel of the base image (i.e. decrease the value by 1 if it is odd). Then you'll want to set it to 0 or 1 depending on whether the corresponding color channel of that pixel was high or low in the message image. For example, if the red value of the pixel in the message image was greater than 127, add 1 to that pixel's red value in the base image. Otherwise, leave it unchanged. Once you've done this for every color channel and every pixel, write the image to mysecret.bmp. Make sure your encoding program works by using your decoding program on mysecret.bmp.
Bonus: Make Decode2.py and Encode2.py that use the lowest two bits to hide an image. Using an extra bit should allow you to encode more colors (4 values per channel rather than just 2). As a result, once you've decoded the image, it should have a nicer range of colors (the previous program was only capable of encoding 8 different colors: black, white, red, green, blue, magenta, cyan and yellow).
Out of the following list of possible image filters, implement two. Then create one more filter of your own design. Be creative!
Blur: Set each pixel to be the average of the nearby pixels. You'll probably want to create a second Image object that you'll write to.
FourTimes: This program should first shrink the original image down to half its width and half its height, placing this smaller version in the upper left-hand corner. Then copy that quadrant to the other 3 quadrants.
UpContrast: Increase the contrast in an image. Compare each color value of each pixel to 127, and double the difference. For example, if a pixel has a red value of 137, that is 10 greater than 127, and so you should set the color value to 147 (20 greater than 127). Similarly, if it had a value of 117, the new color value should be set to 107. Be sure you never set the color value of a pixel to be negative or greater than 255.
OverlayFlip: First flip the image over the vertical axis. Then overlay the original image with its flipped version. To overlay, you really just want to take the average of corresponding pixel values.
EdgeFind: Set the brightness of each pixel based on the magnitude of the difference between the corresponding pixel and its neighbors. That is, if one pixel is really similar to its neighbors, give it either a very small or a very large color value, while if a pixel is very different from its neighbors, do the opposite.
Examples of a starting image and the result of these five filters are shown here:
(Original, Blur)
(FourTimes, UpContrast)
(OverlayFlip, EdgeFind)
Your folder called Lab07 should contain: