iOSアプリからOpenCVを使ってみる

More than 5 years have passed since last update.

iOSアプリ内で写真のモノクロ化や減色などといった画像編集を行いたくなった。OpenCVを使うと、「たったの6ステップ!『漫画カメラ』風に写真を加工するiPhoneアプリの作り方」みたいなことが簡単にできるらしいので、インストールして簡単なサンプルを作ってみたよ。


事前準備

インストールに必要なので事前にcmakeをインストールしておく。

brew install cmake

シンボリックリンクを張って /Developer 経由でヘッダファイルなどを参照できるようにする。

cd /

sudo ln -s /Applications/Xcode.app/Contents/Developer


OpenCVフレームワークをビルド

以下の手順でOpenCVフレームワークをビルドする。masterブランチだとビルドに失敗したので、2.4ブランチでビルドを行った。

cd ~/src

git clone https://github.com/Itseez/opencv.git
cd opencv/
git checkout 2.4
cd ..
python opencv/ios/build_framework.py ios


プロジェクトへOpenCVフレームワークを追加

新規のiOSプロジェクトを作成し、そこへ先ほどビルドしたOpenCVフレームワークを追加する。

まずは新規のiOSプロジェクトを作成し、画面左のプロジェクトナビゲータの「Frameworks」を右クリックし、「Add Files to "プロジェクト名"...」を選択し、~/src/ios/opencv2.framework を選んで「Add」ボタンを押す。

次に、${プロジェクト名}-Prefix.pch へ以下のコードを追加。

#ifdef __cplusplus

#import <opencv2/opencv.hpp>
#endif


Objective-CとC++を混在させる

OpenCVの処理はC++で記述する必要があるので、OpenCVの処理を行うソースファイルの拡張子を「&ast;.m」から「&ast;.mm」へ変更しておく。

(これに気がつかずに「動かない動かない」と小一時間悩んだよ…)


サンプルコード

OpenCVによる画像編集のサンプルとして、カラーの画像をモノクロにしてみる。


UIImageとMatの相互変換

OpenCVで画像を操作するには事前にUIImageオブジェクトをMatオブジェクトへ変換する必要がある。

まずは、UIImageとMatを相互変換するcvMatFromUIImage:メソッドとUIImageFromCVMat:メソッドをViewController.mmへ定義する。

- (cv::Mat)cvMatFromUIImage:(UIImage *)image

{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
CGFloat cols = image.size.width;
CGFloat rows = image.size.height;

cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags

CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);

return cvMat;
}

-(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat

{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
CGColorSpaceRef colorSpace;

if (cvMat.elemSize() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}

CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

// Creating CGImage from cv::Mat
CGImageRef imageRef = CGImageCreate(cvMat.cols, //width
cvMat.rows, //height
8, //bits per component
8 * cvMat.elemSize(), //bits per pixel
cvMat.step[0], //bytesPerRow
colorSpace, //colorspace
kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info
provider, //CGDataProviderRef
NULL, //decode
false, //should interpolate
kCGRenderingIntentDefault //intent
);

// Getting UIImage from CGImage
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);

return finalImage;
}


モノクロにしてみる

写真をモノクロにしてみる。

- (IBAction)doDidPress:(id)sender {

UIImage *image = [UIImage imageNamed:@"neko_tsubaki_blue_car.jpg"];

// 写真をモノクロにする
cv::Mat srcMat = [self cvMatFromUIImage:image];
cv::Mat greyMat;
cv::cvtColor(srcMat, greyMat, CV_BGR2GRAY);
UIImage *greyImage = [self UIImageFromCVMat:greyMat];

self.imageView.image = greyImage;
}


ソース

ソースはgithubに上げてあります。


参考