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


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

## 事前準備

インストールに必要なので事前に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](https://github.com/thata/OpenCVApp)に上げてあります。

## 参考

- 「Instalation in iOS」
  - http://docs.opencv.org/trunk/doc/tutorials/introduction/ios_install/ios_install.html#ios-installation
- 「OpenCV iOS Hello」
  - http://docs.opencv.org/trunk/doc/tutorials/ios/hello/hello.html#opencvioshelloworld
- 「OpenCV iOS - Image Processing」
  - http://docs.opencv.org/trunk/doc/tutorials/ios/image_manipulation/image_manipulation.html