cv::CascadeClassifier の使い方【OpenCV/C++】〜Haar Cascadeで顔検出〜
cv::CascadeClassifier は、学習済みの Haar Cascade または LBP Cascade を使って画像内のオブジェクト(顔・目・車など)を検出するクラスです。OpenCV に同梱されている haarcascade_frontalface_default.xml を読み込むだけで、数行のコードで顔検出が動作します。
cv::CascadeClassifier classifier("haarcascade_frontalface_default.xml");
std::vector<cv::Rect> faces;
classifier.detectMultiScale(gray, faces);
動作環境
- OpenCV 4.x
- コンパイル:
g++ -std=c++17 main.cpp $(pkg-config --cflags --libs opencv4) -o main - 実行:
./main input.jpg
環境構築がまだの方は環境構築ガイドを先にどうぞ。
基本の使い方
完全プログラム
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
int main(int argc, char* argv[]) {
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " <image> <cascade_xml>" << std::endl;
return 1;
}
// 画像読み込み
cv::Mat img = cv::imread(argv[1]);
if (img.empty()) {
std::cerr << "画像を読み込めませんでした: " << argv[1] << std::endl;
return 1;
}
// Cascade 分類器の読み込み
cv::CascadeClassifier classifier;
if (!classifier.load(argv[2])) {
std::cerr << "カスケードファイルを読み込めませんでした: " << argv[2] << std::endl;
return 1;
}
// グレースケール変換 + ヒストグラム平坦化(検出精度向上)
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::equalizeHist(gray, gray);
// 顔検出
std::vector<cv::Rect> faces;
classifier.detectMultiScale(
gray, // 入力画像(グレースケール)
faces, // 検出結果
1.1, // scaleFactor: 画像縮小率
5, // minNeighbors: 検出矩形の最小隣接数
0, // flags: 通常は0
cv::Size(30, 30) // minSize: 最小検出サイズ
);
std::cout << "検出した顔の数: " << faces.size() << std::endl;
// 結果を矩形で描画
for (const auto& face : faces) {
cv::rectangle(img, face, cv::Scalar(0, 255, 0), 2);
std::cout << " 位置: (" << face.x << ", " << face.y << ") "
<< "サイズ: " << face.width << "x" << face.height << std::endl;
}
cv::imwrite("result.jpg", img);
std::cout << "結果を result.jpg に保存しました" << std::endl;
return 0;
}
実行結果
$ ./main photo.jpg /usr/share/opencv4/haarcascades/haarcascade_frontalface_default.xml
検出した顔の数: 2
位置: (142, 88) サイズ: 96x96
位置: (310, 102) サイズ: 88x88
結果を result.jpg に保存しました
result.jpg には検出された顔の領域に緑色の矩形が描画されています。
行ごとの解説
| 処理 | ポイント |
|---|---|
classifier.load() |
戻り値が bool。false の場合はファイルパスが誤っているか、XML が破損している |
cvtColor → equalizeHist |
顔検出はグレースケール前提。equalizeHist で明暗差を補正すると暗い環境での検出率が上がる |
detectMultiScale |
複数スケールで検索し、複数の検出結果を std::vector<cv::Rect> で返す |
cv::rectangle |
検出矩形を元のカラー画像に描画 |
引数と戻り値
classifier.load()
| 引数 | 型 | 説明 |
|---|---|---|
filename |
const cv::String& |
XML ファイルのパス |
| 戻り値 | bool |
読み込み成功なら true |
detectMultiScale()(主要オーバーロード)
| 引数 | 型 | 既定値 | 説明 |
|---|---|---|---|
image |
InputArray |
— | グレースケール画像 |
objects |
std::vector<Rect>& |
— | 検出結果(出力) |
scaleFactor |
double |
1.1 |
検索スケールの縮小率。1 に近いほど精度↑・速度↓ |
minNeighbors |
int |
3 |
矩形をマージする閾値。大きくすると誤検出↓・見逃し↑ |
flags |
int |
0 |
旧API互換フラグ。通常は 0 |
minSize |
Size |
Size() |
検出する最小サイズ |
maxSize |
Size |
Size() |
検出する最大サイズ(省略で無制限) |
実践例
例1: ROI を切り出して目の検出を重ねる
顔領域を検出した後、その ROI に対してさらに目を検出するパターンは実務でよく使います。
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
int main(int argc, char* argv[]) {
cv::Mat img = cv::imread("photo.jpg");
if (img.empty()) {
std::cerr << "画像を読み込めませんでした" << std::endl;
return 1;
}
cv::CascadeClassifier faceCls, eyeCls;
// OpenCV インストール先の haarcascades ディレクトリを指定
if (!faceCls.load("/usr/share/opencv4/haarcascades/haarcascade_frontalface_default.xml") ||
!eyeCls.load("/usr/share/opencv4/haarcascades/haarcascade_eye.xml")) {
std::cerr << "カスケードファイルの読み込みに失敗しました" << std::endl;
return 1;
}
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::equalizeHist(gray, gray);
std::vector<cv::Rect> faces;
faceCls.detectMultiScale(gray, faces, 1.1, 5, 0, cv::Size(60, 60));
for (const auto& face : faces) {
cv::rectangle(img, face, cv::Scalar(0, 255, 0), 2);
// 顔 ROI 内で目を検出
cv::Mat faceRoi = gray(face);
std::vector<cv::Rect> eyes;
eyeCls.detectMultiScale(faceRoi, eyes, 1.1, 3, 0, cv::Size(20, 20));
for (const auto& eye : eyes) {
// 目の座標を元画像座標系に変換して描画
cv::Rect eyeInImg(face.x + eye.x, face.y + eye.y, eye.width, eye.height);
cv::rectangle(img, eyeInImg, cv::Scalar(255, 0, 0), 1);
}
}
cv::imwrite("result_eye.jpg", img);
std::cout << "検出完了: result_eye.jpg に保存しました" << std::endl;
return 0;
}
ROI 座標は顔領域ローカルなので、描画時に face.x / face.y を加算して元画像座標に戻す点に注意してください。
例2: maxSize で画面に近い大きな顔を絞る
監視カメラなど「至近距離の顔だけを拾いたい」ケースでは minSize と maxSize で範囲を絞ると誤検出が減ります。
classifier.detectMultiScale(gray, faces, 1.05, 6, 0,
cv::Size(100, 100), // minSize
cv::Size(400, 400)); // maxSize
scaleFactor を 1.05 に下げるとスケールステップが細かくなり精度が上がりますが、処理時間が増加します。本番では画像解像度と用途に応じて調整してください。
つまずきポイント
⚠️ load() が false を返す
最もよくあるミスはパスの指定誤りです。classifier.load() はファイルが見つからない場合でも例外を投げず false を返すだけなので、戻り値チェックを必ず行ってください。
OpenCV に同梱の XML ファイルはインストール先によってパスが異なります。
# Debian/Ubuntu 系の代表的なパス
/usr/share/opencv4/haarcascades/haarcascade_frontalface_default.xml
# または pkg-config で確認
pkg-config --variable=prefix opencv4
⚠️ detectMultiScale に渡す画像がグレースケールでない
cv::CascadeClassifier::detectMultiScale はグレースケール(シングルチャンネル)画像を期待しています。BGR のまま渡すと実行時に assertion エラーが発生します。
cv::error: (-215:Assertion failed) image.depth() == CV_8U in function 'detectMultiScale'
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY) を必ず通してから渡してください。
⚠️ minNeighbors の調整なしで誤検出が多発する
既定値 3 のままだと背景や衣服の模様を顔として誤検出しやすいです。実写に近い環境では 5〜8 程度に上げて運用し、今度は見逃しが増えすぎないか確認するという二段階チューニングが実務上の定石です。
関連する関数
cv::equalizeHist— 今回のコード内で使用。照明ムラのある画像での検出率向上に有効です。cv::rectangle— 検出矩形の描画に使用。- cv::flip / cv::rotate の使い方【OpenCV/C++】〜画像の反転・90度回転〜 — 撮影方向が不定な入力を正立に補正してから
detectMultiScaleに渡すパターンで組み合わせます。 - cv::hconcat / vconcat の使い方【OpenCV/C++】〜画像を連結する〜 — 検出前後の画像を横並びで比較するデバッグ表示に便利です。
まとめ
cv::CascadeClassifier は XML ファイルを load() して detectMultiScale() を呼ぶだけで顔検出が動作します。精度の鍵は グレースケール変換・scaleFactor・minNeighbors・minSize の4つで、用途に合わせて調整してください。目や顔部品の検出も同じクラスで連鎖的に実装できるため、弊社の顔周り SDK 開発でも現役で使用しています。
🛠 画像処理のプロが開発するSDK/API
本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/API(C++製・OpenCV 4.10)を開発しています。顔認証システムへの組み込み実績多数。
→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

