Kekeの日記

エンジニア、読書なんでも

Bye bye GPUImage2, Hello XXX?

読了時間は約7分です。

f:id:bobchan1915:20180907143043p:plain

画像引用: https://developer.apple.com/jp/metal/

Bye bye GPUImage2

iOS12からのGPUImage2非推奨へ

少し語弊があるので説明するとGPUImage2自体が非推奨なわけではなくて、GPUImage2内部で使われているOpen GL(ES)がiOS12から非推奨になるとのことです。

もう一つ追記すると、非推奨なので元のものをすぐに使えなくなるわけではありません。

Apple公式ソース(英語): What’s New in iOS - Apple Developer

Apple公式ソース(日本語): iOSの新機能 - Apple Developer

実際にGPUImage内でも以下のようなIssueが上がっています。

github.com

Why

これはKhronos Groupが開発したVulkanと似たようなシチュエーションです。

実はすでにOpen GLやその組み込み版であるOpen GL(ES)など、オープンソースで、かつ、GPUによるグラフィック処理をすることができるものはありました。

しかし、近年のGPUをはじめとしたハードウェアの進化は凄まじく、OpenGLのような抽象化されすぎて、利便性を追い求めたものがオーバーヘッドを生んでしまい、ハードウェアの限界性能を引き出せなくなっていたという現実がありました。

その解決策として、より低レベルのAPIを提供したグラフィックAPIVulkanが出た歴史があります。

同様にApple MetalもVulkanと競合するフレームワークです。同様の問題があって、オーバーヘッド軽減に関する効果があることから開発されたと推測することができます。

Apple公式ソース(日本語): iOSの新機能 - Apple Developer

Hello XXX?

では、どのような対応をすればいいのでしょうか。

公式のドキュメントではApple製のグラフィック処理フレームワークMetalを使うことが推奨されています

しかし、Metalを使うとなっても直接低レベルのAPIをアプリケーションで呼び出すには開発コストや既存のGPUImage2などのライブラリからの置き換えを考えると無理難題です。

そこで以下のようなGPUImageに置き換わるようなライブラリがいくつか開発されているので検討してみようと思います。

  • CoreImage
  • GPUImage3
  • FlexibleImage
  • MetalPetal
  • MetalAcc

詳細を述べる前に、2018/09/07時点の簡単な比較表で各プロジェクトの比較をしてみます。

なお、ドキュメントの充実度は主観により、⭐️3つで正則化しています。

リポ名 star数 fork数 contributor数 ドキュメントの充実度 ライセンス
Core Image - - - ⭐️⭐️⭐️ -
BradLarson/GPUImage3 1124 42 4 ⭐️⭐️ BSD 3-Clause
kawoou/FlexibleImage 724 33 1 ⭐️⭐️⭐️ MIT
MetalPetal/MetalPetal 238 31 5 ⭐️⭐️⭐️ MIT
wangjwchn/MetalAcc 213 18 1 ⭐️ MIT

また現時点での必要条件は以下の通りです。記載がないものは-を使っています。

リポ名 swift iOS Xcode OSX tvOS
Core Image - 5.0以上 9.0以上 10.11以上 9.0以上
BradLarson/GPUImage3 4.0以上 9.0以上 9.0以上 10.11以上 -
kawoou/FlexibleImage 3.0以上 8.0以上 - 10.10以上 9.0以上
MetalPetal/MetalPetal - - - - -
wangjwchn/MetalAcc - - - - -

Core Image

Core Image | Apple Developer Documentation

ここでは取り上げませんでしたが、Core Graphicの後継ライブラリです。

Appleのビルドインの静止画や動画に任意のフィルターをかけるものです。

特徴としては

  • パフォーマンスがいい
  • ビルドインフィルターを使ってパイプラインを構築することができる、その種類が非常に豊富
  • 直接コードを書いて定義することができるか

使えるビルドインフィルターの種類はCore Image Filter Reference で確認することができます。

基本的にはCore ImageクラスCIImageを使うのでUIImageをキャストする必要があります。逆も然りです。

// UIImage -> CIImage
let ciImage:CIImage? = CIImage(image: uiImage)

// CIImage -> UIImage
let uiImage:UIImage? = UIImage(ciImage: ciImage)

そしてCIFilterを使ってフィルターを定義して

let filter = CIFilter(name: "CIPhotoEffectMono")!

filter.setDefaults()
filter.setValue(ciImage, forKey: kCIInputImageKey)

最後にfilterを施します。

let output = filter.outputImage

非常に簡単な印象です。

GPUImage3

github.com

GPUImageフレームワークの第三世代目です。

GPUImageはObjective-Cで書かれていて、GPUImage2はそれをSwiftに書き直したものです。

そのGPUImage2ではOpen GL(ES)をラッピングしていたましたが、この世代はMetalをラッピングしていて、パフォーマンスの最適化やMetalをベースに作られたフレームワークと高い統合性を示します。

しかしながら、GPUImage2の機能を完全にカバーしているわけではなくて、フルカバー終え次第、外部コントリビュータからのIssueやPull Requestを受け付けるそうです。

2018/09/07時点で未実装の機能は以下の通りです。

  • 静止画をキャプチャしてフィルタリングする
  • 動画から画像をキャプチャする
  • 静止画を処理する
  • 動画を再エンコーディングする

ただし、開発が活発な印象があります。

f:id:bobchan1915:20180907153015p:plain

FlexibleImage

github.com

FlexibleImageはMetalを使って静止画のみに対応しているグラフィックプラットフォームです。

特にドキュメントが非常に簡潔で、わかりやすく、examples以下にいろんなプラットフォームでの使用例があるので、静止画ならとっつきやすい印象があります。

一般的なフィルターには以下のようにフィルターをかけます。

/// Generate Example
let image1 = UIImage
    .circle(
        color: UIColor.blue,
        size: CGSize(width: 100, height: 100)
    )!
    
    .adjust()
    .offset(CGPoint(x: 25, y: 0))
    .margin(UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5))
    .padding(UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15))
    .normal(color: UIColor.white)
    .border(color: UIColor.red, lineWidth: 5, radius: 50)
    .image()!
    
    .adjust()
    .background(color: UIColor.darkGray)
    .image()

特に使いやすいイメージがあります。

MetalPetal

github.com

MetalPetalはGPUImage3と同様にMetalを使ってリアルタイムで動画や静止画を処理するためのフレームワークです。

以下のように設計されています。

  • 使いやすいAPI
  • パフォーマンスがいいこと
  • 拡張性の高さ
  • Swiftyにかける

また、コアなコンポーネントはApple Core Image Frameworkに近いです。

しかし、何がCore Imageと何がちがうのかというと

  • 頂点とフラグメントの関数を完全にカスタマイズできる
  • MRTをサポートしている
  • 一般的にはパフォーマンスがいい

そうです。しかしながら、ベンチマークを取っていないので注意が必要です。

以下のようにフィルターを施します。

処理の対象はMTIImageクラスのでキャストする必要があります。

入力画像としてはCore ImageかCore Graphicクラスである必要があります。

// CIImage -> MTIImage
let imageFromCIImage = MTIImage(ciImage: ciImage)

そしてフィルターを定義します。

let filter = MTISaturationFilter()
filter.saturation = 0
filter.inputImage = inputImage

そして以下のようにして、出力を取得できます。

let outputImage = filter.outputImage

MetalAcc

github.com

こちらもSwiftで書かれたMetalのGPUベースの処理ライブラリで、GPUImageにインスパイアされて作成されています。

しかしながら、ドキュメントが整っておらず、機能も静止画のフィルターに限られているという印象です。

一般的な使用方法は以下の通りです。

let accImage = AccImage()
        
accImage.Input(inimage)
        
accImage.AddProcessor(AccBrightnessFilter())
        
accImage.Processing()
        
let outimage = accImage.Output()

総括

パフォーマンスについてはベンチマークを取っていないので今回は比較できません。

あとは比較できるとすると機能面、設計ではないでしょうか。

機能については、以下の表に比較します。

なお、ライブラリやフレームワークそれ単体で実現できない機能も❌としています。 (たとえば動画に対応していなくても、スクショなど静止画にするという対応を前処理として取るなど)

また、GPUImage3については、GPUImage2で実装されていた項目について実装されるので🚧をつけています。

Blur

FlexibleImageは単にBlurしか書かれていないので❓をつけています。

機能 Core Image GPUImage3 FlexibleImage MetalPetal MetalAcc
静止画に対応
動画に対応  
BoxBlur   🚧
DiscBlur  
GaussianBlur   🚧
SingleComponentGaussianBlur 🚧
iOSBlur 🚧
MaskedVariableBlur
MedianFilter 🚧
MotionBlur 🚧
NoiseReduction
ZoomBlur 🚧
Lens(Hexagonal Bokeh) Blur

まとめ

iOS12からOpen GL(ES)を使ったライブラリは非推奨となるのでWarningが出るようになるので組織としても技術的負債を抱えるのは辛いと思います。

この記事が少しでも参考になれば嬉しいです。

今度、ベンチマークを取れればいいなと思います。

読んでいただきありがとうございました!

参考文献

Core Imageについて qiita.com

Core ImageとMetalについて qiita.com