Core Imageの顔認識をOS Xで - その6 [OpenCV関係]
昨日までながながとピクセルフォーマットの問題にかかずらってしまったけど、できればこういうのをプログラミングの問題とはしたくない。プログラミングとはアルゴリズムの実装であると僕は考えてるので、エンディアンの問題や実数整数などのバイト表現の問題なんかと同じで、プラットフォームで一貫していてもらって気にしなくてすむようにしてもらいたい、と思う。
まあ、それはもういいとして今日から実装に入る。
渡し方のひとつめはCVPixelBufferを直接渡す形で、OpenCVへはこれを使う。ふたつ目はCIImageに変換した画像を渡す形で、これはCIDetector用。あとのふたつはまあこれもおまけ。なにかに使い道があるかもしれない。
Objective-C++にするとどうもXcodeのcode completionの動作がおかしくなる(C++の関数名だけでなく、知ってたはずのObjective-Cのメソッドも出てこなくなったり、"Jump to definiton"で飛ばなかったりする)。しょうがないのでヘッダはOpenCVとは無関係にするためにこうした。このクラスのimplementationでは <opencv2/opencv.hpp> を読み込んで、void*を使うたびにキャストしている。
ちょっと面倒だけど、こうすれば全部を.mmにせずにすむ。
初期化メソッドはカスケードデータを読み込むだけ。
そのあと
顔認識はCIDetectorにあわせて顔だけでなく目と口も追加した。これの学習データは昔からOpenCVについているhaarcascade_mcs_lefteye.xlmなんかを使った。検出は顔をとらえて、あったらその顔のCvRectをマスクにしてその中で目と口を認識する、という手順。
まあ、それはもういいとして今日から実装に入る。
5 比較用のテストアプリ
ということで、あとは普通のドキュメントベースではないアプリに仕上げれば比較用のテストアプリができる。5.1 AV Foudationを使ったFaceTimeカメラの読み込みオブジェクト
AV Foundationの手続きはひとつのクラスにカプセルしてしまおう。こんなやつ。@interface PTFaceTimeCamera : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> { id delegate; AVCaptureSession *session; AVCaptureVideoDataOutput *dataOutput; AVCaptureDevice *device; AVCaptureDeviceInput *deviceInput; NSInteger droppedFrameCount; } ....このオブジェクトがAVCaptureVideoDataOutputのdelegateになり、さらにこのオブジェクトのdelegateへ画像ファイルを渡す、という形にする。このオブジェクトのdelegateが実装する受け取り用のメソッドは
@interface NSObject (PTFaceTimeCameraCategory) - (void)FaceTimeCamera:(PTFaceTimeCamera *)camera passingCVPixelBuffer:(CVPixelBufferRef)imageBuffer withTimeStamp:(NSDate *)timeStamp; - (void)FaceTimeCamera:(PTFaceTimeCamera *)camera convertingCIImage:(CIImage *)ciimage withTimeStamp:(NSDate *)timeStamp; - (void)FaceTimeCamera:(PTFaceTimeCamera *)camera convertingCGImage:(CGImageRef)cgimage withTimeStamp:(NSDate *)timeStamp; - (void)FaceTimeCamera:(PTFaceTimeCamera *)camera convertingNSImageRep:(NSImageRep *)irep withTimeStamp:(NSDate *)timeStamp; @endというようなもの。おまけでタイムスタンプでもつけるか、と思ってつけた。これはもともとCMSampleBufferがDTS/PTSデータ(MPEG-2のタイムスタンプ)を持っていたので、使えるなら使ってみようと思っただけ。深い意味はない。カメラからのデータのPTS(Presentation Time Stamp)に何か意味があるかと思ってデコードしてみたら、ブートした時刻(uptimeコマンドで返る時刻)が基準になっていた。これをNSDateに変換して渡すようにした。あまり役には立たない。どうやってるかは、あとで公開するソースを見て欲しい。
渡し方のひとつめはCVPixelBufferを直接渡す形で、OpenCVへはこれを使う。ふたつ目はCIImageに変換した画像を渡す形で、これはCIDetector用。あとのふたつはまあこれもおまけ。なにかに使い道があるかもしれない。
5.2 CIDetectorを保持するオブジェクト
CIDetectorは生のまま使ってもいいんだけど、あとのことを考えて一皮ラッパを作った。@interface PTFaceDetectorUsingCIDetector : NSObject { CIDetector *detector; } - (id)init; - (id)initWithContext:(CIContext *)context; - (id)initWithContext:(CIContext *)context andOptions:(NSDictionary *)options; - (NSArray *)featuresInImage:(CIImage *)image; @end実質的にCIDetectorに内部で渡して結果を外に出すだけ。
5.3 OpenCVの顔検出オブジェクト
これはけっこうめんどう。とりあえずこういうふうにした。interface PTFaceDetectorUsingOpenCV : NSObject { void *faceCascade; void *leftEyeCascade; void *rightEyeCascade; void *mouthCascade; void *orgImage; void *grayImage; void *smallImage; void *storage; int scale; int code; int samplePerPixel; BOOL findEyesAndMouths; } @property (readwrite) BOOL findEyesAndMouths; - (id)initWithImageScaling:(int)scaling; - (NSArray *)featuresInPixelBuffer:(CVPixelBufferRef)buffer;CvHaarClassifierCascadeやIplImageをインスタンス変数として保持しているけど、全部void*型になっている。これはOpenCV2のヘッダがC++用になっていて、それをこのヘッダに書くとこれを読み込むファイルは全部Objective-C++対応(拡張子.mm)にしないといけない。
Objective-C++にするとどうもXcodeのcode completionの動作がおかしくなる(C++の関数名だけでなく、知ってたはずのObjective-Cのメソッドも出てこなくなったり、"Jump to definiton"で飛ばなかったりする)。しょうがないのでヘッダはOpenCVとは無関係にするためにこうした。このクラスのimplementationでは <opencv2/opencv.hpp> を読み込んで、void*を使うたびにキャストしている。
ちょっと面倒だけど、こうすれば全部を.mmにせずにすむ。
初期化メソッドはカスケードデータを読み込むだけ。
NSBundle *mainBundle = [NSBundle mainBundle]; tempPath = [mainBundle pathForResource:haarcascadeFrontalfaceAlt2 ofType:haarcascadeType]; faceCascade = cvLoad([tempPath fileSystemRepresentation], 0, 0, 0); if (faceCascade == NULL) { [self release]; return nil; } ....こんなふうにバンドルリソースから読み込む。ほんとはIplImageの領域も確保したいけど、初期化時には大きさがわからないので最初のCVPixelBufferがわたってきたときにやる。
そのあと
- CVPixelBufferからIplImageにコピー
- グレーに変換
- サイズをダウンスケール
顔認識はCIDetectorにあわせて顔だけでなく目と口も追加した。これの学習データは昔からOpenCVについているhaarcascade_mcs_lefteye.xlmなんかを使った。検出は顔をとらえて、あったらその顔のCvRectをマスクにしてその中で目と口を認識する、という手順。
2013-08-01 22:45
nice!(0)
コメント(0)
トラックバック(0)
コメント 0