Core Imageの顔認識をOS Xで - その7 [OpenCV関係]
ここんとこ毎日集中的にやってる顔認識比較アプリ。ちょっと急ぎすぎてるせいもあって、またひとつ訂正。
昨日、OpenCVのヘッダを読み込ませないためにOpenCVの定義が必要なものは全部voidポインタにしないといけないと書いた。しかしちょっと考えれば、もうすこしマシな手があることがわかる。つまりCore FoundationのOpaque Typeと同じことをすればいい。
昨日IplImage構造体へのポインタをvoidにしたけど、こうする。
昨日、OpenCVのヘッダを読み込ませないためにOpenCVの定義が必要なものは全部voidポインタにしないといけないと書いた。しかしちょっと考えれば、もうすこしマシな手があることがわかる。つまりCore FoundationのOpaque Typeと同じことをすればいい。
昨日IplImage構造体へのポインタをvoidにしたけど、こうする。
typedef struct _IplImage IplImage; typedef struct CvMemStorage CvMemStorage; @interface PTFaceDetectorUsingOpenCV : NSObject { void *faceCascade; void *leftEyeCascade; void *rightEyeCascade; void *mouthCascade; IplImage *orgImage; IplImage *grayImage; IplImage *smallImage; CvMemStorage *storage; }いままでさんざCore Foundationを見てきたのに、昨日は思いつかなかった。やっぱりボケてる。 ところでCvHaarClassifierCascadeへのポインタはvoidのままにした。自分の書いたコードを見てみるとvoidポインタのキャストの方が多かった。 まあ、わずかな手間の違いと、読みやすさのどちらをとるか、ということになる。
5.4 OpenCVとCoreImageでFaceFeatureを共通して使う
CIDetectorはCIFaceFeatureの配列を返す。 OpenCVの顔認識オブジェクトの返り値も同じCIFaceFeatureにしたい。でもCIFeatureのヘッダを見るとプロパティが全部readonlyのコンテナクラスで、初期化メソッドが公開されていない。しょうがないのでclass-dumpして初期化メソッドを探した。 すると@interface CIFaceFeature : CIFeature { struct CGRect bounds; BOOL hasLeftEyePosition; struct CGPoint leftEyePosition; BOOL hasRightEyePosition; struct CGPoint rightEyePosition; BOOL hasMouthPosition; struct CGPoint mouthPosition; BOOL hasTrackingID; int trackingID; BOOL hasTrackingFrameCount; int trackingFrameCount; } + (id)faceFeaturesWithBoundsArray:(const struct CGRect *)arg1 count:(unsigned long long)arg2; + (id)faceFeatureWithBounds:(struct CGRect)arg1; @property int trackingFrameCount; // @synthesize trackingFrameCount; @property BOOL hasTrackingFrameCount; // @synthesize hasTrackingFrameCount; @property int trackingID; // @synthesize trackingID; @property BOOL hasTrackingID; // @synthesize hasTrackingID; @property struct CGPoint mouthPosition; // @synthesize mouthPosition; @property BOOL hasMouthPosition; // @synthesize hasMouthPosition; @property struct CGPoint rightEyePosition; // @synthesize rightEyePosition; @property BOOL hasRightEyePosition; // @synthesize hasRightEyePosition; @property struct CGPoint leftEyePosition; // @synthesize leftEyePosition; @property BOOL hasLeftEyePosition; // @synthesize hasLeftEyePosition; @property struct CGRect bounds; // @synthesize bounds; - (id).cxx_construct; - (id)initWithBounds:(struct CGRect)arg1 hasLeftEyePosition:(BOOL)arg2 leftEyePosition:(struct CGPoint)arg3 hasRightEyePosition:(BOOL)arg4 rightEyePosition:(struct CGPoint)arg5 hasMouthPosition:(BOOL)arg6 mouthPosition:(struct CGPoint)arg7 hasTrackingID:(BOOL)arg8 trackingID:(int)arg9 hasTrackingFrameCount:(BOOL)arg10 trackingFrameCount:(int)arg11; - (id)initWithBounds:(struct CGRect)arg1; - (id)type; @end全部のインスタンス変数を一気に受ける初期化メソッドがあった。これでOpenCV側も同じCIFaceFeatureの配列を返すことができる。コンパイルのときに知らないメソッドだ、と警告が出るけど実行時には問題なくちゃんとこの初期化メソッドが呼ばれる。警告が鬱陶しかったらNSObjectのカテゴリを作ってこのメソッドの宣言だけしておけばいい。こういうルーズさはObjective-Cならでは。 ひとつ注意しないといけないことがあって、OpenCVの座標は左上が原点になっている。cvHaarDetectObjects()で見つかった顔の位置CvRectはCGRectに変換するときに左下からに読み替える必要がある。
5.5 CIFaceFeatureのサブクラス
これとは別に、CIDetectorにCIDetectorTrackingオプションをNOに設定したとしてもCIFaceFeatureはhasTrackingIDにYESが設定されて返ってくる。これはバグだ。これでは不便なのでこのバグを取ったサブクラスを作ることにした。しかし、CIFaceFeatureのオプションはCIFaceFeatureが直接参照することはできない。 CIDetectorTrackingにNOが設定されたときの、CIFaceFeatureのtrackingIDは一見デタラメだけど、なぜか16進下3桁はいつも同じ0xF72になっている。なんでこうなのかはもちろんわからないけど、今のバージョンは必ずこうなるらしい。次のバージョンでこのバグがfixされることを期待して、サブクラスでこうした。- (BOOL)hasTrackingID { if ([super hasTrackingID]) return checkMatchMagicNumber([self trackingID]); return NO; } static int magicNumberValue = 0x0f72; static int magicNumberOrder = 0x0fff; static BOOL checkMatchMagicNumber(int num) { return (magicNumberOrder & num) != magicNumberValue; }こういうのって、忘れてしまってあとから見て何やってるのか全然わからなくなるので、コメントだけでなく大袈裟に書いておくのがいい。ソースにあるコメントってやっぱりどうしても読み飛ばしてしまいがちだし。
2013-08-02 22:02
nice!(0)
コメント(0)
トラックバック(0)
コメント 0