Core Imageを使うと、簡単かつ高速に画像ぼかすことができます。
(↑はgif化しているので見た目がいまひとつですが...)
ぼかし自体は CIGaussianBlur ひとつでできるのですが、以下の点に注意しておくことでより高いクオリティの結果を得ることができます。
- ぼかし後の画像サイズは、入力画像サイズと異なる
- なにもしないと、画像のエッジ付近がやや暗くなってしまう
CIImage#extent について
CIImage#extent は簡単に言うと、その画像のサイズを持つ出力矩形領域です。
例えば解像度480x640の画像から作られた CIImage の extent は (x: 0, y: 0, w: 480, h: 640) となります。
しかしこれに、半径 20 の CIGaussianBlur を適用すると、 CIImage#extent は (x: -60, y: -60, w: 600, h: 760) となり、入力画像よりも大きな画像が出力されていることがわかります。
これをImage ViewなどにAspect Fit表示すると、元画像より相対的に小さく表示されてしまうでしょう。
入力画像と同じ大きさで表示したければ、 CIImage#cropped(to:) などで入力画像の extent で切り取る必要があります。
ぼかし画像のエッジ付近が暗くなってしまう理由
ぼかしフィルタは一般的に、対象ピクセルを中心とした半径r内にあるピクセルの値(色)の加重平均を求めて生成します。
しかし画像のエッジ付近については、サンプリングする範囲が画像の外に及んでしまうため、その部分の値を「黒」とみなして計算した結果、暗い色になってしまうことがあります。
この問題の対処方法のひとつとして CIAffineClamp を利用する方法があります。
↑のリファレレンスにもどんな結果が得られるか記載されていますが、 画像の外のピクセル値を、最寄りの四辺のピクセル値と同じとみなすようCore Imageに指示することができる のです。
CropとClampedToExtentを適用したぼかしフィルタ
以上を踏まえたCore Imageによるぼかしフィルタは以下のようになります。
// inputImage: CIImage, radius: CGFloat
let outputImage = inputImage
.clampedToExtent() // CIAffineClamp
.applyingFilter(
"CIGaussianBlur",
parameters: [kCIInputRadiusKey: radius]
)
.cropped(to: inputImage.extent)
- 左: clampedToExtentあり
- 右: clampedToExtentなし

