Advent Calendar 二日目です。
昨日に続いて happy_ryo です。
昨日は iOS と全く関係無い事を書きましたが、今日は気を取り直して iOS で、GPUImage や OpenCV を使わずに Core Image の機能を使ってチルトシフトっぽい画像加工をする方法を紹介したいと思います。レシピ自体は、Apple の Core Image のドキュメントで公開されているのですが、実際のソースコードが乗っていないので、ドキュメントを元に書いてみました。(Filter 生成時にパラメータを渡す事で、より簡潔に書く事が出来ますが、見やすいようにパラメータを個々に設定する記述をしてあります)
以下の手順で作業を進めます。
- 元画像をブラー処理したものを作成
- グラデーションを2種類作成する
- 2種類のグラデーションを合成する
- 元画像、ブラー処理画像、合成されたグラデーション画像をマスク処理で組み合わせる
下準備
元画像をブラー処理する
まずは CIGaussianBlur を利用して、ブラー処理された画像を作成します。 inputRadius パラメータを変更する事で、ブラーのかかり具合を調整する事が出来ます。サイズ合わせの所では、生成後の画像のサイズを元の画像にあわせて調整しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- (CIImage *)blurred { CIImage *ciImage = [[CIImage alloc] initWithImage:_imageView.image]; CIFilter *gaussianBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"]; // Filter のパラメータ設定 [gaussianBlurFilter setValue:ciImage forKey:kCIInputImageKey]; [gaussianBlurFilter setValue:@10.0 forKey:@"inputRadius"]; // ここまで // サイズ合わせ CIFilter *cropFilter = [CIFilter filterWithName:@"CICrop"]; [cropFilter setValue:gaussianBlurFilter.outputImage forKey:@"inputImage"]; [cropFilter setValue:[CIVector vectorWithCGRect:ciImage.extent] forKey:@"inputRectangle"]; CIImage *result = cropFilter.outputImage; return result; } |
グラデーションを2種類作成する
上から下、下から上に向けてのグラデーション2種類を CILinearGradient を使用して作成します。 inputPoint の値を調整する事で、チルトシフト加工後の画像のどの部分に焦点が当たっているように表現するかを変更する事が可能です。こちらもブラー処理と同様にサイズ調整をしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
- (CIImage *)topLinearGradient { CIFilter *filter = [CIFilter filterWithName:@"CILinearGradient"]; CIImage *ciImage = [[CIImage alloc] initWithImage:_imageView.image]; // 作成する画像のパラメータ設定 [filter setValue:[CIVector vectorWithX:0 Y:(CGFloat) (0.75 * ciImage.extent.size.height)] forKey:@"inputPoint0"]; [filter setValue:[CIColor colorWithRed:0 green:1 blue:0 alpha:0.9] forKey:@"inputColor0"]; [filter setValue:[CIVector vectorWithX:0 Y:(CGFloat) (0.5 * ciImage.extent.size.height)] forKey:@"inputPoint1"]; [filter setValue:[CIColor colorWithRed:0 green:1 blue:0 alpha:0] forKey:@"inputColor1"]; // ここまで // サイズ合わせ CIFilter *cropFilter = [CIFilter filterWithName:@"CICrop"]; [cropFilter setValue:filter.outputImage forKey:@"inputImage"]; [cropFilter setValue:[CIVector vectorWithCGRect:ciImage.extent] forKey:@"inputRectangle"]; CIImage *result = cropFilter.outputImage; return result; } - (CIImage *)bottomLinearGradient { CIFilter *filter = [CIFilter filterWithName:@"CILinearGradient"]; CIImage *ciImage = [[CIImage alloc] initWithImage:_imageView.image]; // 作成する画像のパラメータ設定 [filter setValue:[CIVector vectorWithX:0 Y:(CGFloat) (0.25 * ciImage.extent.size.height)] forKey:@"inputPoint0"]; [filter setValue:[CIColor colorWithRed:0 green:1 blue:0 alpha:0.9] forKey:@"inputColor0"]; [filter setValue:[CIVector vectorWithX:0 Y:(CGFloat) (0.5 * ciImage.extent.size.height)] forKey:@"inputPoint1"]; [filter setValue:[CIColor colorWithRed:0 green:1 blue:0 alpha:0] forKey:@"inputColor1"]; // ここまで // サイズ合わせ CIFilter *cropFilter = [CIFilter filterWithName:@"CICrop"]; [cropFilter setValue:filter.outputImage forKey:@"inputImage"]; [cropFilter setValue:[CIVector vectorWithCGRect:ciImage.extent] forKey:@"inputRectangle"]; CIImage *result = cropFilter.outputImage; return result; } |
2種類のグラデーションを合成する
先ほど作成した二つのグラデーションを CIAdditionCompositing を利用して一つに合成します。ここでは他に特別な処理はせず、 CIAdditionCompositing に任せて合成するだけです。
1 2 3 4 5 6 7 8 9 10 11 |
- (CIImage *)additionCompositing { CIContext *context = [CIContext contextWithOptions:nil]; CIFilter *filter = [CIFilter filterWithName:@"CIAdditionCompositing"]; // 作成する画像のパラメータ設定 [filter setValue:self.topLinearGradient forKey:@"inputImage"]; [filter setValue:self.bottomLinearGradient forKey:@"inputBackgroundImage"]; // ここまで CIImage *result = filter.outputImage; return result; } |
仕上げ
最後に元の画像と、これまで用意したブラー処理された画像とグラデーションを CIBlendWithMask を利用してマスク処理します。 CGImageRef は ARC 環境下でも自分でリリースする必要があるのを忘れないようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
- (void)blendWithMask { CIContext *context = [CIContext contextWithOptions:nil]; CIImage *ciImage = [[CIImage alloc] initWithImage:_imageView.image]; CIFilter *filter = [CIFilter filterWithName:@"CIBlendWithMask"]; // 作成する画像のパラメータ設定 [filter setValue:self.blurred forKey:@"inputImage"]; [filter setValue:ciImage forKey:@"inputBackgroundImage"]; [filter setValue:self.additionCompositing forKey:@"inputMaskImage"]; // ここまで CIImage *result = filter.outputImage; CGRect extent = [result extent]; CGImageRef cgImage = [context createCGImage:result fromRect:extent]; UIImage *resultImage = [[UIImage alloc] initWithCGImage:cgImage]; CGImageRelease(cgImage); _resultImageView.image = resultImage; } |
Core Image には沢山の組み込み Filter が用意されているので、ちょっとした事であれば外部のライブラリに頼らずにささっと作ってしまっても良いかもしれませんね。
明日は t_ishida の番です。
営業→工場で作業→Word&Excel→Java→shellscript→Java→PHP→Python→Objective-C→Swift→PHP→JavaScript→ベトナム 子会社CTO→本社CTO←イマココ