LoginSignup
7
7

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-11-13

はじめに

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

便利なポイント

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など利用すれば、作りもエレガントなものになるのかなと思われますね。

では、ラップしてみる

まずは宣言。

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 機能に載る部分になります。

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()辺りの呼出しとか、たくさん細々とメソッドを追加していけば良いかと思います。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7