Think Big Act Local

iPhone開発を軸にブレブレの記事を書いていきます。

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

f:id:himaratsu:20130429015624j:plain f:id:himaratsu:20130429015629j:plain

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です。

実行結果

実行前

f:id:himaratsu:20130429015624j:plain

実行後

f:id:himaratsu:20130429015629j:plain

もっと詳しく知りたい

OpenCVを使う方法

OpenCVとは、画像処理・画像認識のために作られたオープンソースC言語ライブラリです。
OpenCVを使うためのフレームワークiOS標準搭載ではないので、
ここからダウンロードしてプロジェクトに追加する必要があります。

以前はソースをコンパイルして扱う必要があったようですが、
今はフレームワークが使えるようになったので楽チンです。

OpenCVの使い方

  • 元画像(UIImageクラス)をIplImageオブジェクトに変換
  • IplImageにごにょごにょと操作を加える
  • フィルタ後のデータ(IplImage)をUIImageに変換する

ソース

著者はOpenCVに不慣れなため、参考リンクを貼ることにします。
変換から実際のフィルタリングまで、丁寧で非常に分かりやすいです。
OpenCVで写真を漫画風に加工しよう 〜実装編〜 | Developers.IO

実行結果

実行前

f:id:himaratsu:20130429015624j:plain

実行後

f:id:himaratsu:20130429023209j:plain

もっと詳しく知りたい

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変数に渡す配列を変化させることでフィルタを選択できます。
(画像処理の知識が必要となりますね)

実行結果

実行前

f:id:himaratsu:20130429015624j:plain

実行後

f:id:himaratsu:20130429023124j:plain

もっと詳しく知りたい

まとめ

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