CSC108H Assignment 4, Summer 2011

Due Monday, August 15, 11:55 pm

Introduction

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.

Binary Numbers

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.

Steganography

Steganography is the art of hiding messages in ways that people won't expect. We will be doing some basic steganography, and hiding very simple text in pictures. The reason that binary numbers are important, is that we're going to be using them to store our text and our images.

Text types and Images

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.

Your Task

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

Additional requirements

How to tackle this assignment

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.

Principles:

Advice:

This program can be broken down into several separate aspects. Some of these follow:
  1. Read this handout thoroughly and carefully, making sure you understand everything in it, particularly how the advanced statistics are calculated. Read the code stubs to make sense of the following advice.
  2. Start with the functions (and not the methods). These are more easily tested, and if they are working allow you to easily go from list of bits to strings and back.
  3. When trying to encode and decode text, use lists of bits as an intermediate step. So to encode text, first convert the text to bits, and then modify the picture using those bits. To decode, extract the bits from the picture, and then convert those bits to a string.
  4. Note that you should be able to call steganograph_name.enc_text("string") more than once so long as there is space in the image.
  5. The shell can be useful for testing half-written functions.
  6. It may be worthwhile to use nose to test the more advanced stuff, just because otherwise there's a lot of work to be redone in loading pictures from files, or creating pictures.
  7. useful media methods.
  8. Picture.copy() - returns a copy of the picture
  9. Picture.get_pixel(x,y) - returns the pixel at the specified x and y co-ordinates.
  10. Pixel.get_red() - returns the amount of red for that pixel. Similar methods exist for green and blue.
  11. Pixel.set_red(x) - sets the amount of red for that pixel. Similar methods exist for green and blue.
  12. The following methods are useful for testing, more than the code itself:
  13. 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.
  14. Picture.set_title_and_filename(string) - sets the title and filename of the picture to the given string.
  15. Picture.save() - writes the given picture to a file with the filename set by the above method.
  16. Some files that may be useful:
  17. A short story by J.L Borges.
  18. The story hidden using 2 bits per color.
  19. The story hidden using 6 bits per color.
  20. Correct code should be able to decode the two images above to produce a string that starts with the story in the text file.

Marking

These are the aspects of your work that we will focus on in the marking:

Submitting your assignment

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:

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.