This is an optional, individual assignment. Any solutions you submit will be added to your final grade as extra credit.
The theme of this assignment is transforming images. You may find it helpful to review the materials we learned about images, including the image processing lab.
Problem 1: Simple Transformations
Write and document the following three functions:
only_red(img). Creates and returns a new image containing only the red aspects of the original image. You can do this by setting all the values in the green and blue values for each pixel to 0.
deuteranopia(img). Creates and returns a new image that crudely simulates what a red–green colorblind (“deuteranopic”) viewer would see when looking at the image. To do so, set both the red and green pixel values to be the average of the original red and green pixel values. That way the pixels
[128, 0, 0]and
[0, 128, 0]both come out as
[64, 64, 0].
negate(img). Creates and returns a new image that is a conversion of the original image to its photographic negative form. Modify all colors in a way that turns black into white, white into black, dark colors into light, light into dark, etc. (Don’t worry if you don’t exactly match the color correspondence between a photo negative and its print.)
Finally, write (but do not document) a testing function
test_simple(img) that calls the above three functions (separately) on the given image and displays the original along with all three modified versions with appropriately descriptive window titles.
Place all of your code for this problem in a file
Problem 2: Image Convolutions
One of the most common ways to apply certain effects to an image is by doing a convolution of the image with a kernel matrix. The kernel matrix encodes a localized transformation that will be applied to every pixel in the image. For example, consider the following 3x3 matrix.
The above matrix encodes the sharpening transformation and causes pixels to become more distinct from their immediate neighbors. Almost every kernel matrix is an NxN square and has an odd side-length N. (e.g. 3x3 and 5x5). The way an NxN kernel matrix is applied to an image is as follows.
- To compute the new RGB values of the pixel at location
(x,y), you look at the NxN neighborhood of pixels surrounding the point
(x,y). In other words, all pixels in the range
- To compute the R value of
(x,y), you iterate over the entire NxN neighborhood surrounding
(x,y), multiplying the corresponding matrix value with the R component of the current pixel, and sum up the results.
- You repeat this process to compute the G and B values of the pixel at
This process is visualized in the following animation for the 3x3 sharpening transformation mentioned above. The animation only shows computing one component of each pixel, and the input image is on the left, the kernel matrix is the 3x3 matrix, and the new image produced is on the right.
Notice how the value of each new pixel is computed by multiplying the kernel matrix with the values of the surrounding pixels and taking the sum.
There are many websites that demo the effects of convoluting matrices with images. Check out this website for some examples.
Since the pixels around the edges of the images do not have a sufficiently large neighborhood, you might notice that in the above animation the pixel values outside the image wrap around to the opposite side of the image. In this assignment, you may implement any edge handling strategy you like, but I recommend that you treat pixels outside the image as black (i.e.
[0, 0, 0]). This is a common strategy and is quite simple and will work for our purposes. This may cause a dark border to appear around the output image depending on the choice of kernel matrix.
Implementing Image Convolution
Write (but do not document) a function called
convolve(img, kernel) that takes an image
img and an NxN matrix
kernel and creates and returns a new image by convolving it with the kernel.
To do this, you will need to do something like the following:
- Create a new image of the same size.
- Iterate over each pixel of the new image
- Create sum variables for each component R, G, and B that are initialized to zero
- Iterate over each cell of the kernel
- Multiply the R component of the pixel in the neighborhood corresponding to the current kernel cell and add it to the sum for R
- Do the same for the G and B components
- After you are finished iterating over the kernel, make sure the R, G, B values are within the range 0..255 and are integers and then create a new pixel using the
color_rgbfunction and insert it into the correct place in the new image
Note that for large images this function takes a while to finish. For example, my sample solution takes approximately 30 seconds to compute a 3x3 matrix on the the cat.png we were looking at earlier.
Place your code in a file named
>>> kernel = [[ 0, -1, 0], [-1, 5, -1], [ 0, -1, 0]] >>> new_img = convolve(img, kernel) >>> print(new_img)
>>> kernel = [[1/9, 1/9, 1/9], [1/9, 1/9, 1/9], [1/9, 1/9, 1/9]] >>> convolve(img, kernel) >>> new_img = convolve(img, kernel) >>> print(new_img)
>>> kernel = [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]] >>> convolve(img, kernel) >>> new_img = convolve(img, kernel) >>> print(new_img)
How to Turn in Your Code
- Once you are finished with the assignment, you should go to https://codepost.io and log into your account.
- Go to the CS 65 course.
- Go to Extra Credit Assignment and upload all of your files.
- After you submit the assignment, you should be able to reopen it and see a Partners tab at the top of the submission page. Send the link to your partner and have them open it while logged in with their account.
- That’s it! You’ve just submitted your assignment.
Important Grading Criteria
I will evaluate your work with the following criteria in mind.
- Correctness. Does your code do what is asked? (e.g. named correctly, input and outputs correct values, etc.)
- Formatting. Is your code formatted so that it is easy to understand?
- Elegance. Is your code concise and elegant?