CIAffineClamp
ってなぁに
CIAffineClamp
によるフィルターを画像に適用すると,画像の縁(上下左右1px)をそれぞれの方向に無限大に引き伸ばした仮想上の画像データを生成することができます.これにより,ガウシアン・フィルタや一部のカラーフィルタのように,あるピクセルの周囲のピクセルをサンプリングしてそのピクセルの色を決定するようなフィルタ(下図)を扱う場合に画像縁の欠けている部分を補うことができます.
↓こういうやつ
↑デフォルトでは,はみ出した参照できない部分は透明色で補完されます.
直接CIGaussianBlur
を使った場合
左)オリジナル,右)ぼかし加工結果
画像の縁の色が薄くなってぼかしが弱いことがわかります.
ソース
// オリジナル画像
let nsImage = NSImage(imageLiteralResourceName: "Test")
guard let imageData = nsImage.tiffRepresentation, let ciImage = CIImage(data: imageData) else { return }
// ガウシアン・フィルタ
guard let blurFilter = CIFilter(name: "CIGaussianBlur") else { return }
blurFilter.setValue(ciImage, forKey: kCIInputImageKey)
blurFilter.setValue(5, forKey: kCIInputRadiusKey)
guard let blurImage = blurFilter.outputImage else { return }
let context = CIContext(options: nil)
guard let resultImage = context.createCGImage(blurImage, from: CGRect(origin: .zero, size: nsImage.size)) else { return }
resultView.image = NSImage(cgImage: resultImage, size: nsImage.size)
CIAffineClamp
を介して間接的にCIGaussianBlur
を使った場合
左)オリジナル,中央)CIAffineClamp時点での結果,右)ぼかし加工結果
画像の縁までしっかりぼかしがかかっていることがわかります.
ソース
// オリジナル画像
let nsImage = NSImage(imageLiteralResourceName: "Test")
guard let imageData = nsImage.tiffRepresentation, let ciImage = CIImage(data: imageData) else { return }
// CIAffineClamp
guard let clampFilter = CIFilter(name: "CIAffineClamp") else { return }
clampFilter.setValue(ciImage, forKey: kCIInputImageKey)
clampFilter.setValue(CGAffineTransform(scaleX: 1.0, y: 1.0), forKey: kCIInputTransformKey)
guard let clampImage = clampFilter.outputImage else { return }
let context = CIContext(options: nil)
let rect = CGRect(origin: CGPoint(x: -25, y: -25), size: CGSize(width: nsImage.size.width + 50, height: nsImage.size.height + 50))
guard let cgImage = context.createCGImage(clampImage, from: rect) else { return }
imageView.image = NSImage(cgImage: cgImage, size: rect.size)
// ガウシアン・フィルタ
guard let blurFilter = CIFilter(name: "CIGaussianBlur") else { return }
blurFilter.setValue(clampImage, forKey: kCIInputImageKey)
blurFilter.setValue(5, forKey: kCIInputRadiusKey)
guard let blurImage = blurFilter.outputImage else { return }
guard let resultImage = context.createCGImage(blurImage, from: CGRect(origin: .zero, size: nsImage.size)) else { return }
resultView.image = NSImage(cgImage: resultImage, size: nsImage.size)
所感
CIFilterはうまく組み合わせることで,効率的に高速な画像処理が行えるため,ガンガン使っていきましょう.