Purpose: to practice nested lists as a data structure and nested for loops as a way to process them.
Overview ¶
In this lab, you will build a simple image processing app that applies various transformations to images.
Setup ¶
This lab builds on what you learned last week about Brython. We’re adding a new tool called radiant (GitHub page) that makes it easier to write Brython apps. To install it, select Tools > Manage packages in Thonny, search for radiant-runtime-bridge, and click Install.
Download these files into a new lab11 folder and update the header documentation in image_processing.py as usual.
- image_processing.py — image processing functions (you’ll edit this)
- image_app.py — web app UI (you’ll make small additions here)
- image_helpers.py — helper functions (do not modify)
- sydney_sunset.png — sample image
Run image_app.py in Thonny. A browser tab will open automatically.
Familiarize yourself with the interface. You can get a source image in three ways: Capture a photo from your webcam, click Use Sample Image to load the included photo, or use the file chooser to upload any image from your computer. Once you have a source image, click Process to apply an operation to it.
Read through image_processing.py and skim image_app.py — image_processing.py contains the image processing logic (your main focus) and image_app.py contains the user interface. You don’t need to read image_helpers.py.
Look carefully at how brighten works in image_processing.py. It uses nested for loops to visit every pixel and build a new image. Each pixel is a list [red, green, blue] where each value is 0–255.
Task 1: Add a darken function ¶
| Darkened (4×) | Original | Brightened (4×) |
|---|---|---|
![]() |
![]() |
![]() |
Task 2: Fix flip_horizontal ¶
For each new function you add below, you’ll also need to wire it up in image_app.py: add its name to the operations list in __init__ (# ── ①) and add a matching elif branch in on_process (# ── ②).
Task 3: Refactoring practice — volume control ¶
Before touching the image code, let’s practice a useful technique on a simpler example.
Here are two functions that adjust volume on a 0–100 scale:
def louder(volume):
return min(100, volume + 10)
def softer(volume):
return max(0, volume - 10)
These two functions are almost identical — the only difference is + 10 vs. - 10, and min vs max. Duplicated code like this is a problem: if you ever need to change the logic (say, changing the range to 0–200), you’d have to remember to change it in both places. A better design factors out the difference as a parameter.
Task 4: Refactor brighten and darken ¶
Look at your brighten and darken functions in image_processing.py. They have the same duplication problem as louder/softer — one adds 25, the other subtracts 25, but otherwise they’re identical.
Task 5: New image processing functions ¶
Optional extensions ¶
If you finish early, try one or more of these.
Snow: write a snow(image) function that replaces every pixel with a random [r, g, b] color. You’ll need from random import randint.
Sepia: sepia toning gives images a warm brownish antique look. Each output channel is a weighted sum of the input channels:
| Output | Formula |
|---|---|
| Red | int(0.393*r + 0.769*g + 0.189*b) |
| Green | int(0.349*r + 0.686*g + 0.168*b) |
| Blue | int(0.272*r + 0.534*g + 0.131*b) |
Use min(255, ...) to clamp values that exceed 255.
Submission ¶
Submit image_processing.py and image_app.py on Moodle. (No need to submit image_helpers.py since you didn’t edit it.)


