LoginSignup
11
11

More than 5 years have passed since last update.

RxとOpenCVSharpで顔認識を簡単に作成する

Last updated at Posted at 2015-02-06

はじめに

 前回の投稿では、カメラキャプチャアプリを作成しました。RxOpenCVSharpを組み合わせると、簡単にできる事が分かりました。
 ここまでくると欲を出したくなる物で、OpenCVには顔認証ができる仕組みが準備されています。今回はそれを使って、顔認識をさせようという物です。

画面にタイムスタンプを入れてみよう

顔認識の前に画面の左上にタイムスタンプを入れてみよう。

実装

1. フォントの定義
(変更前)
/// <summary>
/// 初期化
/// </summary>
public void Initialize()
{
    // キャプチャーデバイス
    var capture = new CvCapture(0);

(変更後)
/// <summary>
/// 初期化
/// </summary>
public void Initialize()
{
    // 画面にタイムスタンプを描画するためのフォント定義
    var font = default(CvFont);
    Cv.InitFont(out font, FontFace.HersheyScriptComplex, 0.5, 0.5);

    // キャプチャーデバイス
    var capture = new CvCapture(0);
2.Rxのシーケンスの途中にテキスト描画処理を追加
(変更前)
var capture = new CvCapture(0);
Observable.Interval(TimeSpan.FromMilliseconds(1), DispatcherScheduler.Current)
    .Select(_ => Cv.QueryFrame(capture))
    .Where(frame => frame != null)
    .Subscribe(frame =>
    {

(変更後)
var capture = new CvCapture(0);
Observable.Interval(TimeSpan.FromMilliseconds(1), DispatcherScheduler.Current)
    .Select(_ => Cv.QueryFrame(capture))
    .Where(frame => frame != null)
    .Do(frame => frame.PutText(string.Format("{0:MM/dd HH:mm:ss}", DateTime.Now), new CvPoint(0, 15), font, Cv.RGB(255, 255, 255)))
    .Subscribe(frame =>
    {

Do()を使用して、キャプチャーした画像のframeの左上にタイムスタンプを表示します。

実行

実行すると、左上にタイムスタンプが表示します。

顔認識をさせる

さて、顔認識をさせましょう。
OpenCVでは「カスケード型分類器」と呼ばれる物が実装されていて、それを利用するとチョー簡単に顔認識ができます。

実装

1.顔認識分類器の作成
(変更前)
/// <summary>
/// 初期化
/// </summary>
public void Initialize()
{
    // 画面にタイムスタンプを描画するためのフォント定義
    var font = default(CvFont);
    Cv.InitFont(out font, FontFace.HersheyScriptComplex, 0.5, 0.5);

    // キャプチャーデバイス
    var capture = new CvCapture(0);

(変更後)
/// <summary>
/// 初期化
/// </summary>
public void Initialize()
{
    // 画面にタイムスタンプを描画するためのフォント定義
    var font = default(CvFont);
    Cv.InitFont(out font, FontFace.HersheyScriptComplex, 0.5, 0.5);

    // 顔の分類器作成
    var faceDetector = Cv.Load<CvHaarClassifierCascade>("haarcascade_frontalface_default.xml");

    // キャプチャーデバイス
    var capture = new CvCapture(0);

顔の分類器の作成で指定されている"haarcascade_frontalface_default.xml"は、OpenCVSharpには同梱されていません。OpenCV本体をダウンロードして、解凍後、自力でプロジェクトに追加します。

  • OpenCVの本体をダウンロードし、解凍する。
  • 解凍先のフォルダopencv\sources\data\haarcascadeshaarcascade_frontalface_default.xmlがあるので、プロジェクトに取り込む。
  • 取り込み方法は、VS2013の「ソリューションエクスプローラ」で「LivetWPFApplication1」を選択し、右クリック[追加]-[既存の項目]を選択。ファイル選択エクスプローラが開くので、上記xmlファイルを選択して下さい。
  • 取り込み後、プロジェクトの中にhaarcascade_frontalface_default.xmlが追加されるので、プロパティを表示し、「ビルドアクション」は「なし」、「出力ディレクトリーにコピー」も「新しい場合にコピー」を選択して下さい。

    10.png

2. 顔認識処理を追加

顔認識を行い、認識した場所に四角の矩形枠を描画するメソッドを追加します

(追加メソッド)
/// <summary>
/// 顔認識を行い、矩形枠を描画する
/// </summary>
/// <param name="frame">キャプチャーした画像</param>
/// <param name="faceDetector">顔の分類器</param>
private void ImageProcess(IplImage frame, CvHaarClassifierCascade faceDetector)
{
    using (var frameMatImage = Cv.GetMat(frame))
    {
        using (var grayMatImage = new CvMat(frameMatImage.Rows, frameMatImage.Cols, MatrixType.U8C1))
        {
            // グレースケールへ変換
            frameMatImage.CvtColor(grayMatImage, ColorConversion.BgraToGray);
            var grayImage = Cv.GetImage(grayMatImage);
            //正規化する
            grayImage.EqualizeHist(grayImage);
            using (var cvMem = new OpenCvSharp.CvMemStorage())
            {
                //検出を行う
                Cv.HaarDetectObjects(
                                    grayImage,
                                    faceDetector,
                                    cvMem,
                                    1.1,
                                    10,
                                    0,
                                    new CvSize(10, 10))
                .Where(face => face.HasValue)
                .Select(face => face.Value.Rect)
                .ToList()
                .ForEach(faceRect =>
                {
                    //検出場所へ矩形表示
                    frame.DrawRect(faceRect, Cv.RGB(255, 0, 0), 2);
                });
            }
        }
    }
}

3. Rxのシーケンスに追加する

追加したメソッド(ImageProcess)を、Rxのシーケンスに挿入します。

(変更前)
var capture = new CvCapture(0);
Observable.Interval(TimeSpan.FromMilliseconds(1), DispatcherScheduler.Current)
    .Select(_ => Cv.QueryFrame(capture))
    .Where(frame => frame != null)
    .Do(frame => frame.PutText(string.Format("{0:MM/dd HH:mm:ss}", DateTime.Now), new CvPoint(0, 15), font, Cv.RGB(255, 255, 255)))
    .Subscribe(frame =>
    {

(変更後)
var capture = new CvCapture(0);
Observable.Interval(TimeSpan.FromMilliseconds(1), DispatcherScheduler.Current)
    .Select(_ => Cv.QueryFrame(capture))
    .Where(frame => frame != null)
    .Do(frame => frame.PutText(string.Format("{0:MM/dd HH:mm:ss}", DateTime.Now), new CvPoint(0, 15), font, Cv.RGB(255, 255, 255)))
    .Do(frame => ImageProcess(frame, faceDetector))
    .Subscribe(frame =>
    {

実行

実行して自分の顔へカメラを向けて下さい。顔の周りに矩形枠が表示されるはずです。
OpenCVに同梱されているカスケードファイルは精度が低いので、真正面じゃないと認識しないと思います。

また、顔以外を認識するカスケードファイルは、「顔以外」のものを画像認識するを参照して下さい。

ViewModelソース

最後に、ViewModel側のソースコード全てを掲載します。View側は変更無しです。
長い説明となりましたが、最後まで読んでいただきありがとうございました。

MainWindowViewModel.cs
public class MainWindowViewModel : ViewModel
{
    #region ImageSource 表示画像
    private BitmapSource _CaptureImageSource;
    /// <summary>
    /// 表示画像
    /// </summary>
    public BitmapSource CaptureImageSource
    {
        get
        { return _CaptureImageSource; }
        set
        {
            if (_CaptureImageSource == value)
                return;
            _CaptureImageSource = value;
            RaisePropertyChanged();
        }
    }
    #endregion
    /// <summary>
    /// 初期化
    /// </summary>
    public void Initialize()
    {
        // 画面にタイムスタンプを描画するためのフォント定義
        var font = default(CvFont);
        Cv.InitFont(out font, FontFace.HersheyScriptComplex, 0.5, 0.5);

        // 顔の分類器作成
        var faceDetector = Cv.Load<CvHaarClassifierCascade>("haarcascade_frontalface_default.xml");

        // キャプチャーデバイス
        var capture = new CvCapture(0);
        Observable.Interval(TimeSpan.FromMilliseconds(1), DispatcherScheduler.Current)
            .Select(_ => Cv.QueryFrame(capture))
            .Where(frame => frame != null)
            .Do(frame => frame.PutText(string.Format("{0:MM/dd HH:mm:ss}", DateTime.Now), new CvPoint(0, 15), font, Cv.RGB(255, 255, 255)))
            .Do(frame => ImageProcess(frame, faceDetector))
            .Subscribe(frame =>
            {
                var bitmapSource = frame.ToBitmapSource();
                frame.Dispose();
                this.CaptureImageSource = bitmapSource;
            });
    }
    /// <summary>
    /// 顔認識を行い、矩形枠を描画する
    /// </summary>
    /// <param name="frame">キャプチャーした画像</param>
    /// <param name="faceDetector">顔の分類器</param>
    private void ImageProcess(IplImage frame, CvHaarClassifierCascade faceDetector)
    {
        using (var frameMatImage = Cv.GetMat(frame))
        {
            using (var grayMatImage = new CvMat(frameMatImage.Rows, frameMatImage.Cols, MatrixType.U8C1))
            {
                // グレースケールへ変換
                frameMatImage.CvtColor(grayMatImage, ColorConversion.BgraToGray);
                var grayImage = Cv.GetImage(grayMatImage);
                //正規化する
                grayImage.EqualizeHist(grayImage);
                using (var cvMem = new OpenCvSharp.CvMemStorage())
                {
                    //検出を行う
                    Cv.HaarDetectObjects(
                                        grayImage,
                                        faceDetector,
                                        cvMem,
                                        1.1,
                                        10,
                                        0,
                                        new CvSize(10, 10))
                    .Where(face => face.HasValue)
                    .Select(face => face.Value.Rect)
                    .ToList()
                    .ForEach(faceRect =>
                    {
                        //検出場所へ矩形表示
                        frame.DrawRect(faceRect, Cv.RGB(255, 0, 0), 2);
                    });
                }
            }
        }
    }
}
11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11