読了時間は約7分です。
画像引用: 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が上がっています。
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
GPUImageフレームワークの第三世代目です。
GPUImageはObjective-Cで書かれていて、GPUImage2はそれをSwiftに書き直したものです。
そのGPUImage2ではOpen GL(ES)をラッピングしていたましたが、この世代はMetalをラッピングしていて、パフォーマンスの最適化やMetalをベースに作られたフレームワークと高い統合性を示します。
しかしながら、GPUImage2の機能を完全にカバーしているわけではなくて、フルカバー終え次第、外部コントリビュータからのIssueやPull Requestを受け付けるそうです。
2018/09/07時点で未実装の機能は以下の通りです。
- 静止画をキャプチャしてフィルタリングする
- 動画から画像をキャプチャする
- 静止画を処理する
- 動画を再エンコーディングする
ただし、開発が活発な印象があります。
FlexibleImage
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
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
こちらも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