Image Processing 3

Table of Contents

This assignment is to be done individually. You can talk to other people in the class, me (Dave), the prefect, and lab assistants for ideas and to gain assistance. You can help each other debug programs, if you wish. The code that you write should be your own, however, and you shouldn't hand a printout of your program to others. See the course syllabus for more details or just ask me if I can clarify.

We will use anonymous grading on Moodle, which means that the grader won't see your name until after the grading is done. This is an easy way to help add an extra element of fairness to the grading. Therefore, make sure your name doesn't appear on your actual submission. When you submit via Moodle, it will know you are. Thanks!

1 Your assignment

Create a directory called image3 for this assignment. To facilitate reading in, storing, and manipulating image files in "standard" formats (like .jpg, .gif, and .png), we will again use images.py. Copy images.py into your directory from the above link or from the course website. Here is the documentation for images.py.

Create a Python module called photolab3.py. It should contain the following functions:

1.1 Scaling the image

Create a Python function defined as:

scale(image, scaling)

Scales your image. If scaling is 2, for example, each side of your new image should be double the size of the original. Likewise, if scaling is 0.25, each side of your new image should be 1/4 the size of the original. If scaling is less than or equal to 0, do no image work at all and return None. Otherwise, return the new image.

Hint, of sorts: You've got a choice to make. Do you loop over all the pixels in the old image, and figure out where they belong in the new image? Or do you loop over all the pixels in the new image (which are initially unassigned), and for each one figure out which is the right pixel in the old image to copy in? One of these approaches is much easier than the other. Definitely spend time in advance thinking about what kinds of calculations you would have to do for each of these approaches, and ask for help if you need it. It would be a shame to lose a whole lot of time programming up the "hard" approach to abandon it later.

Question I've sometimes gotten from students in the past: What if the new image is bigger than the original? Do I need to interpolate between pixels to calculate appropriate pixel values and so on?

Answer: No. Every pixel on the new image should just be a copy of some pixel on the old. The challenge is in figuring out which one. If the new image is smaller than the old, then some of the pixels on the original will be ignored. If the new image is larger than the old, than some of the pixels from the original will be duplicated.

1.2 Blurring the image

Create a Python function defined as:

blur(image, radius)

Blurs an image. For each pixel, average all of the pixels within a square centered at that pixel and with a "radius" as given. In other words, if your radius is 3, your square should go 3 pixels left, 3 pixels right, 3 pixels up, and 3 pixels down from the center to form a 7 x 7 square. Make sure to put all of your results in a new image, instead of overwriting your original as you go; otherwise, your blurred pixels will cascade on top of each other. You'll want to work with a small image for this task, as blurring in Python can take some time. Here is picture of me which is nice and small. Nonetheless, some approaches to programming this result in much slower algorithms than others. On our department x2go server, I can blur this small image in about 2 seconds with a radius of 3 and about 5 seconds with a radius of 10. Likewise, if I run it on a 640x480 sized image (such as any of the images from the last assignment), I can blur a radius of 3 in about 20 seconds. You do not need to achieve my precise timing numbers, but you need to do something reasonable. Any technique that works will receive most of the correctness points for this problem (assuming it runs in under one minute for a radius of 3, I can't keep the grader testing these indefinitely), but you need to end up with timing more-or-less close to my numbers if you wish to earn all of the correctness points.

You can choose to ignore blurring around the edges of the image that is in the range where the blurring square would run off the edge of the image. Alternatively, if you like, you can blur those pixels by using whatever pixels you do have in range.

2 Test your code

Test your program by an appropriate main() function, which you should also submit as part of your photolab3.py. Feel free to use your own pictures if you'd like. We will test your program similarly.

3 Design and style

Make sure that your program follows good design and style. See the guidelines from recent assignments. If you really want to learn lots about Python style, the official style guide for Python is a great read.

4 The last point

If you complete the above functions (and other things like style, etc. are correct), you will receive nearly all of the points for this assignment. You should feel proud and good about yourself that you have gotten this far, and feel free to stop here! If you want to try to earn the remaining point, write one more function:

dither(image)

Dithering is a technique used when you want to print a gray picture in a place such as a newspaper where no shades are available. Instead, you need to use individual pixels of black and white to simulate shades of gray. The popular Floyd-Steinberg algorithm for dithering is as follows:

  • Loop over all pixels, running across one row at a time.
  • For each pixel: if its value is larger than 128, set it to 255 (pure white). Otherwise, set it to 0 (pure black). Record the "error", which is the old value of this pixel minus the new one.
  • This "error" represents how much blackness we have added to this pixel. Distribute this blackness to adjacent pixels as follows:
    1. Add 7/16 of the error to the pixel immediately to the right ("east").
    2. Add 3/16 of the error to the pixel immediately diagonally to the bottom left ("southwest").
    3. Add 5/16 of the error to the pixel immediately below ("south").
    4. Add 1/16 of the error to the pixel immediately diagonally to the bottom right ("southeast").
  • Any error to be distributed to pixels that are out of range should be ignored.

Dithering should only be done on gray images, so convert the image to gray first inside your dither function. You can use the image method copyGray() to convert an image to gray.

In order for dithering to work, you must make your changes to the same image that you are looping over. Dithering by reading one image and making your changes on a copy will not work correctly, because the "error" never gets a chance to accumulate. In other words, make sure that you make a gray scale copy of the image first, and then do all dithering work (looping, reading, writing, etc.) on the copy itself. If your image looks really weird, this may likely your problem. But remember, you should not change the original image that is passed to this function.

dither.jpg

5 Submit your work

When finished, zip up your code and submit your work through Moodle.

Good luck, and have fun! Remember that lab assistants are available in the evenings in CMC 102 and CMC 306 to help out if you need it, and you can attend prefect sessions as well.

Author: Dave Musicant

Emacs 24.5.1 (Org mode 8.2.10)

Validate