Learning Goals
3 min- Read and write images with
cv2.imread/imwrite. - Remember OpenCV uses BGR, not RGB.
- Resize, crop, convert to grayscale.
- Apply blur and Canny edge detection.
Warm-Up · BGR, the Famous Gotcha
5 minpip install opencv-python
import cv2 img = cv2.imread("cat.jpg") # loads as a NumPy array (Lesson 5!) print(img.shape) # (H, W, 3) print(img[0, 0]) # [B G R] ← Blue, Green, Red order!
OpenCV stores colours as BGR, not RGB — a historical quirk. If your displayed image looks blue-tinted, you forgot to convert. To show with matplotlib (which expects RGB): cv2.cvtColor(img, cv2.COLOR_BGR2RGB).
An OpenCV image IS a NumPy array — everything from Lesson 5 applies. OpenCV just adds fast, battle-tested operations (resize, blur, edges, detection) on top. Watch the BGR/RGB order.
New Concept · The Core Operations
14 minRead, write, show
import cv2, matplotlib.pyplot as plt img = cv2.imread("cat.jpg") cv2.imwrite("copy.png", img) # save # show via matplotlib (convert BGR→RGB first) plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)); plt.axis("off")
Resize & crop
small = cv2.resize(img, (200, 200)) # exact size half = cv2.resize(img, None, fx=0.5, fy=0.5) # by factor crop = img[50:250, 100:400] # NumPy slice
Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # shape (H, W)
Blur (smoothing)
blur = cv2.GaussianBlur(img, (7, 7), 0) # kernel must be odd
Blurring averages nearby pixels — used to reduce noise before edge detection, and for privacy (blur faces/plates).
Edges with Canny
edges = cv2.Canny(gray, threshold1=100, threshold2=200) # white pixels where the image changes sharply
Draw on images
cv2.rectangle(img, (50, 50), (200, 200), (0, 255, 0), 2) # green box cv2.putText(img, "cat", (50, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
These draw functions are how detection results get annotated (Lesson 31's face boxes).
Worked Example · A Mini Vision Pipeline
12 min# vision_basics.py — load → gray → blur → edges, side by side import cv2, matplotlib.pyplot as plt img = cv2.imread("street.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) edges = cv2.Canny(blur, 80, 160) panels = { "original": cv2.cvtColor(img, cv2.COLOR_BGR2RGB), "gray": gray, "blurred": blur, "edges": edges, } fig, ax = plt.subplots(1, 4, figsize=(16, 4)) for a, (name, im) in zip(ax, panels.items()): a.imshow(im, cmap="gray" if im.ndim == 2 else None) a.set_title(name); a.axis("off") fig.tight_layout(); fig.savefig("pipeline.png", dpi=130)
Read the diff
The canonical pre-processing chain: colour → gray (simplifies) → blur (denoise) → Canny (find edges). Blurring before Canny is important — raw edges are noisy. This exact pipeline feeds lane detection, document scanning, and shape finding. Each step is one OpenCV call on a NumPy array.
Try It Yourself
13 minLoad a photo, resize it to 300px wide (keep aspect ratio), and save it. Print original and new shapes.
Hint
h, w = img.shape[:2] new_w = 300 new_h = int(h * new_w / w) out = cv2.resize(img, (new_w, new_h)) cv2.imwrite("resized.jpg", out)
Try three Canny threshold pairs (low/high) and compare the edge maps. How do thresholds change what counts as an edge?
Blur just a rectangular region of an image (e.g., to censor it) and paste it back. Combine slicing + GaussianBlur.
Hint
x1, y1, x2, y2 = 100, 80, 260, 220 region = img[y1:y2, x1:x2] img[y1:y2, x1:x2] = cv2.GaussianBlur(region, (35, 35), 0)
Mini-Challenge · Document Scanner Look
8 minGive a photo the "scanned document" look: grayscale, then adaptive threshold to make it crisp black-and-white. Compare to a plain global threshold.
Show one possible solution
import cv2 gray = cv2.cvtColor(cv2.imread("note.jpg"), cv2.COLOR_BGR2GRAY) scan = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) cv2.imwrite("scanned.png", scan)
Adaptive thresholding adjusts per-region — it handles uneven lighting that a single global threshold can't. That's why scanner apps look clean across the whole page.
Recap
3 minOpenCV images are NumPy arrays in BGR order. Core ops: imread/imwrite, resize, cvtColor (gray/RGB), GaussianBlur, Canny edges, and draw functions. The classic chain is gray → blur → edges. Adaptive thresholding beats global on uneven lighting. Next: detect faces with one library call.
Vocabulary Card
- BGR
- OpenCV's colour order (Blue, Green, Red) — convert to RGB before matplotlib.
- cvtColor
- Convert between colour spaces (BGR↔RGB, BGR↔GRAY).
- GaussianBlur
- Smoothing filter; reduces noise (odd kernel size).
- Canny
- Edge-detection algorithm with low/high thresholds.
Homework
4 minBuild cv_toolkit.py with functions: resize_to_width, to_gray, blur, edges, and censor_region. Apply all five to a photo and save a labelled grid PNG. Add a one-line docstring per function.
Wrap each operation from the lesson into a small function. The grid follows the worked-example pattern.