CascadeClassifier の使い方【OpenCV/C#(OpenCvSharp)】〜Haar Cascadeで顔検出〜

OpenCV for C#
📌 準備: OpenCV の環境構築がまだの方はこちら → C++ 環境構築ガイド / C#(OpenCvSharp)セットアップ

CascadeClassifier の使い方【OpenCV/C#(OpenCvSharp)】〜Haar Cascade で顔検出〜

CascadeClassifier は、Haar Cascade(ハール特徴量)やLBPベースの学習済み分類器を読み込み、画像中の顔・目・笑顔などを高速に検出するクラスです。OpenCV に同梱された XML ファイルを用意するだけで、すぐに顔検出を試せます。

最小の呼び出しイメージは次のとおりです。

var classifier = new CascadeClassifier("haarcascade_frontalface_default.xml");
Rect[] faces = classifier.DetectMultiScale(gray);

動作環境

  • OpenCV 4.x(OpenCvSharp4)
  • .NET 8 コンソールアプリ(トップレベルステートメント)
  • NuGet: OpenCvSharp4 / OpenCvSharp4.runtime.win

環境構築がまだの方は環境構築ガイドを先にどうぞ。


基本の使い方

XML ファイルの入手

OpenCV のリポジトリに同梱されている XML を使います。
以下の URL から haarcascade_frontalface_default.xml をダウンロードし、プロジェクトフォルダに置いてください。

opencv/data/haarcascades(GitHub)

完全プログラム(静止画から顔検出)

using OpenCvSharp;

// --- 設定 ---
const string xmlPath  = "haarcascade_frontalface_default.xml";
const string imgPath  = "people.jpg";   // 検出対象画像(別途用意)
const string savePath = "result.jpg";

// CascadeClassifier は IDisposable を実装していないので using 不要
// ただし Mat は必ず using で解放する
var classifier = new CascadeClassifier(xmlPath);
if (classifier.Empty())
{
    Console.Error.WriteLine("XML の読み込みに失敗しました。パスを確認してください。");
    return;
}

using var src  = Cv2.ImRead(imgPath);
if (src.Empty()) { Console.Error.WriteLine("画像を読み込めません。"); return; }

// グレースケール変換(DetectMultiScale は 1ch 画像を推奨)
using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

// ヒストグラム均等化でコントラストを補正(薄暗い画像への対策)
Cv2.EqualizeHist(gray, gray);

// 顔検出
Rect[] faces = classifier.DetectMultiScale(
    image      : gray,
    scaleFactor: 1.1,     // 画像縮小ステップ(1.1 ≒ 10% ずつ縮小)
    minNeighbors: 3,      // 検出矩形の最低隣接数(大きいほど誤検出が減る)
    flags      : HaarDetectionTypes.ScaleImage,
    minSize    : new Size(30, 30),   // 検出する最小顔サイズ
    maxSize    : new Size()          // Size() = 制限なし
);

Console.WriteLine($"検出数: {faces.Length}");

// 検出結果を矩形で描画
using var result = src.Clone();
foreach (var face in faces)
{
    Cv2.Rectangle(result, face, Scalar.LimeGreen, thickness: 2);
    Console.WriteLine($"  顔: {face}");
}

Cv2.ImWrite(savePath, result);
Console.WriteLine($"保存完了: {savePath}");

実行結果の例

検出数: 2
  顔: {X=112,Y=84,Width=96,Height=96}
  顔: {X=310,Y=102,Width=88,Height=88}
保存完了: result.jpg

行ごとの解説

行の内容 説明
new CascadeClassifier(xmlPath) 学習済み XML を読み込む。失敗すると Empty() が true になる
Cv2.CvtColor(..., BGR2GRAY) DetectMultiScale はグレースケール入力を推奨(内部で変換も可だが精度が落ちる場合がある)
Cv2.EqualizeHist ヒストグラム均等化で照明ムラを補正。暗い画像での検出率を上げる定番前処理
scaleFactor: 1.1 1.05 に近づけると精度↑・速度↓。1.3 以上にすると速度↑・検出漏れ↑
minNeighbors: 3 小さくすると検出数↑・誤検出↑。5〜6 程度まで上げると誤検出を絞れる
HaarDetectionTypes.ScaleImage 推奨フラグ。画像をスケールして走査する標準モード

引数と戻り値(DetectMultiScale)

引数 型 説明
image InputArray グレースケール画像(1ch)を推奨
scaleFactor double 検出ウィンドウを縮小するステップ比率(> 1.0)
minNeighbors int 矩形を採用する最低隣接数。多いほど厳しくなる
flags HaarDetectionTypes ScaleImage(推奨)または DoCannyPruning など
minSize Size 検出する物体の最小サイズ
maxSize Size 検出する物体の最大サイズ(Size() = 無制限)
戻り値 Rect[] 検出された矩形の配列

実践例

カメラ映像からリアルタイム顔検出

カメラ映像に対してフレームごとに DetectMultiScale を実行します。
VideoCapture の詳細は VideoCapture の使い方【OpenCV/C#(OpenCvSharp)】〜カメラ映像を取り込む〜 も参照してください。

using OpenCvSharp;

const string xmlPath = "haarcascade_frontalface_default.xml";

var classifier = new CascadeClassifier(xmlPath);
if (classifier.Empty()) { Console.Error.WriteLine("XML 読み込み失敗"); return; }

using var cap  = new VideoCapture(0);   // 0 = 既定カメラ
using var frame = new Mat();
using var gray  = new Mat();

if (!cap.IsOpened()) { Console.Error.WriteLine("カメラを開けません"); return; }

Console.WriteLine("q キーで終了");

while (true)
{
    cap.Read(frame);
    if (frame.Empty()) break;

    Cv2.CvtColor(frame, gray, ColorConversionCodes.BGR2GRAY);
    Cv2.EqualizeHist(gray, gray);

    Rect[] faces = classifier.DetectMultiScale(
        gray, scaleFactor: 1.1, minNeighbors: 4,
        minSize: new Size(60, 60));

    foreach (var face in faces)
        Cv2.Rectangle(frame, face, Scalar.LimeGreen, 2);

    Cv2.ImShow("Face Detection", frame);

    // q キーで終了
    if (Cv2.WaitKey(1) == 'q') break;
}

Cv2.DestroyAllWindows();

顔領域をクロップして保存

検出した顔矩形を Mat のインデクサで切り出せます。各顔画像を個別ファイルに書き出す場合の例です。

using OpenCvSharp;

const string xmlPath = "haarcascade_frontalface_default.xml";
const string imgPath = "people.jpg";

var classifier = new CascadeClassifier(xmlPath);

using var src  = Cv2.ImRead(imgPath);
using var gray = new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.EqualizeHist(gray, gray);

Rect[] faces = classifier.DetectMultiScale(gray, 1.1, 3, minSize: new Size(30, 30));

for (int i = 0; i < faces.Length; i++)
{
    // Rect でインデクシングして顔領域を取り出す(Clone でデータをコピー)
    using var face = src[faces[i]].Clone();
    string path = $"face_{i:D3}.jpg";
    Cv2.ImWrite(path, face);
    Console.WriteLine($"保存: {path}  サイズ: {face.Size()}");
}

つまずきポイント

⚠️ XML のパスを間違えると Empty() のまま動いてしまう

new CascadeClassifier("wrong_path.xml") はコンストラクタで例外を投げません。
そのまま DetectMultiScale を呼ぶと検出数ゼロが返るだけで、エラーメッセージも出ないため、原因に気づきにくいです。
必ず classifier.Empty() を初期化後に確認し、false でない場合は即リターンする実装習慣をつけてください。

⚠️ カラー画像をそのまま渡すと精度が落ちる

DetectMultiScale にカラー(3ch)Mat を渡しても内部でグレースケール化されますが、公式は 1ch 入力を推奨しています。明示的に Cv2.CvtColor → Cv2.EqualizeHist を挟むほうが検出精度・速度ともに安定します。

⚠️ Mat のスライス(src[faces[i]])は参照コピー

src[rect] で取り出した Mat はメモリを共有しています。保存・加工する前に .Clone() を呼ばないと、元 Mat を Dispose したあとにアクセス違反が発生します。クロップしたら必ず Clone() して独立した Mat を作り、using で解放してください。


関連する関数


まとめ

CascadeClassifier は XML を読み込み DetectMultiScale を呼ぶだけで顔検出を実装できます。scaleFactor と minNeighbors の調整が検出精度と速度のバランスを決める核心です。初期化後の Empty() チェックと、グレースケール変換・ヒストグラム均等化のセットを忘れずに実装してください。

🛠 画像処理のプロが開発するSDK/API

本ブログを運営するスワローインキュベートは、OpenCV ベースのなりすまし判定SDK/APIを開発しています。SDK は C#/.NET 8 に正式対応しており、OpenCvSharp を使うプロジェクトからそのまま組み込めます。

→ 顔認証のなりすまし対策 設計チェックリスト(当ブログ)
→ なりすまし判定SDK/APIの詳細を見る
→ API仕様書・サンプルコード

タイトルとURLをコピーしました