ARKitのcapturedImageをmediapipeで利用する時など、CVPixelBufferのpixelFormatを変換する必要があることがあります。
以前の記事ではvImageを利用しましたが、vImageはCPUでの計算に最適化されており映像処理などには向かないものでした。
https://qiita.com/noppefoxwolf/items/b12d56e052664a21d8b6
そこで、GPUを利用してYCbCrをBGRAに変換するライブラリを作りました。Xcode12のSwiftPMで簡単に導入することができます。
https://github.com/noppefoxwolf/BlueDress
使い方
簡単に使うことができます。
import BlueDress
let converter = YCbCrImageBufferConverter()
let bgraBuffer = try converter.convertToBGRA(imageBuffer: imageBuffer)
変換の流れ
実際は変換ではなく、新規に新しいBGRAのPixelBufferを作ってそこにYCbCrを焼いています。
YCbCrのPixelBufferは、内部に2枚の画像を持っておりそれぞれ
- Y: 輝度
- CbCr 青と赤の色差
をサイズ違いでもっています。
YCbCrは以下の計算式で変換することが出来るので、Metalシェーダを使ってこの計算を行うというわけです。
R = 1.164(Y-16) + 1.596(Cr-128)
G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
B = 1.164(Y-16) + 2.018(Cb-128)
実装で詰まりがちなところ
各PlaneのPixelFormat
YCbCrから2枚のPlaneを取り出す際に、最初はそれぞれをBGRAとして取り出していたのですがこれは間違いでそれぞれ
- Y: r8Unorm
- CbCr: rg8Unorm
で取り出す必要があります。
よく考えれば当然なのですが、ARKitのMetalテンプレートを読んで気がつきました。
CVPixelBufferとMTLTextureの関係
MTLTextureはあくまでCVPixelBufferの参照を持つ概念なので、MTLTextureに対して行った描画処理は特に取り出す必要はなく、MTLTextureのソースとして渡したCVPixelBufferに適用されます。
MTLTextureが-6660エラーで作れない
最終描画先の空のMTLTextureを作りたくて、そのソースとなる空のCVPixelBufferを自前で作っていたのですがどうもエラーでMTLTextureが作れませんでした。
MTLTextureを作る場合は、CVPixelBufferを生成する時にkCVPixelBufferMetalCompatibilityKey:true
を与えるのを忘れないようにしましょう。