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変数に渡す配列を変化させることでフィルタを選択できます。
(画像処理の知識が必要となりますね)
実行結果
実行前
実行後
もっと詳しく知りたい
まとめ
- CIFilter、OpenCV、vImageを使って画像フィルタを行った
- CIFilterは画像処理の知識なしにフィルタを選ぶだけでOK
- OpenCV、vImageは画像処理の知識があれば複雑な処理もかけられる