[Swift]iPhoneのカメラに映ってる画像にカスタムフィルタかけてみる(後編)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

今回は前編でカメラ表示した画像にCIKernelを使用してカスタムフィルターをかけてみたいと思います。

CIKernelとはiOS8から追加された、フィルタ(CIFilter)を自作することが出来るクラスです。 ですが、フィルターの実装部分(カーネル)をOpenGLのシェーディング言語(GLSL)で書く必要があるため未経験者には敷居が高いです。

そこで今回は自分で書かずに、 Writing Kernels|MacDeveloperLibrary から、routine that places RGB values in the GBR channelsを使いたいと思います。

実装

まず、カスタムフィルター用のCustomFilterクラスを作成します。

import CoreImage
class CustomFilter: CIFilter {
    
    var kernel: CIKernel
    var inputImage: CIImage?
    
    override init() {
        super.init()
        self.kernel = createKernel()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.kernel = createKernel()
    }
    
    func outputImage() -> CIImage? {
        if let inputImage = self.inputImage {
            let dod = inputImage.extent()
            let args = [inputImage as AnyObject]
            let dod = inputImage.extent().rectByInsetting(dx: -1, dy: -1)
            return kernel.applyWithExtent(dod, roiCallback: {
                   (index, rect) in
               return rect.rectByInsetting(dx: -1, dy: -1)
               }, arguments: args)
        }
        return nil
    }
    
    private func createKernel() -> CIKernel {
        let kernelString =
        "kernel vec4 RGB_to_GBR(sampler source_image)\n" +
        "{\n" +
            "vec4 originalColor, twistedColor;\n" +
            "originalColor = sample(source_image, samplerCoord(source_image));\n" +
            "twistedColor.r = originalColor.g;\n" +
            "twistedColor.g = originalColor.b;\n" +
            "twistedColor.b = originalColor.r ;\n" +
            "twistedColor.a = originalColor.a;\n" +
            "return twistedColor;\n" +
        "}\n"

        return CIKernel(string: kernelString)
    }
    
}

override init()でカーネルを生成します。

private func createKernel() -> CIKernelメソッド内で出力したいシェーディング言語を書きます。 別のフィルターにしたい時は上記コードの33行目のkernelStringの内容をを変更して下さい。

前編のコードの修正

前回作成した、ViewController.swift内に下記の編集を加えます。

let filter = CustomFilter()を追加します。

let filter = CustomFilter()

func captureOutputメソッド内の下記コードを書き換えます。

let outputImage = CIImage(CVPixelBuffer: pixelBuffer, options: nil)

を下記コードに変更します↓

filter.inputImage = CIImage(CVPixelBuffer: pixelBuffer, options: nil)
let outputImage = filter.outputImage

実行結果

左がフィルターを通して写してみた画面をキャプチャしたもので、右が普通に写真で撮った画像になります。

フィルターあり フィルターなし

カボチャが紫色になりました。(毒々しくなりました)

最後に

今回、CIKernelを使用してカスタムフィルターをかけてみましたが、CIFilterには標準で色々なフィルタが存在するため、また、GLSLで書かないといけないため敷居が高く通常ではあまり使う機会が無いのかなと思います。 標準に無いフィルターを使用したい場合はカスタムフィルタ、あるものは標準を使うといった使い分けが必要だと思いました。

参考