Homework 03: Laplacian Pyramids
Total: 5 points
Submission: Gradescope Programming Assignment
Work: Individual
Objective
In this homework, you will implement a compact multiscale image representation based on the Laplacian pyramid. You will:
- construct a Laplacian pyramid from a grayscale image;
- reconstruct the original image from the stored residuals and coarsest Gaussian level;
- create a lossy representation by discarding small residual coefficients; and
- quantify how sparse the residual representation becomes after thresholding.
Use the course cv Conda environment and OpenCV (cv2) for the pyramid operations described below.
Do not use generative AI to solve this homework. The goal is to make sure you understand how multiscale residual representations are constructed and reconstructed.
Starter file
Complete the provided hw03.py file. Do not rename the required functions or change their arguments because the Gradescope autograder calls them directly.
The required functions are:
build_laplacian_pyramid(image, levels)
reconstruct_laplacian_pyramid(pyramid)
threshold_laplacian_pyramid(pyramid, threshold)
residual_nonzero_fraction(pyramid)Part 1 — Build a Laplacian pyramid
Complete:
def build_laplacian_pyramid(image, levels):
...The input is a 2-D grayscale numpy.float32 image with intensity values in [0, 1]. The argument levels is the number of residual levels to construct.
Return a list with this structure:
[L0, L1, ..., L_(levels-1), G_levels]
At each level:
- Smooth and downsample the current Gaussian image using
cv2.pyrDown(..., borderType=cv2.BORDER_REFLECT). - Expand the coarse image back to the exact size of the current image using
cv2.pyrUp(..., dstsize=current.shape[::-1]). - Compute the signed residual:
\[ L_i = G_i - \operatorname{expand}(G_{i+1}). \]
- Continue from the coarse image.
Store the final coarse Gaussian image as the final element of the list.
Requirements:
- all returned arrays must have dtype
numpy.float32; - preserve signed residual values; do not clip them to
[0, 1]; - the implementation must work with odd-sized and non-square images; and
levelswill be at least 1 in graded tests.
Part 2 — Reconstruct the image
Complete:
def reconstruct_laplacian_pyramid(pyramid):
...Start from the smallest Gaussian image stored at pyramid[-1]. For each finer level:
- expand the current image to exactly the residual’s width and height using
cv2.pyrUp()anddstsize; and - add the residual at that level.
In other words,
\[ G_i = \operatorname{expand}(G_{i+1}) + L_i. \]
With an unmodified Laplacian pyramid, the reconstruction should match the original image apart from small floating-point roundoff.
Return a numpy.float32 array.
Part 3 — Threshold residuals for lossy compression
Complete:
def threshold_laplacian_pyramid(pyramid, threshold):
...The residual levels often contain many values close to zero. To create a simple lossy representation, set small residual coefficients to exactly zero.
For every residual level pyramid[:-1]:
abs(coefficient) < threshold -> 0Important details:
- the comparison is strictly less than the threshold;
- do not threshold the final coarse Gaussian image;
- return an independent copy: a new list containing a separate NumPy array for every pyramid level;
- do not modify the original pyramid or share its arrays with the returned pyramid; and
- return
numpy.float32arrays.
For this list-of-arrays structure, an appropriate independent copy is:
new_pyramid = [level.copy() for level in pyramid]You may then threshold the copied residual arrays. You do not need to use copy.deepcopy().
As the threshold increases, more detail coefficients will typically become zero and the reconstructed image will lose more fine detail.
Part 4 — Measure residual sparsity
Complete:
def residual_nonzero_fraction(pyramid):
...Compute
\[ \text{nonzero fraction} =\frac{\text{number of nonzero residual coefficients}} {\text{total number of residual coefficients}}. \]
Count coefficients only in pyramid[:-1]. Do not include the final coarse Gaussian image.
Return a Python float in [0, 1].
A smaller nonzero fraction means that more residual coefficients are zero and the representation is more sparse. This statistic is only a simple proxy for compressibility; it is not the actual compressed file size.
Run your program
Activate the course environment and run the script with a JPG/JPEG or PNG image. We recommend reusing your HW01 headshot so you can visually compare how increasing the threshold changes the reconstruction:
conda activate cv
python hw03.py my_image.pngThe provided script constructs a three-level Laplacian pyramid and compares:
- the original image;
- exact reconstruction from the unmodified pyramid; and
- lossy reconstructions after thresholding the residuals at
0.01,0.03, and0.08.
A successful run must:
- print the maximum exact-reconstruction error;
- print the residual nonzero fraction for each threshold; and
- save a viewable
hw03_output.pngcomparison image.
Look at how the residual nonzero fraction and reconstructed image quality change as the threshold increases.
What to submit
Submit exactly one file to Gradescope:
hw03.py
Do not submit your input image, hw03_output.png, screenshots, or a PDF report. Your headshot is only for local testing; the autograder runs your functions and script with instructor-provided images and arrays.
Submission limit — important
You may make at most 5 Gradescope submissions for this homework. Test and debug thoroughly in your local cv environment before submitting.
- Submissions 1–5 are graded normally.
- Submission 6 and every later submission receive a score of 0 for the homework.
- A Gradescope infrastructure failure marked as an autograder error does not count against the five-submission limit.
Grading
HW03 is worth 5 points and is fully autograded.
The autograder checks:
- required functions and interfaces;
- Laplacian-pyramid construction, including odd and non-square image sizes;
- exact reconstruction from the stored pyramid;
- residual thresholding and preservation of the coarse image;
- residual nonzero-fraction calculation; and
- whether
python hw03.py IMAGEruns successfully and saves the expected output file.
Some basic tests are public and provide detailed feedback when you submit. More substantive tests are private. Before the deadline, Gradescope will show only an overall pass/fail status for the private tests, without revealing which private test failed or any private-test details. The individual private tests, scores, and feedback become visible after the Gradescope late due date. Passing all public tests does not guarantee full credit.
Notes
- Use the course
cvConda environment. - Use
cv2.pyrDown()andcv2.pyrUp()as specified above. - Keep pyramid computations in floating point so signed residuals are preserved.
- Do not change the required function names or arguments.
- Do not hard-code outputs for a particular image, image size, number of levels, or threshold.
- Test locally before using one of your five Gradescope submissions.