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
- Python
- C++
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()
#include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>
using namespace cv;
using namespace std;
int main() {
// Load cascade
CascadeClassifier face_cascade;
if(!face_cascade.load(samples::findFile(
"haarcascades/haarcascade_frontalface_default.xml"))) {
cout << "Error loading cascade" << endl;
return -1;
}
// Read image
Mat img = imread("face.jpg");
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
// Detect faces
vector<Rect> faces;
face_cascade.detectMultiScale(gray, faces, 1.1, 5, 0, Size(30, 30));
cout << "Found " << faces.size() << " face(s)" << endl;
// Draw rectangles
for(size_t i = 0; i < faces.size(); i++) {
rectangle(img, faces[i], Scalar(255, 0, 0), 2);
}
imshow("Face Detection", img);
waitKey(0);
return 0;
}
Complete Face and Eye Detection
Based on OpenCV’s facedetect.cpp sample:- Python
- C++
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()
#include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void detectAndDraw(Mat& img, CascadeClassifier& cascade,
CascadeClassifier& nestedCascade,
double scale) {
double t = 0;
vector<Rect> faces;
Mat gray, smallImg;
cvtColor(img, gray, COLOR_BGR2GRAY);
double fx = 1 / scale;
resize(gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT);
equalizeHist(smallImg, smallImg);
t = (double)getTickCount();
cascade.detectMultiScale(smallImg, faces,
1.1, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
t = (double)getTickCount() - t;
printf("detection time = %g ms\n", t*1000/getTickFrequency());
for(size_t i = 0; i < faces.size(); i++) {
Rect r = faces[i];
Mat smallImgROI;
vector<Rect> nestedObjects;
Point center;
Scalar color = Scalar(0, 255, 0);
int radius;
double aspect_ratio = (double)r.width/r.height;
if(0.75 < aspect_ratio && aspect_ratio < 1.3) {
center.x = cvRound((r.x + r.width*0.5)*scale);
center.y = cvRound((r.y + r.height*0.5)*scale);
radius = cvRound((r.width + r.height)*0.25*scale);
circle(img, center, radius, color, 3, 8, 0);
}
else
rectangle(img,
Point(cvRound(r.x*scale), cvRound(r.y*scale)),
Point(cvRound((r.x + r.width-1)*scale),
cvRound((r.y + r.height-1)*scale)),
color, 3, 8, 0);
if(nestedCascade.empty())
continue;
smallImgROI = smallImg(r);
nestedCascade.detectMultiScale(smallImgROI, nestedObjects,
1.1, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
for(size_t j = 0; j < nestedObjects.size(); j++) {
Rect nr = nestedObjects[j];
center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
radius = cvRound((nr.width + nr.height)*0.25*scale);
circle(img, center, radius, color, 3, 8, 0);
}
}
imshow("result", img);
}
int main(int argc, const char** argv) {
VideoCapture capture;
Mat frame;
CascadeClassifier cascade, nestedCascade;
double scale = 1;
if(!cascade.load(samples::findFile(
"haarcascades/haarcascade_frontalface_alt.xml"))) {
cerr << "ERROR: Could not load classifier cascade" << endl;
return -1;
}
if(!nestedCascade.load(samples::findFile(
"haarcascades/haarcascade_eye_tree_eyeglasses.xml")))
cerr << "WARNING: Could not load classifier for nested objects" << endl;
if(!capture.open(0)) {
cout << "Capture from camera failed" << endl;
return 1;
}
cout << "Video capturing started..." << endl;
while(capture.read(frame)) {
if(frame.empty())
break;
Mat frame1 = frame.clone();
detectAndDraw(frame1, cascade, nestedCascade, scale);
char c = (char)waitKey(10);
if(c == 27 || c == 'q' || c == 'Q')
break;
}
return 0;
}
Smile Detection
- Python
- C++
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()
#include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>
using namespace cv;
using namespace std;
int main() {
CascadeClassifier face_cascade, smile_cascade;
face_cascade.load(samples::findFile(
"haarcascades/haarcascade_frontalface_default.xml"));
smile_cascade.load(samples::findFile(
"haarcascades/haarcascade_smile.xml"));
VideoCapture cap(0);
Mat frame, gray;
while(cap.read(frame)) {
cvtColor(frame, gray, COLOR_BGR2GRAY);
vector<Rect> faces;
face_cascade.detectMultiScale(gray, faces, 1.3, 5);
for(size_t i = 0; i < faces.size(); i++) {
Rect face = faces[i];
rectangle(frame, face, Scalar(255, 0, 0), 2);
// Get face ROI
Mat faceROI = gray(face);
// Detect smile in lower half
Rect lowerHalf(0, face.height/2,
face.width, face.height/2);
Mat smileROI = faceROI(lowerHalf);
vector<Rect> smiles;
smile_cascade.detectMultiScale(smileROI, smiles,
1.8, 20, 0, Size(25, 25));
if(!smiles.empty()) {
putText(frame, "Smiling!",
Point(face.x, face.y - 10),
FONT_HERSHEY_SIMPLEX, 0.9,
Scalar(0, 255, 0), 2);
}
}
imshow("Smile Detection", frame);
if(waitKey(1) == 'q')
break;
}
return 0;
}
Profile Face Detection
- Python
- C++
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)
#include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>
using namespace cv;
using namespace std;
int main() {
Mat img = imread("group.jpg");
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
CascadeClassifier frontal_cascade, profile_cascade;
frontal_cascade.load("haarcascade_frontalface_default.xml");
profile_cascade.load("haarcascade_profileface.xml");
vector<Rect> frontal_faces, profile_faces, profile_right;
// Detect frontal
frontal_cascade.detectMultiScale(gray, frontal_faces, 1.1, 5);
// Detect left profiles
profile_cascade.detectMultiScale(gray, profile_faces, 1.1, 5);
// Detect right profiles
Mat gray_flipped;
flip(gray, gray_flipped, 1);
profile_cascade.detectMultiScale(gray_flipped, profile_right, 1.1, 5);
// Flip coordinates back
for(auto& r : profile_right) {
r.x = img.cols - r.x - r.width;
}
// Draw detections
for(auto& r : frontal_faces)
rectangle(img, r, Scalar(0, 255, 0), 2);
for(auto& r : profile_faces)
rectangle(img, r, Scalar(255, 0, 0), 2);
for(auto& r : profile_right)
rectangle(img, r, Scalar(0, 0, 255), 2);
imshow("Face Detection", img);
waitKey(0);
return 0;
}
Improving Detection Accuracy
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)
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
)
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)
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
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
