CIFilter, OpenCV, vImageを使って画像フィルタ処理をかける

<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429015624.jpg" alt="f:id:himaratsu:20130429015624j:plain" title="f:id:himaratsu:20130429015624j:plain" class="hatena-fotolife" itemprop="image"></span>　<span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429015629.jpg" alt="f:id:himaratsu:20130429015629j:plain" title="f:id:himaratsu:20130429015629j:plain" class="hatena-fotolife" itemprop="image"></span></p>


Instagramなどの写真アプリでよくある画像フィルタを作ってみます。  
調べたところ、大きく3つの方法があるようです。

* **CIFilter を使う方法**
* **OpenCV を使う方法**  
* **vImage を使う方法**  
  
  
それぞれについて、コードを交えて説明を書きます。


## CIFilterを使う方法
CIFilterとは、iOS 5以降で使えるようになった、CoreImage.frameworkが提供する機能です。  

#### CIFilterのかけ方
* 元画像（UIImageクラス）をCIImageオブジェクトに変換
* CIFilterオブジェクトを作る
* CIFilterのoutputImageプロパティから、フィルタ後の画像を取得
* 取得した画像データ（CIImage）をUIImageに変換する

#### ソース
``` objc
// 元画像を取得
UIImage *originImage = [UIImage imageNamed:@"sample.png"];

// UIImageをCIImageに変換
CIImage *filteredImage = [[CIImage alloc] initWithCGImage:originImage.CGImage];

// CIFilterを作成（今回はモノクロ風フィルタをかけます）
CIFilter *filter = [CIFilter filterWithName:@"CIMinimumComponent"];
[filter setValue:filteredImage forKey:@"inputImage"];

// フィルタ後の画像を取得
filteredImage = filter.outputImage;

// CIImageをUIImageに変換する
CIContext *ciContext = [CIContext contextWithOptions:nil];
CGImageRef imageRef = [ciContext createCGImage:filteredImage
                                      fromRect:[filteredImage extent]];
UIImage *outputImage  = [UIImage imageWithCGImage:imageRef
                                            scale:1.0f
                                      orientation:UIImageOrientationUp];
CGImageRelease(imageRef);

// 表示
[self.view addSubview:outputImage];

```

どのフィルタを使うかは CIMinimumComponent の部分で指定できます。
フィルタによっては、強度や露光量などのパラメータを設定できるものもあります。
その場合は、

``` objc
[filter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputIntensity"];
[filter setValue:[NSNumber numberWithFloat:2.0] forKey:@"inputRadius"];
```

のように、CIFilterオブジェクトにセットしていけばOKです。

#### 実行結果
実行前
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429015624.jpg" alt="f:id:himaratsu:20130429015624j:plain" title="f:id:himaratsu:20130429015624j:plain" class="hatena-fotolife" itemprop="image"></span></p>

実行後
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429015629.jpg" alt="f:id:himaratsu:20130429015629j:plain" title="f:id:himaratsu:20130429015629j:plain" class="hatena-fotolife" itemprop="image"></span></p>

#### もっと詳しく知りたい
* <a style="color:#0070C5;" href="http://d.hatena.ne.jp/shu223/20111117/1321496682" target="_blank">CIFilter の効果を一通り試せるサンプルコード（フィルタ名一覧つき） - Over&Out その後</a><a href="http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/shu223/20111117/1321496682" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shu223/20111117/1321496682" alt="" /></a>
* <a style="color:#0070C5;" href="http://blog.oneworld-inc.com/entry/2013/02/23/000231" target="_blank">【iOS】たった数行で画像のフィルタ/エフェクトが実現できる超便利フレームワークCoreImage。とりあえず7つ紹介！ - @kitano_ow 's blog</a><a href="http://b.hatena.ne.jp/entry/http://blog.oneworld-inc.com/entry/2013/02/23/000231" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/http://blog.oneworld-inc.com/entry/2013/02/23/000231" alt="" /></a>


## OpenCVを使う方法
OpenCVとは、画像処理・画像認識のために作られたオープンソースのC言語ライブラリです。  
OpenCVを使うためのフレームワークはiOS標準搭載ではないので、  
[ここからダウンロードしてプロジェクトに追加する必要があります。](http://sourceforge.net/projects/opencvlibrary/files/opencv-ios/2.4.3/)  
  
以前はソースをコンパイルして扱う必要があったようですが、  
今はフレームワークが使えるようになったので楽チンです。

#### OpenCVの使い方
* 元画像（UIImageクラス）をIplImageオブジェクトに変換
* IplImageにごにょごにょと操作を加える
* フィルタ後のデータ（IplImage）をUIImageに変換する

#### ソース
著者はOpenCVに不慣れなため、参考リンクを貼ることにします。  
変換から実際のフィルタリングまで、丁寧で非常に分かりやすいです。  
<a style="color:#0070C5;" href="http://dev.classmethod.jp/smartphone/opencv-manga-2/" target="_blank">OpenCVで写真を漫画風に加工しよう 〜実装編〜 ｜ Developers.IO</a><a href="http://b.hatena.ne.jp/entry/http://dev.classmethod.jp/smartphone/opencv-manga-2/" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/http://dev.classmethod.jp/smartphone/opencv-manga-2/" alt="" /></a>  
  

#### 実行結果
実行前
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429015624.jpg" alt="f:id:himaratsu:20130429015624j:plain" title="f:id:himaratsu:20130429015624j:plain" class="hatena-fotolife" itemprop="image"></span></p>

実行後
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429023209.jpg" alt="f:id:himaratsu:20130429023209j:plain" title="f:id:himaratsu:20130429023209j:plain" class="hatena-fotolife" itemprop="image"></span></p>

#### もっと詳しく知りたい
* <a style="color:#0070C5;" href="http://d.hatena.ne.jp/shu223/20121129/1354145960" target="_blank">たったの6ステップ！『漫画カメラ』風に写真を加工するiPhoneアプリの作り方 - Over&Out その後</a><a href="http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/shu223/20121129/1354145960" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shu223/20121129/1354145960" alt="" /></a>



## vImageを使う方法
vImageとは、iOS 5以降で使えるようになった、Accelerate frameworkが提供する機能です。    
iOSデバイスのハードウェア向けに最適化されており、高速に処理できると言われています。


#### vImageの使い方
* 描画領域の確保など前準備を行う
* 画像フィルタ処理（畳み込み演算）をかける
* フィルタ後のデータを取得する

#### ソース
``` objc
// 描画領域の確保など、前準備
const size_t width = _originImage.size.width;
const size_t height = _originImage.size.height;
const size_t bytesPerRow = width * 4;

CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst;
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8,
                                               bytesPerRow, space,
                                               bitmapInfo);
CGColorSpaceRelease(space);

if (!bmContext) {
    return;
}

CGRect dstRect = CGRectMake(0, 0, width, height);
CGContextDrawImage(bmContext, dstRect, _originImage.CGImage);
    
UInt8 *data = (UInt8 *)CGBitmapContextGetData(bmContext);
if (!data) {
    CGContextRelease(bmContext);
    return;
}

vImage_Buffer src = {data, height, width, bytesPerRow};
    
// 出力容量確保
const size_t dstSize = sizeof(UInt8) * width * height * 4;
void *dstData = malloc(dstSize);
vImage_Buffer dst = {dstData, height, width, bytesPerRow};

// 今回は先鋭化のフィルタを作る
static int16_t sharpen_kernel[9] = {
    -1, -1, -1,
    -1, 9, -1,
    -1, -1, -1
};

// たたみこみ演算
vImageConvolve_ARGB8888(&src,
                        &dst,
                        NULL,
                        0,
                        0,
                        sharpen_kernel,
                        3,
                        3,
                        1,
                        NULL,
                        kvImageCopyInPlace);
    
// 処理結果をUIImageに格納
memcpy(data, dstData, dstSize);
free(dstData);
    
CGImageRef blurredImageRef = CGBitmapContextCreateImage(bmContext);
UIImage* blurred = [UIImage imageWithCGImage:blurredImageRef];
    
CGImageRelease(blurredImageRef);
CGContextRelease(bmContext);

[self.view addSubview:blurred];
```

vImageConvolve_ARGB8888 の第6変数に渡す配列を変化させることでフィルタを選択できます。  
（画像処理の知識が必要となりますね）

#### 実行結果
実行前  
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429015624.jpg" alt="f:id:himaratsu:20130429015624j:plain" title="f:id:himaratsu:20130429015624j:plain" class="hatena-fotolife" itemprop="image"></span></p>

実行後  
<p><span itemscope itemtype="http://schema.org/Photograph"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/h/himaratsu/20130429/20130429023124.jpg" alt="f:id:himaratsu:20130429023124j:plain" title="f:id:himaratsu:20130429023124j:plain" class="hatena-fotolife" itemprop="image"></span></p>

#### もっと詳しく知りたい
* <a style="color:#0070C5;" href="http://www.slideshare.net/t26v0748/vimage-13710390" target="_blank">vImageのススメ（改訂版）</a><a href="http://b.hatena.ne.jp/entry/http://www.slideshare.net/t26v0748/vimage-13710390" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/http://www.slideshare.net/t26v0748/vimage-13710390" alt="" /></a>
* <a style="color:#0070C5;" href="http://gihyo.jp/dev/serial/01/ios_oss" target="_blank">職人が教える！iOSアプリ開発で使いこなしたいとっておきのOSS：連載｜gihyo.jp … 技術評論社</a><a href="http://b.hatena.ne.jp/entry/http://gihyo.jp/dev/serial/01/ios_oss" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/http://gihyo.jp/dev/serial/01/ios_oss" alt="" /></a>


## まとめ
* CIFilter、OpenCV、vImageを使って画像フィルタを行った
* CIFilterは画像処理の知識なしにフィルタを選ぶだけでOK
* OpenCV、vImageは画像処理の知識があれば複雑な処理もかけられる


## 元記事
* http://himaratsu.hatenablog.com/entry/2013/04/29/024711