Overview

The blur_image.py program is split into a number of functions, each with a specific role. Each problem in this assignment corresponds roughly to each of the functions, with the last being modifications in the main function to tie everything together.

We’ll work from the bottom up, starting at the individual pixel level and working our way up to the high-level blur algorithm. This creates a hierarchy of function calls; for example, get_pixel_at will be called by average_of_surrounding, which itself is called by blur, which is finally called by main.

By using the given test_blur_image.py program, you’ll be able to verify your work as you progress through the assignment. You’re not expected to read and understand the code in this file, but you may find it helpful to comment/uncomment/reoder the test function calls at the bottom of test_blur_image.py.

Problem 1: Accurately reading individual pixels

For this problem, you should implement the get_pixel_at function.

This function should return the pixel value in the given pixel_grid at row i and column j or 0 if there is no row i or no column j. This function can be done in as few as 2-3 lines of code (not counting comments), but we’ve also seen valid solutions be 6-10 lines.

For example, consider the grid

pixel_grid = [ [1, 5, 61], [4, 3, 2], [10, 11, 100] ]

Note

(Refer to the section on Pixel Grids in the background for detail on this structure.)

Calling get_pixel_at(pixel_grid, 0, 2) would return 61, representing the value at row index 0 and column index 2.

Calling get_pixel_at(pixel_grid, 3, 3) would return 0, as the grid coordinates (3, 3) are “out of bounds.”

Once you’ve completed get_pixel_at (or even have made partial progress), you should run test_blur_image.py. If you see the message “All tests passed for get_pixel_at!” then you’re probably good to move on to the next problem!

Note: the test program tests all of the functions we expect you to write. So if you see a Test Error message, be sure to double check which function it’s referring to.

Problem 2: Averaging a pixel

For this problem, you should implement the average_of_surrounding function.

This function should return the average of the values of the pixel at location i,j and the eight pixels surrounding it in the given pixel_grid. This is the algorithm described in the background section. The return value from the function should be an integer, meaning in your average calculation you should use the truncated division operator, //. Our solution for average_of_surrounding has about 5-6 lines of code, but, as above, valid solutions exist that are longer or shorter.

For example, consider the same pixel grid from Problem 1:

pixel_grid = [ [1, 5, 61], [4, 3, 2], [10, 11, 100] ]

When calling average_of_surrounding(pixel_grid, 1, 1), you should expect a return value of 21. Note the truncated division: even though 1+5+61+4+3+2++10+11+100=1971 + 5 + 61 + 4 + 3 + 2 + + 10 + 11 + 100 = 197 and 1979=21.8ˉ\frac{197}{9} = 21.\bar{8}, pixel values must be whole integers.

Likewise, when calling average_of_surrounding(pixel_grid, 0, 2), the return value should be 7.

Note

average_of_surrounding must use the get_pixel_at function you implemented in Problem 1.

Once running test_blur_image.py shows “All tests passed for average_of_surrounding!” you should be ready to move on to Problem 3!

Problem 3: Blurring a grid of pixels

Given a pixel_grid (a rectangular grid of pixels), blur should return a new grid of pixels of the same size and shape. For each pixel in the given pixel_grid, you should compute its (truncated) average and store it in the same location in the new grid you are creating. Finally, you should return the new grid. You will probably need to add anywhere from 8-15 lines of code to this function. We strongly recommend starting with an empty list and appending new values to the list to create your new grid, as other approaches (e.g. copying) are likely to result in hard to track down bugs.

For example, given

pixel_grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

blur should return the new, blurred grid:

[[1, 2, 1], [3, 5, 3], [2, 4, 3]]

As with the previous problems, if you’ve correctly implemented the blur function, running test_blur_image.py should print out “”All tests passed for blur!”

Problem 4: Reading grids from CSV files

In this problem, you’ll implement the read_grid function. This function must use the given csv_line_to_pixels helper function (note the import at the top of blur_image.py).

Info

In programming, we’ll often write so-called “helper functions.” These are functions that (usually) do very small tasks and potentially only show up in one or two other places. These functions help make other, more complex functions shorter and easier to understand. In this assignment, csv_line_to_pixels and get_pixel_at are “helper functions”.

This function should take a string as its argument, representing the path to a file, and then returns a nested list.

For example, the call read_grid("test_grids/small_grid.csv") should return:

[ [0, 0, 0], [0, 9, 0], [0, 0, 0]]

You are allowed to use either of the following patterns for reading files in Python:

with open(...) as f:
    ...  # read file contents
f = open(...)
...  # read file contents
f.close()

Our solution to read_grid uses 5-8 lines of code. As is the case in any of our assignments and problems, your solution may use fewer or more.

Once you’ve implemented read_grid, running test_blur_image.py should both print out “All tests passed …” for all of the expected functions and “All tests passed!”.

Problem 5: Main

Thus far, running blur_image.py still doesn’t actually do anything. That’s because we need to implement the main function!

Note

In most programming projects, there will be a main function, or similar, that represents what’s called the “entry point” for the program. In programs like what we write in this course and similar programming courses, main should represent a concise overview of the steps the entire program takes.

Once we’re done writing it, the main function will do three things:

  1. Generate a “pixel grid” based on a file name given on the command line / terminal. (This has already been written for you; it calls the read_grid function written in Problem 4.)
  2. Call the blur function.
  3. Save the results to new files.

Steps 2 and 3 are called out in the main function with comments that look like:

...  # REPLACE THIS LINE ...

Step 2 should look like a call to the blur function, saving its return value in a new variable.

Step 3 should look like a call to the two given functions write_image and write_grid (note the import statement at the top of blur_image.py). The output_image_filename and output_grid_filename variables should be passed to the matching function.

Code Quality

Info

Make sure to provide descriptive comments for each function in docstring format

Warning

If you discuss this assignment with one or more classmates, you must specify with whom you collaborated in the header comment in your submission. Otherwise, you may be guilty of academic misconduct and face consequences. Note that you may not collaborate in a way that is prohibited, even if you cite the collaboration.

Your assignment should pass two checks: flake8 and our code quality guidelines. The code quality guidelines are very thorough. For this assignment, the most relevant rules can be found in these sections:

Submitting

Warning

Your file must be named blur_image.py in order to be graded correctly. Please comment out any assert statements before submitting to gradescope.

Warning

By submitting your work, you are attesting that all code in the submission is entirely your own, and is not copied from un-cited sources including, but not limited to, AI or in-editor “chat” features. See the course’s academic misconduct policy for additional examples and details.

Submit blur_image.py on Gradescope under the assignment Homework 3.

HW3 - Homework 3

Initial Submission by Friday 05/09 at 11:59 pm.

Submit on Gradescope