Help us understand the problem. What is going on with this article?

CIKernel を使用してCore Imageのカスタムフィルタをつくる

More than 1 year has passed since last update.

Core Image の CIFilter を使うといろいろなフィルタ処理(画像処理/画像加工)ができますが、iOS 8で CIKernel というクラスが追加され、そのフィルタ(CIFilter)を自作できるようになりました。

以下、CIKernel のフィルタ処理を記述し、それを使ってオリジナルの CIFilter を定義し、画像に適用するまでの手順です。

(更新履歴)本記事は2014年のiOS 8がまだベータ版だった頃に書いたもので、Objective-Cコードは古くなっている可能性があります。Swiftコードは2017年12月に追記したもので、Swift 4対応です。

CIKernel をつくる

カーネルを書く

カーネルはGLSLのサブセットで書きます。

kernel vec4 swapRedAndGreenAmount ( __sample s, float amount )
                  { return mix(s.rgba, s.grba, amount); }

CIKernelオブジェクトを生成する

上記で書いたシェーダを 文字列として CIKernel の初期化メソッドに渡します

objc
CIKernel *kernel = [CIKernel kernelWithString:kernelStr];
swift
let kernel = CIColorKernel(source: kernelStr)!

CIFilter サブクラスをつくる

上記で生成した CIKernel オブジェクトを使用して、CIFilter サブクラスをつくります。

ヘッダには下記のようにプロパティを定義します。

MyFilter.m
@interface MyFilter : CIFilter

@property (nonatomic, strong) CIImage *inputImage;
@property (nonatomic, copy) NSNumber *inputAmount;

@end

実装ファイル側は長くなるので省略しますが、CIKernelを適用して出力する部分はこんな感じす。

MyFilter.m
- (CIImage *)outputImage
{
    return [[self myKernel] applyWithExtent:self.inputImage.extent
                                  arguments:@[self.inputImage, self.inputAmount]];
}

Swift 4で書くと、こんな感じになります。

MyFilter.swift
class MyFilter: CIFilter {

    private let kernelStr =
    "kernel vec4 swapRedAndGreenAmount ( __sample s, float amount ) { return mix(s.rgba, s.grba, amount); }"

    private let kernel: CIColorKernel

    var inputImage: CIImage?
    var inputAmount: Float = 1.0

    override init() {
        kernel = CIColorKernel(source: kernelStr)!
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func outputImage() -> CIImage? {
        guard let inputImage = inputImage else {return nil}
        return kernel.apply(extent: inputImage.extent, arguments: [inputImage, inputAmount])
    }
}

画像に適用する

使い方は 普通の CIFilter (ビルトインフィルタ) と同じ

objc
NSDictionary *params = @{kCIInputImageKey: ciImage,
                         @"inputAmount": @(1.0),
                         };
CIFilter *filter = [CIFilter filterWithName:@"MyFilter"
                        withInputParameters:params];

Swift 4の場合:

swift
let filter = MyFilter()
filter.inputImage = inputImage
let outputImage = filter.outputImage()

実行結果

results.jpg

(左:元画像 右:カスタムフィルタ適用後の画像)

参考資料

  • Developing Core Image Filter for iOS (WWDC 2014 Session 515)
  • CIKernel Class Reference
  • Core Image Kernel Language Reference

関連記事

ちょっと古いものもありますが、Core Image、画像処理関連の記事は他にもいろいろと書いているので、よろしければご参照ください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした