IplImage(cv::Mat)をObjective-Cクラスでラップする

#はじめに
今更 `IplImage`かよ、という方、スミマセン。
この項で提示したいのは、OpenCVでのイメージの扱いを Objective-Cでラップすると便利だよ、ということなのです。`cv::Mat`でも一向に構いませんので、その辺りは便利な風に読み替えていただければ幸いです。
ちなみに、OpenCV 3.0系統ではいわゆる、`IplImage`的な Cインタフェースが今後メンテされないことが決まっているそうです。`cv::Mat`を使うように心がけましょうｗ

#便利なポイント
Mac で OpenCV 関連のプログラミングを行うにあたって、1つの方法としては OpenCVの世界にドップリ浸かって、ウィンドウも画像表示も、何から何までオープンソースの世界で済ます、というのがあると思います。
ただ、動作環境ターゲットを Macに絞った場合、やはり Cocoaが使いたくなる。さらに言えば、C++ boostの shared_ptrとかも使わず、Objective-Cの ARCでいいじゃない、という気分にもなってきます。
さらに、Xcodeには Debug Quick Look という強力な機能が備わっていて、Objective-Cで Wrapすればその機能も活用できます。これは、デバッガ上から何時でも`cv::imshow()`を実行できるとイメージすると良いかも。

#Swiftの存在
開発言語として Swiftも出てきましたね。
原則、ポインタアクセスなどを許さない Swiftですが、OpenCVを使おうとすると否が応でも Objective-Cでのラップが必要になってきます。
メイン部分や UI系統はあくまでも Swiftで組みつつ、内部のイメージ処理部分などを Objective-Cでラップされた OpenCVなど利用すれば、作りもエレガントなものになるのかなと思われますね。

#では、ラップしてみる
まずは宣言。

```objc:OImage.h
@interface OImage : NSObject
@property (nonatomic, assign, readonly) IplImage *iplImage;
- (id)initWithIplImage:(IplImage *)iplImage;
- (id)initWithCvSize:(CvSize)size depth:(int)depth channels:(int)channels;
- (id)initWithBytes:(void *)bytes size:(CvSize)size bytesPerRow:(int)bytesPerRow channels:(int)channels;
- (NSImage *)NSImage;
```

そして実装です。(NULLチェック等はざっくり省略しております...)
`-(CGImageRef)createCGImage`メソッドと、`-(id)debugQuickLookObject`メソッドの組合せが、Xcodeの Debug Quick Look 機能に載る部分になります。

```objc:OImage.m
@implementation OImage
- (id)initWithIplImage:(IplImage *)iplImage {
    if ((self = [super init]) == nil)
        return nil;
    _iplImage = cvCloneImage(iplImage);
    return self;
}

- (id)initWithCvSize:(CvSize)size depth:(int)depth channels:(int)channels {
    if ((self = [super init]) == nil)
        return nil;
    _iplImage = cvCreateImage(size, depth, channels);
    return self;
}

- (id)initWithBytes:(void *)bytes size:(CvSize)size bytesPerRow:(int)bytesPerRow channels:(int)channels {
    if ((self = [super init]) == nil)
        return nil;
    _iplImage = cvCreateImage(size, depth, channels);
    memcpy(_iplImage->imageData, bytes, bytesPerRow * size.height);
    return self;
}

- (CGImageRef)createCGImage {
    CGBitmapInfo bitmapInfo = (CGBitmapInfo)kCGImageAlphaNone;
    IplImage *iplImage = self.iplImage;
    int componentBytes = 1;
    NSColorSpace *cs = nil;
    if (iplImage->nChannels == 1) {
        colorspace = [NSColorSpace genericGrayColorSpace];
        if (iplImage->depth == IPL_DEPTH_16U) {
            bitmapInfo |= kCGBitmapByteOrder16Host;
            componentBytes = sizeof(short);
        } else if (iplImage->depth == IPL_DEPTH_32F) {
            bitmapInfo |= kCGBitmapByteOrder32Host | kCGBitmapFloatComponents;
            componentBytes = sizeof(float);
        } else if (iplImage->depth == IPL_DEPTH_32S) {
            bitmapInfo |= kCGBitmapByteOrder32Host;
            componentBytes = sizeof(int);
        }
    } else {
        colorspace = [NSColorSpace genericRGBColorSpace];
        bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipleLast;
        if (iplImage->nChannels == 3) {
            iplImage = cvCreateImage(cvGetSize(iplImage), IPL_DEPTH_8U, 4);
            cvCvtColor(self.iplImage, iplImage, CV_RGB2RGBA);
        }
    }
    CGContextRef context = CGBitmapContextCreate(NULL, iplImage->width, iplImage->height, componentBytes * 8, iplImage->widthStep, cs.CGColorSpace, bitmapInfo);
    void *data = CGBitmapContextGetData(context);
    memcpy(data, iplImage->imageData, iplImage->imageSize);
    CGImageRef cgimage = CGBitmapContextCreateImage(context);
    CGContextRelease(context);
    if (iplImage != self.iplImage)
        cvReleaseImage(&iplImage);
    return cgimage;
}

- (NSImage *)NSImage {
    CGImageRef cgimage = [self createCGImage];
    if (cgimage == NULL)
        return nil;
    NSImage *nsimage = [[NSImage alloc] initWithCGImage:cgimage size:NSZeroSize];
    CGImageRelease(cgimage);
    return nsimage;
}

- (id)debugQuickLookObject {
    return [self NSImage];
}

- (void)dealloc {
    if (_iplImage != NULL)
        cvReleaseImage(&_iplImage);
}
```

あとは、せっかく作った Wrapperクラスですので、grayscaleとか、HSV変換とか、`cvFindContours()`辺りの呼出しとか、たくさん細々とメソッドを追加していけば良いかと思います。