[Optional Extension] Assignment 5 - Image Processing
Due: Thursday, May 23, 2024, at 10pm
This assignment is completely optional. If you receive at least 80% of the points, you will gain one extra token. If you receive at least 50% of the points (but not quite 80%), you will gain half of an extra token.
You may work alone or with a partner, but you must type up the code yourself. You may also discuss the assignment at a high level with other students. You should list any student with whom you discussed each part, and the manner of discussion (high-level, partner, etc.) in a comment at the top of each file. You should only have one partner for an entire assignment.
If you worked with a partner for the base version of this assignment, you may not work with another partner for the extension unless neither of you plans to resubmit the base version.
You will be extending your original imageProcessing.py
code. It would be a good idea to copy your original code to a imageProcessingExtension.py
file, and work in that file.
Parts of this assignment:
Comments and collaboration
As with all assignments in this course, for each file in this assignment, you are expected to provide top-level comments (lines that start with #
at the top of the file) with your name and a collaboration statement. For this assignment, you have multiple programs; each needs a similar prelude.
You need a collaboration statement, even if just to say that you worked alone.
Note on style:
The following style guidelines are expected:
- Variable names should be clear and easy to understand, should not start with a capital letter, and should only be a single letter when appropriate (usually for
i
,j
, andk
as indices, potentially forx
andy
as coordinates, and maybep
as a point,c
for a circle,r
for a rectangle, etc.). - It’s good to use empty lines to break code into logical chunks.
- Comments should be used for anything complex, and typically for chunks of 3-5 lines of code, but not every line.
- Don’t leave extra print statements in the code, even if you left them commented out.
- Make sure not to have code that computes the right answer by doing extra work (e.g., leaving a computation in a for loop when it could have occurred after the for loop, only once).
Note: The example triangle-drawing program on page 108 of the textbook demonstrates a great use of empty lines and comments, and has very clear variable names. It is a good model to follow for style.
Getting started
Here is an example image after you have completed all parts of this assignment. Make sure to try it out on an image of your own! (Note that it seems that the image has to be a .gif or maybe a .png file unless you install additional libraries.) Also, it’s a good idea to use fairly small images for this assignment, so you can view all eight versions of the image at once. An image no larger than 400x300 would be a good choice – you can use many freely available programs to resize an image.
The original .png
image is here.
The file type .gif
involves “lossy” compression, which means it takes less space to store the picture, but you lose some color detail. This can lead to interesting artifacts using the file therese.gif
instead of therese.png
, as shown below:
The original .gif
image is here.
If your Python install isn’t working with .png
files but you get the above output, your sharpening may well be working fine. Check with me if you’re concerned.
Part 1: Generalizing image processing
The get<Something>Color
functions serve as examples of polymorphism: all take in an image and the x
and y
coordinates of a pixel and return a color. Replace your create<Something>Image
functions with a single function that takes a getColorFunc
function as a new parameter and returns the new image.
Make sure to always use the original image when calling the getColorFunc
function!
Add this to your program, remove the existing create<Something>Image
functions, and then “refactor” your program to call the createImage
function instead:
def createImage(origImage, getColorFunc):
"""
Creates a copy of the original image, and recolors the copy using
getColorFunc for each pixel in the original image.
origImage: an Image object to copy
getColorFunc: the function to call for each pixel
returns: the new image
"""
return None # TODO: implement me!
Part 2: Sharpening an image
Now, you will implement a new get<Something>Color
function to sharpen an image.
Add a function getSharpColor
that sharpens an image by performing a weighted average (unlike your blurring approach from the original assignment). It should multiply the color at (x,y)
by 9
, and subtract each of the surrounding pixels’ colors. Make sure to ensure that each color value ends up in the range 0
to 255
. (You can think of blurring as weighting each of the nine pixels by +1/9.)
Note: You do not have to handle borders. Similar to the original assignment, you should have a one-pixel border of a color of your choice (just pick one).
def getSharpColor(image, x, y):
"""
Chooses a color to sharpen an image by taking a weighted
average across each color channel (R/G/B) of the 3x3 square
of pixels centered at (x,y). The pixel (x,y) is weighted
with +9, and the eight neighbors are weighted with -1.
Note that if the pixel (x,y) is on the border of the image,
it just returns a default color.
returns: the result of color_rgb
"""
# If this pixel is on a border, just return the default color
# Weight this pixel with +9
# Weight its eight neighbors with -1
# Make sure each color value is in the range 0..255,
# and return the color
return None # TODO: implement this function!
You can change your main
function to the following to try out this new functionality:
def main():
# Load the original image
image = Image(Point(0,0), "therese.png")
width = image.getWidth()
height = image.getHeight()
# Create the window to display both images
win = GraphWin("Image Processing", width*4, height*2)
# Draw the original image
image.move(width/2, height/2)
image.draw(win)
# Create the grayscale image
grayscaleImage = createImage(image, getGrayscaleColor)
grayscaleImage.move(0, height)
grayscaleImage.draw(win)
# Create the inverse image
inverseImage = createImage(image, getInverseColor)
inverseImage.move(width, 0)
inverseImage.draw(win)
# Create the sepia image
sepiaImage = createImage(image, getSepiaColor)
sepiaImage.move(width, height)
sepiaImage.draw(win)
# Create the blurry image
blurryImage = createImage(image, getBlurryColor)
blurryImage.move(width*2, 0)
blurryImage.draw(win)
# Create the blurred image
blurrierImage = createImage(image, getBlurrierColor)
blurrierImage.move(width*2, height)
blurrierImage.draw(win)
# Create the sharpened version of the blurred image
deblurredImage = createImage(blurryImage, getSharpColor)
deblurredImage.move(width, 0)
deblurredImage.draw(win)
# Create the sharpened version of the original image
sharpenedImage = createImage(image, getSharpColor)
sharpenedImage.move(width*3, height)
sharpenedImage.draw(win)
# Stick around until the user clicks the window
win.getMouse()
Reflection
# You should be equipped to complete this part after finishing your assignment.
Were there any particular issues or challenges you dealt with in completing this assignment? How long did you spend on this assignment? Write a brief discussion (a sentence or two is fine) in comments at the bottom of your imageProcessingExtension.py
file.
Grading
This assignment will be graded out of 100 points, as follows:
-
5 points - submit a valid
imageProcessingExtension.py
file with the right name -
5 points -
imageProcessingExtension.py
file contains top-level comments with file name, purpose, author names, and collaboration statement -
10 points - code style enables readable programs
-
50 points - Part 1 (generalize
create<Something>Image
functions to singlecreateImage
function; code still works otherwise) -
25 points - Part 2 (sharpen image using
getSharpColor
) -
5 points - reflection in the comments at the bottom of the
imageProcessingExtension.py
file
What you should submit
You should submit a single imageProcessingExtension.py
file on Moodle.