raw Software
RAW Software Computer Vision Object Detection

Face Detection with OpenCV and C++

Robert Eisele

OpenCV's cv::CascadeClassifier can detect frontal faces in an image and return one bounding rectangle per detection. The example below targets OpenCV 4 and writes those rectangles as structured JSON. It is a face detector, not a face-recognition system: it locates face-like regions but does not determine anyone's identity.

C++ Implementation

#include <iostream>
#include <vector>

#include <opencv2/core.hpp>
#include <opencv2/core/persistence.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>

int main(int argc, char** argv) {
  if (argc != 3) {
    std::cerr << "usage: face_detect <image> <cascade.xml>\n";
    return 2;
  }

  const cv::Mat image = cv::imread(argv[1], cv::IMREAD_COLOR);
  if (image.empty()) {
    std::cerr << "unable to read image: " << argv[1] << '\n';
    return 1;
  }

  cv::CascadeClassifier classifier;
  if (!classifier.load(argv[2])) {
    std::cerr << "unable to load cascade: " << argv[2] << '\n';
    return 1;
  }

  cv::Mat gray;
  cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
  cv::equalizeHist(gray, gray);

  std::vector<cv::Rect> faces;
  classifier.detectMultiScale(
    gray,
    faces,
    1.1,
    3,
    0,
    cv::Size(30, 30)
  );

  cv::FileStorage json(
    ".json",
    cv::FileStorage::WRITE |
      cv::FileStorage::MEMORY |
      cv::FileStorage::FORMAT_JSON
  );
  if (!json.isOpened()) {
    std::cerr << "unable to initialize JSON output\n";
    return 1;
  }

  json.startWriteStruct("faces", cv::FileNode::SEQ);
  for (const cv::Rect& face : faces) {
    json.startWriteStruct("", cv::FileNode::MAP);
    json << "x" << face.x;
    json << "y" << face.y;
    json << "width" << face.width;
    json << "height" << face.height;
    json.endWriteStruct();
  }
  json.endWriteStruct();

  std::cout << json.releaseAndGetString() << '\n';
  return 0;
}

OpenCV's FileStorage API produces the JSON rather than assembling it with string concatenation. A successful run writes an object of this form:

{
  "faces": [
    { "x": 142, "y": 86, "width": 118, "height": 118 }
  ]
}

Build and Run

On systems that provide an opencv4.pc file, compile with:

c++ -std=c++17 -O2 face_detect.cpp \
  -o face_detect \
  $(pkg-config --cflags --libs opencv4)

Then pass an image and a trained cascade:

./face_detect portrait.jpg \
  /path/to/haarcascade_frontalface_default.xml

OpenCV distributes pretrained cascades in its data/haarcascades directory. Distribution packages install them in different locations, so locate the XML file through the package manager rather than hard-coding a system path.

Detection Parameters

The arguments after facesdetectMultiScale control the detector:

The returned coordinates refer to the original image because this implementation converts color and equalizes the histogram without resizing. If preprocessing changes image dimensions, map every rectangle back to the source coordinate system before exporting it.

Limits of Haar Cascades

Haar cascades implement the Viola-Jones detection model: inexpensive rectangular features reject most non-face windows through a sequence of increasingly selective stages. It remains useful for compact CPU-only applications, but frontal-pose cascades are sensitive to head rotation, occlusion, lighting, image scale, and the population represented by their training data.

Evaluate false positives and false negatives on data representative of the actual application. For unconstrained photographs, varied poses, or higher accuracy requirements, use an OpenCV DNN face detector or another maintained model with documented training and evaluation data. Face detection also processes biometric data; collect, retain, and transmit images only with an appropriate legal basis and privacy policy.

References