CIFilter, OpenCV, vImageを使って画像フィルタ処理をかける
Instagramなどの写真アプリでよくある画像フィルタを作ってみます。
調べたところ、大きく3つの方法があるようです。
- CIFilter を使う方法
- OpenCV を使う方法
- vImage を使う方法
それぞれについて、コードを交えて説明を書きます。
CIFilterを使う方法
CIFilterとは、iOS 5以降で使えるようになった、CoreImage.frameworkが提供する機能です。
CIFilterのかけ方
- 元画像(UIImageクラス)をCIImageオブジェクトに変換
- CIFilterオブジェクトを作る
- CIFilterのoutputImageプロパティから、フィルタ後の画像を取得
- 取得した画像データ(CIImage)をUIImageに変換する
ソース
// 元画像を取得 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 の部分で指定できます。 フィルタによっては、強度や露光量などのパラメータを設定できるものもあります。 その場合は、
[filter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputIntensity"]; [filter setValue:[NSNumber numberWithFloat:2.0] forKey:@"inputRadius"];
のように、CIFilterオブジェクトにセットしていけばOKです。
実行結果
実行前
実行後
もっと詳しく知りたい
- CIFilter の効果を一通り試せるサンプルコード(フィルタ名一覧つき) - Over&Out その後
- 【iOS】たった数行で画像のフィルタ/エフェクトが実現できる超便利フレームワークCoreImage。とりあえず7つ紹介! - @kitano_ow 's blog
OpenCVを使う方法
OpenCVとは、画像処理・画像認識のために作られたオープンソースのC言語ライブラリです。
OpenCVを使うためのフレームワークはiOS標準搭載ではないので、
ここからダウンロードしてプロジェクトに追加する必要があります。
以前はソースをコンパイルして扱う必要があったようですが、
今はフレームワークが使えるようになったので楽チンです。
OpenCVの使い方
- 元画像(UIImageクラス)をIplImageオブジェクトに変換
- IplImageにごにょごにょと操作を加える
- フィルタ後のデータ(IplImage)をUIImageに変換する
ソース
著者はOpenCVに不慣れなため、参考リンクを貼ることにします。
変換から実際のフィルタリングまで、丁寧で非常に分かりやすいです。
OpenCVで写真を漫画風に加工しよう 〜実装編〜 | Developers.IO
実行結果
実行前
実行後
もっと詳しく知りたい
vImageを使う方法
vImageとは、iOS 5以降で使えるようになった、Accelerate frameworkが提供する機能です。
iOSデバイスのハードウェア向けに最適化されており、高速に処理できると言われています。
vImageの使い方
- 描画領域の確保など前準備を行う
- 画像フィルタ処理(畳み込み演算)をかける
- フィルタ後のデータを取得する
ソース
// 描画領域の確保など、前準備 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変数に渡す配列を変化させることでフィルタを選択できます。
(画像処理の知識が必要となりますね)
実行結果
実行前
実行後