Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/opencv/opencv/llms.txt

Use this file to discover all available pages before exploring further.

Face Detection with Cascade Classifiers

Learn how to detect faces, eyes, and other facial features in images and video using OpenCV’s pre-trained Haar cascade classifiers.

Introduction to Face Detection

Face detection is one of the most common applications of computer vision. OpenCV provides robust pre-trained models that can detect faces in various conditions.

Why Haar Cascades?

  • Pre-trained models available for immediate use
  • Fast enough for real-time detection
  • No GPU required
  • Works well for frontal faces
  • Lightweight and easy to deploy

Basic Face Detection

Single Face Detection

import cv2 as cv

# Load the cascade classifier
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 
                                   'haarcascade_frontalface_default.xml')

# Read image
img = cv.imread('face.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# Detect faces
faces = face_cascade.detectMultiScale(
    gray,
    scaleFactor=1.1,
    minNeighbors=5,
    minSize=(30, 30)
)

print(f"Found {len(faces)} face(s)")

# Draw rectangle around each face
for (x, y, w, h) in faces:
    cv.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)

cv.imshow('Face Detection', img)
cv.waitKey(0)
cv.destroyAllWindows()

Complete Face and Eye Detection

Based on OpenCV’s facedetect.cpp sample:
import cv2 as cv
from video import create_capture
from common import clock, draw_str

def detect(img, cascade):
    """Detect faces or eyes using cascade classifier"""
    rects = cascade.detectMultiScale(
        img, 
        scaleFactor=1.3, 
        minNeighbors=4, 
        minSize=(30, 30),
        flags=cv.CASCADE_SCALE_IMAGE
    )
    if len(rects) == 0:
        return []
    # Convert to x1, y1, x2, y2 format
    rects[:,2:] += rects[:,:2]
    return rects

def draw_rects(img, rects, color):
    """Draw rectangles on image"""
    for x1, y1, x2, y2 in rects:
        cv.rectangle(img, (x1, y1), (x2, y2), color, 2)

def main():
    import sys
    import getopt

    args, video_src = getopt.getopt(sys.argv[1:], '', 
                                   ['cascade=', 'nested-cascade='])
    try:
        video_src = video_src[0]
    except:
        video_src = 0
    
    args = dict(args)
    cascade_fn = args.get('--cascade', 
                         'haarcascades/haarcascade_frontalface_alt.xml')
    nested_fn = args.get('--nested-cascade', 
                        'haarcascades/haarcascade_eye.xml')

    # Load cascades
    cascade = cv.CascadeClassifier(cv.samples.findFile(cascade_fn))
    nested = cv.CascadeClassifier(cv.samples.findFile(nested_fn))

    # Open camera or video
    cam = create_capture(video_src)

    while True:
        ret, img = cam.read()
        if not ret:
            break
        
        # Convert to grayscale and equalize histogram
        gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        gray = cv.equalizeHist(gray)

        # Measure detection time
        t = clock()
        
        # Detect faces
        rects = detect(gray, cascade)
        vis = img.copy()
        draw_rects(vis, rects, (0, 255, 0))  # Green rectangles for faces
        
        # Detect eyes within each face
        if not nested.empty():
            for x1, y1, x2, y2 in rects:
                roi = gray[y1:y2, x1:x2]
                vis_roi = vis[y1:y2, x1:x2]
                subrects = detect(roi.copy(), nested)
                draw_rects(vis_roi, subrects, (255, 0, 0))  # Blue for eyes
        
        dt = clock() - t

        # Display detection time
        draw_str(vis, (20, 20), 'time: %.1f ms' % (dt*1000))
        cv.imshow('Face Detection', vis)

        # Press ESC to exit
        if cv.waitKey(5) == 27:
            break

    print('Done')
    cv.destroyAllWindows()

if __name__ == '__main__':
    main()

Smile Detection

import cv2 as cv

# Load cascades
face_cascade = cv.CascadeClassifier(cv.data.haarcascades + 
                                   'haarcascade_frontalface_default.xml')
smile_cascade = cv.CascadeClassifier(cv.data.haarcascades + 
                                    'haarcascade_smile.xml')

cap = cv.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    
    # Detect faces
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    
    for (x, y, w, h) in faces:
        cv.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
        
        # Get face ROI for smile detection
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = frame[y:y+h, x:x+w]
        
        # Detect smiles (only in lower half of face)
        smiles = smile_cascade.detectMultiScale(
            roi_gray[h//2:, :],  # Lower half
            scaleFactor=1.8,
            minNeighbors=20,
            minSize=(25, 25)
        )
        
        # Draw smile detection
        for (sx, sy, sw, sh) in smiles:
            cv.rectangle(roi_color, (sx, sy + h//2), 
                       (sx+sw, sy+sh + h//2), (0, 255, 0), 2)
            cv.putText(frame, 'Smiling!', (x, y-10),
                      cv.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
    
    cv.imshow('Smile Detection', frame)
    
    if cv.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv.destroyAllWindows()

Profile Face Detection

import cv2 as cv

# Load both frontal and profile cascades
frontal_cascade = cv.CascadeClassifier(cv.data.haarcascades + 
                                      'haarcascade_frontalface_default.xml')
profile_cascade = cv.CascadeClassifier(cv.data.haarcascades + 
                                      'haarcascade_profileface.xml')

img = cv.imread('group.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# Detect frontal faces
frontal_faces = frontal_cascade.detectMultiScale(gray, 1.1, 5)
print(f"Frontal faces: {len(frontal_faces)}")

# Detect profile faces (left)
profile_faces = profile_cascade.detectMultiScale(gray, 1.1, 5)

# Flip image to detect right-facing profiles
gray_flipped = cv.flip(gray, 1)
profile_faces_flipped = profile_cascade.detectMultiScale(gray_flipped, 1.1, 5)

# Flip coordinates back
width = img.shape[1]
profile_faces_right = [(width - x - w, y, w, h) 
                      for (x, y, w, h) in profile_faces_flipped]

print(f"Profile faces (left): {len(profile_faces)}")
print(f"Profile faces (right): {len(profile_faces_right)}")

# Draw all detections
for (x, y, w, h) in frontal_faces:
    cv.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

for (x, y, w, h) in profile_faces:
    cv.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)

for (x, y, w, h) in profile_faces_right:
    cv.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)

cv.imshow('Face Detection (Green=Frontal, Blue=Left, Red=Right)', img)
cv.waitKey(0)

Improving Detection Accuracy

1

Preprocessing

# Convert to grayscale
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

# Equalize histogram for better contrast
gray = cv.equalizeHist(gray)

# Apply slight Gaussian blur to reduce noise
gray = cv.GaussianBlur(gray, (3, 3), 0)
2

Parameter Tuning

faces = face_cascade.detectMultiScale(
    gray,
    scaleFactor=1.1,     # Smaller = more thorough, slower
    minNeighbors=5,      # Higher = fewer false positives
    minSize=(30, 30),    # Minimum face size
    maxSize=(300, 300),  # Maximum face size
    flags=cv.CASCADE_SCALE_IMAGE
)
3

Multi-scale Detection

Try multiple scale factors and combine results:
all_faces = []
for scale in [1.05, 1.1, 1.2, 1.3]:
    faces = face_cascade.detectMultiScale(gray, scale, 5)
    all_faces.extend(faces)

# Remove duplicates using Non-Maximum Suppression
# (implementation depends on your needs)
4

Temporal Filtering

For video, track faces across frames:
# Use object tracking to smooth detections
# Only accept detections that appear in multiple consecutive frames
Best practices for face detection:
  • Always convert to grayscale first
  • Use histogram equalization for better contrast
  • Start with scaleFactor=1.1 and minNeighbors=5
  • Adjust minSize based on expected face sizes
  • For video, resize frames for faster processing
  • Use nested detection (face → eyes) to verify results
Limitations of Haar cascades:
  • Works best with frontal faces
  • Struggles with occlusions (sunglasses, masks, hands)
  • Sensitive to lighting conditions
  • Less accurate than deep learning methods
  • Can produce false positives
For production applications requiring high accuracy, consider using deep learning-based face detection (see Deep Learning tutorial).

Next Steps

  • Learn about Deep Learning face detection for better accuracy
  • Explore Object Detection for detecting other objects
  • Try face recognition and facial landmarks detection