SSブログ

Core Imageの顔認識をOS Xで - その7 [OpenCV関係]

ここんとこ毎日集中的にやってる顔認識比較アプリ。ちょっと急ぎすぎてるせいもあって、またひとつ訂正。

昨日、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;
}
こういうのって、忘れてしまってあとから見て何やってるのか全然わからなくなるので、コメントだけでなく大袈裟に書いておくのがいい。ソースにあるコメントってやっぱりどうしても読み飛ばしてしまいがちだし。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立08/02献立08/03 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。