> ## 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

> Comprehensive guide to detecting faces and facial features using Haar cascade classifiers in OpenCV

# 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

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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()
    ```
  </Tab>

  <Tab title="C++">
    ```cpp theme={null}
    #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;
    }
    ```
  </Tab>
</Tabs>

## Complete Face and Eye Detection

Based on OpenCV's facedetect.cpp sample:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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()
    ```
  </Tab>

  <Tab title="C++">
    ```cpp theme={null}
    #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;
    }
    ```
  </Tab>
</Tabs>

## Smile Detection

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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()
    ```
  </Tab>

  <Tab title="C++">
    ```cpp theme={null}
    #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;
    }
    ```
  </Tab>
</Tabs>

## Profile Face Detection

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    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)
    ```
  </Tab>

  <Tab title="C++">
    ```cpp theme={null}
    #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;
    }
    ```
  </Tab>
</Tabs>

## Improving Detection Accuracy

<Steps>
  <Step title="Preprocessing">
    ```python theme={null}
    # 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)
    ```
  </Step>

  <Step title="Parameter Tuning">
    ```python theme={null}
    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
    )
    ```
  </Step>

  <Step title="Multi-scale Detection">
    Try multiple scale factors and combine results:

    ```python theme={null}
    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)
    ```
  </Step>

  <Step title="Temporal Filtering">
    For video, track faces across frames:

    ```python theme={null}
    # Use object tracking to smooth detections
    # Only accept detections that appear in multiple consecutive frames
    ```
  </Step>
</Steps>

<Note>
  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
</Note>

<Warning>
  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](/tutorials/deep-learning)).
</Warning>

## Next Steps

* Learn about [Deep Learning](/tutorials/deep-learning) face detection for better accuracy
* Explore [Object Detection](/tutorials/object-detection) for detecting other objects
* Try face recognition and facial landmarks detection
