#はじめに
前回の投稿では、カメラキャプチャアプリを作成しました。Rx
とOpenCVSharp
を組み合わせると、簡単にできる事が分かりました。
ここまでくると欲を出したくなる物で、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\haarcascades
にhaarcascade_frontalface_default.xml
があるので、プロジェクトに取り込む。 - 取り込み方法は、VS2013の「ソリューションエクスプローラ」で「LivetWPFApplication1」を選択し、右クリック[追加]-[既存の項目]を選択。ファイル選択エクスプローラが開くので、上記xmlファイルを選択して下さい。
- 取り込み後、プロジェクトの中に
haarcascade_frontalface_default.xml
が追加されるので、プロパティを表示し、「ビルドアクション」は「なし」、「出力ディレクトリーにコピー」も「新しい場合にコピー」を選択して下さい。
#####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側は変更無しです。
長い説明となりましたが、最後まで読んでいただきありがとうございました。
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);
});
}
}
}
}
}