Skillversitas

Face and Object Detection

Face detection in classical OpenCV uses cascade classifiers - Haar-like features evaluated in a boosted cascade, fast enough for real-time on a CPU. The modern path is the DNN detector, which is the subject of the next chapter; this one covers the Haar and LBP cascades shipped with OpenCV, how to load and run them, how to tune them, and the same pattern generalized to other objects (eyes, hands, smile) via the bundled XML files.

Haar Cascades

import cv2

# cascade XMLs ship with the package under data/
haar = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
print(haar.empty())            # False if it loaded

cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    if not ret:
        break
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray = cv2.equalizeHist(gray)        # helps under uneven light

    faces = haar.detectMultiScale(gray, scaleFactor=1.1,
                                  minNeighbors=5, minSize=(30, 30))
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv2.imshow("Faces", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

detectMultiScale slides a window across the image at shrinking scales and returns the boxes that pass the boosted classifier. The three knobs: scaleFactor (1.05-1.2, the ratio between consecutive window sizes - lower finds smaller faces but is slower), minNeighbors (how many overlapping detections must agree - higher = fewer false positives, the main tuning dial), and minSize (reject boxes smaller than this). Equalizing the histogram before detection materially improves recall in real lighting. The result is an array of (x, y, w, h) - the same annotation idiom from the drawing chapter renders them.

LBP and the Face-Eye Chain

import cv2

cascade_dir = cv2.data.haarcascades
face_c = cv2.CascadeClassifier(cascade_dir + "haarcascade_frontalface_default.xml")
eye_c = cv2.CascadeClassifier(cascade_dir + "haarcascade_eye.xml")

img = cv2.imread("group.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

faces = face_c.detectMultiScale(gray, 1.1, 5)
for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    # run the eye detector INSIDE the face ROI
    roi_gray = gray[y:y + h, x:x + w]
    roi_color = img[y:y + h, x:x + w]
    eyes = eye_c.detectMultiScale(roi_gray)
    for (ex, ey, ew, eh) in eyes:
        cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (255, 0, 0), 2)

cv2.imwrite("faces_eyes.jpg", img)

The pattern generalizes: detectors are cascaded, each stage running on the previous stage's region of interest. Eyes are detected inside the face box, a smile cascade inside the face, and the ROI is passed in the local coordinates the detector expects. This cascade-of-cascades structure is how multi-part detection works before you involve learned networks - the face detector is the gate that makes the eye search cheap.

Tuning and Failure Modes

SymptomCause / fix
Missed faceslow light, extreme angle - equalize first; lower minNeighbors; check the face is inside minSize
Many false positivesraise minNeighbors (5-8); tighten minSize
Duplicates over one faceraise minNeighbors; the cascade merges overlapping boxes as it does
Slowdownscale the frame; raise scaleFactor toward 1.2

Haar cascades are honest about their limits: they are frontal-face machines, they degrade sharply at profile angles and strong lighting gradients, and they detect faces but say nothing about identity or expression. For production-grade detection - profiles, occlusion, crowded scenes - the DNN detector in the next chapter is the upgrade; the Haar path remains the right choice when you want zero model files, zero dependencies, and a detector that runs on anything with a CPU.

Other Object Cascades

The haarcascades directory ships detectors beyond faces - upper body, full body, hand, smile, QR-code-like patterns - and the detectMultiScale call is identical for all of them. The same tuning logic applies, and the same ROI-cascade pattern chains them (full body -> face -> eyes). They are the same boosted-Haar engine with different training data, so expect the same strengths (fast, CPU-only, no download) and the same weaknesses (frontal bias, lighting sensitivity). For anything beyond faces, the DNN route is usually worth the model file.

Summary

Detect faces with a loaded CascadeClassifier and detectMultiScale, tuning scaleFactor, minNeighbors (the main false-positive dial), and minSize, and equalizing the gray frame first for real lighting. Chain detectors by running each stage inside the previous stage's ROI to build multi-part detection, and read the failure table - misses want pre-processing and lower neighbor counts, false positives want higher ones. Haar cascades are the zero-dependency baseline: fast and frontal, and the DNN chapter is the upgrade when accuracy across angles and scenes is the requirement.