The purpose of this assignment is to give you some practice writing classes in Python, along with becoming comfortable with the concept of binary numbers.
You should spend at least one hour reading through this handout to make sure you understand what is required, so that you don't waste your time going in circles, or worse yet, hand in something you think is correct and find out later that you misunderstood the specifications. Highlight the handout, scribble on it, and find all the things you're not sure about as soon as possible.
It is probably worth spending at least a day thinking about the assignment before writing code of any sort. Try to think through potential problems to catch them before they appear. This will save hours of frustrating debugging time.
In particular, you should make sure you are comfortable with binary numbers before you begin coding. Make sure you can go from a list of bits to a number, to a character and back again before you build any of the methods.
So for we've been dealing with numbers in base 10, where each digit in a number is from 0-9 and represents how many powers of 10 are in the number for the appropriate power.
But there's nothing special about 10 as base. We can write all numbers in any base greater than 1. In particular, we will be dealing with base two. In base two, all of the digits are either 0 or 1. And each spot represents a power of 2. Examples follow:
You can find a more complete introduction here. Make sure to read it before continuing.
We will be considering only ascii encoded text. This will allow us to use the chr and ord built in python functions to convert individual characters to numbers. Once we have numbers, we can build bit representations of these numbers. chr(x) takes an int x (from 0 to 255) and returns a character. ord(x) is the inverse of chr. It takes a character and returns a number from 0 to 255. ord(chr(x)) returns x.
When we have a single integer that represents a character, we can convert it to a list of 8 bits (because that's how many we need to represent any number between 0 and 255). Important: If we have a list L that represents an integer between 0 and 255 we assume thatn L[0] represents the bit that corresponds to 2**7, and L[7] represents the bit that corresponds to 2**0. So if we read our list from 0 to 7 we get the binary representation of the number.
The reason this relates to images, is that the media package gives us access to the individual pixels of an image. Each pixel has a red, green, and blue value that is between 0 and 255. If we have some bits that correspond to a story, we can change an image so that some of the colour bits match the story bits.
By choosing the right colour bits to change, we can make our changes to the original image almost imperceptible. Obviously, we want to choose the bits that correspond to low powers of two first. If we change a value by plus or minus 2**0, that's a much smaller change than by 2**4. We will see this by creating objects that can hide text in images, by encoding various amounts of the bits in the colour. While the order of colours is arbirtrary, we will assume that red comes before green comes before blue. So if we're reading bits our of a pixel, we read the ones from the red colour first, then the green, then the blue.
You are to write code that creates a class that can take strings and hide them in images. Your code will allow you to hide with varying degrees of precision. In particular, you may use from 1 to 7 bits of the colour to hide your text. The information that specifies how many bits you are using will be held in the last bit of each colour of the first pixel of the image. We will not use the first pixel to hide any information about the text itself.
This assignment will have you writing both methods and functions. You are free to define your own if you wish. You will also be using media. Code stubs can be found here: steganograph.py
(red_value%2)*2**2+(green_value%2)*2+(blue_value%2)
__iter__
method. In otherwords, if you use a for loop, you will get the right order.break
or continue
statements.
Any functions that do will receive a mark of zero.
We are imposing this restriction (and we have not even taught you these
statements)
because they are very easy to "abuse," resulting in terrible code.
While this assignment is much more reasonable than assignment 2, you'll still need a good strategy for how to tackle it. Here is our suggestion. First download the code from steganograph.py.
steganograph_name.enc_text("string")
more than once so long as there is space in the image.Picture.copy()
- returns a copy of the picturePicture.get_pixel(x,y) - returns the pixel at the specified x and y co-ordinates.-
Pixel.get_red()
- returns the amount of red for that pixel. Similar methods exist for green and blue.
Pixel.set_red(x)
- sets the amount of red for that pixel. Similar methods exist for green and blue.
- The following methods are useful for testing, more than the code itself:
Picture.__init__(picture_filename)
- creates a Picture object based on the picture at the given filename which is a string. Check help for other ways of using the constructor.
Picture.set_title_and_filename(string)
- sets the title and filename of the picture to the given string.
Picture.save()
- writes the given picture to a file with the filename set by the above method.
- Some files that may be useful:
- A short story by J.L Borges.
- The story hidden using 2 bits per color.
- The story hidden using 6 bits per color.
- Correct code should be able to decode the two images above to produce a string that starts with the story in the text file.
These are the aspects of your work that we will focus on in the marking:
Correctness: Your code should perform as specified. Correctness, as measured by our tests, will count for the largest single portion of your marks.
Docstrings: For each function that you design from scratch,
write a good
docstring
.
(Do not change the docstrings that we have already written for you.)
Make sure that you read the Assignment rules page for some important
rules and guidelines about docstrings.
Internal comments: Within functions, the more complicated parts of your code should also be described using "internal" comments. For this assignment, internal comments will be more important than on assignment 1.
Programming style: Your variable names should be meaningful and your code as simple and clear as possible.
Formatting style: Make sure that you read the Assignment rules page for some important rules and guidelines about formatting your code.
You must hand in your work electronically, using the MarkUs system. Log in to it here, using your cdf login and password.
To declare your partnership, one of you needs to invite the other to be a partner, and then they need to accept the invitation. To invite a partner, navigate to the Assignment 2 page, find "Group Information", and click on "Invite". You will be prompted for the other student's cdf user name; enter it. To accept an invitation, find "Group Information" on the Assignment 2 page, find the invitation listed there, and click on "Join". Note that, when working in a pair, only one person should submit the assignment.
To submit your work, again navigate to the Assignment 2 page, then click on the "Submissions" tab near the top. Click "Add a New File" and either type a file name or use the "Browse" button to choose one. Then click "Submit".
For this assignment, hand in just one file:
steganograph.py
Once you have submitted, be sure to check that you have submitted the correct version; new or missing files will not be accepted after the due date. Remember that spelling of filenames, including case, counts. If your file is not named exactly as above, your code will receive zero for correctness.