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
- Load a test image (or synthetic gradient + noise patch).
- Apply Gaussian blur and visualize scale effects.
- Run Sobel and Canny — compare edge maps.
- Plot histogram before/after equalization.
- Add synthetic shot noise at different levels and estimate SNR.
- 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-pythonStep 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.