← Back to curriculum

Module 1 — Imaging & digital images

Project: build an imaging pipeline lab

Load RAW-like data, demosaic, apply gamma, add synthetic noise, visualize SNR vs ISO, and compare edge detectors in Python.

~150 min read + exercises

Project: build an imaging pipeline lab

Before we begin

This is your first hands-on CV exercise. You will use Python, NumPy, Matplotlib, and OpenCV to simulate pieces of a camera pipeline and compare edge detectors.

Estimated time: 2–3 hours.


What you will build

  1. Load a test image (or synthetic gradient + noise patch).
  2. Apply Gaussian blur and visualize scale effects.
  3. Run Sobel and Canny — compare edge maps.
  4. Plot histogram before/after equalization.
  5. Add synthetic shot noise at different levels and estimate SNR.
  6. Save a report figure (multi-panel PNG).

Setup

bash
mkdir cv-module1-lab && cd cv-module1-lab
python -m venv .venv
# Windows: .venv\Scripts\activate
pip install numpy matplotlib opencv-python

Step 1 — Load and inspect

python
import cv2
import numpy as np
import matplotlib.pyplot as plt
 
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)
assert img is not None, "Place sample.jpg in project folder"
print("shape:", img.shape, "dtype:", img.dtype, "min/max:", img.min(), img.max())

Step 2 — Gaussian blur sweep

python
sigmas = [0, 1, 2, 4]
fig, axes = plt.subplots(1, len(sigmas), figsize=(12, 3))
for ax, s in zip(axes, sigmas):
    out = img if s == 0 else cv2.GaussianBlur(img, (0, 0), s)
    ax.imshow(out, cmap="gray")
    ax.set_title(f"σ={s}")
    ax.axis("off")
plt.savefig("blur_sweep.png", dpi=150, bbox_inches="tight")

Deliverable: Explain in 2–3 sentences how blur affects subsequent Sobel edges.


Step 3 — Sobel vs Canny

python
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
mag = np.sqrt(sobelx**2 + sobely**2)
canny = cv2.Canny(img, 50, 150)

Overlay or side-by-side save edges_compare.png.


Step 4 — Histogram equalization

python
eq = cv2.equalizeHist(img)

Plot histograms of img and eq on one figure.


Step 5 — Synthetic noise & SNR

python
def add_shot_noise(gray, scale=1.0):
    f = gray.astype(np.float32) / 255.0
    noisy = np.random.poisson(f * scale * 255) / (scale * 255)
    return np.clip(noisy * 255, 0, 255).astype(np.uint8)
 
for scale in [0.5, 1.0, 2.0]:
    n = add_shot_noise(img, scale)
    signal = img.astype(float).mean()
    noise_std = (n.astype(float) - img.astype(float)).std()
    snr = signal / (noise_std + 1e-6)
    print(f"scale={scale} approx SNR={snr:.2f}")

Extension ideas

  • Convert to HSV and threshold a hue range.
  • Implement separable Gaussian manually with two 1D convolutions.
  • Linearize sRGB with gamma 2.2 before blur — compare edges.

What's next

Welcome to Module 2 — camera geometry and correspondence.