Learning Goals
3 min- Load a pre-trained Haar cascade and run
detectMultiScale. - Understand cascades conceptually (light/dark patterns, fast rejection).
- Tune
scaleFactorandminNeighborsfor fewer false positives. - Draw boxes around detected faces.
Warm-Up · Detection vs Classification
5 minclassification: "is there a face?" → yes / no detection: "where are the faces?" → list of boxes (x, y, w, h)
Haar cascades detect — they return the location of every face. The trick: a face has predictable light/dark patterns (eyes darker than cheeks, bridge of nose lighter than eyes). The cascade checks thousands of tiny patterns, rejecting non-faces fast.
You don't train a face detector — OpenCV ships pre-trained cascade files. You load one, call detectMultiScale, and get back boxes. Old-school, fast, runs on a potato — perfect for webcams (Lesson 32).
New Concept · detectMultiScale
14 minLoad the cascade
import cv2 # OpenCV ships these XML files; cv2.data.haarcascades is their folder face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
Detect
img = cv2.imread("group.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # cascades work on gray faces = face_cascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) print(f"found {len(faces)} faces") for (x, y, w, h) in faces: print(f" face at ({x},{y}) size {w}×{h}")
The two tuning knobs
scaleFactor how much to shrink the image each pass (1.05-1.4)
smaller = more thorough but slower
minNeighbors how many overlapping detections to confirm a face (3-6)
higher = fewer false positives, may miss real faces
minSize ignore faces smaller than thisDraw the boxes
for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.imwrite("detected.jpg", img)
Other cascades
OpenCV ships eye, smile, full-body and cat-face cascades too. Same API — load a different XML and call detectMultiScale. (We'll use the smile cascade in Lesson 33.)
Worked Example · Box Every Face
12 min# face_detect.py — detect & annotate faces in a photo import cv2, matplotlib.pyplot as plt face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml") img = cv2.imread("group.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(40, 40)) print(f"detected {len(faces)} faces") for i, (x, y, w, h) in enumerate(faces, 1): cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(img, f"#{i}", (x, y - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)); plt.axis("off") plt.savefig("faces_boxed.png", dpi=130, bbox_inches="tight")
Sample output
detected 4 faces
Read the diff
Four lines of real work: load cascade, grayscale, detect, draw. Haar cascades are fast but imperfect — they miss tilted/profile faces and sometimes box a non-face. Modern deep-learning detectors (MTCNN, YOLO-face) are more accurate but heavier. For a webcam toy, Haar is perfect.
Try It Yourself
13 minRun the detector on a group photo. Print how many faces it found and draw the boxes.
Find a photo where the detector boxes a non-face. Raise minNeighbors until the false positive disappears. What did you lose?
Detect faces, then GaussianBlur each face region — an automatic privacy filter.
Hint
for (x, y, w, h) in faces: roi = img[y:y+h, x:x+w] img[y:y+h, x:x+w] = cv2.GaussianBlur(roi, (51, 51), 0)
Mini-Challenge · Eyes Within Faces
8 minDetect faces, then run the eye cascade inside each face box only (not the whole image — faster and fewer false positives). Draw face boxes in green and eye boxes in blue.
Show one possible solution
eye_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_eye.xml") for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) face_gray = gray[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(face_gray, 1.1, 6) for (ex, ey, ew, eh) in eyes: cv2.rectangle(img, (x+ex, y+ey), (x+ex+ew, y+ey+eh), (255, 0, 0), 2)
Non-negotiables: search for eyes only within face regions, offset eye coords by the face origin. Restricting the search region is a key real-world optimisation.
Recap
3 minDetection finds where, not just whether. Haar cascades ship pre-trained in OpenCV; load the XML, grayscale, call detectMultiScale with scaleFactor/minNeighbors, get boxes. Fast and CPU-friendly, but misses tilted faces. Restrict sub-detectors (eyes) to face regions. Next: run this live on a webcam.
Vocabulary Card
- object detection
- Locating objects (returning boxes), not just classifying the whole image.
- Haar cascade
- A fast, classic detector using light/dark pattern checks; pre-trained XML files.
- detectMultiScale
- Runs the detector at multiple image scales; returns (x, y, w, h) boxes.
- minNeighbors
- Confidence knob — higher means fewer false positives.
Homework
4 minBuild a face-privacy tool: given a photo, detect every face and blur it, saving a censored copy. Test on a group photo. Report how many faces were caught and any it missed, with a note on why (angle, lighting, size).
Combine face_detect.py with the blur-region trick. The note should mention Haar's weakness on profile/tilted faces.