web-dev-qa-db-ja.com

iPhoneのGPU(メタル)とCPU(OpenCV)でカメラフィードデータを処理する

IOSで120fpsでリアルタイムのビデオ処理を行っており、最初にGPUで画像を前処理し(ダウンサンプリング、CPUで十分な速度ではない色の変換など)、後でOpenCVを使用してCPUでフレームを後処理したいと考えています。

Metalを使用してGPUとCPUの間でカメラフィードを共有する最速の方法は何ですか?

言い換えると、パイプは次のようになります。

_CMSampleBufferRef -> MTLTexture or MTLBuffer -> OpenCV Mat
_

CMSampleBufferRef-> MTLTextureを次のように変換しています

_CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

// textureRGBA
{
    size_t width = CVPixelBufferGetWidth(pixelBuffer);
    size_t height = CVPixelBufferGetHeight(pixelBuffer);
    MTLPixelFormat pixelFormat = MTLPixelFormatBGRA8Unorm;

    CVMetalTextureRef texture = NULL;
    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, _textureCache, pixelBuffer, NULL, pixelFormat, width, height, 0, &texture);
    if(status == kCVReturnSuccess) {
        textureBGRA = CVMetalTextureGetTexture(texture);
        CFRelease(texture);
    }
}
_

金属シェーダーが完成したら、MTLTextureをOpenCVに変換します

_cv::Mat image;
...
CGSize imageSize = CGSizeMake(drawable.texture.width, drawable.texture.height);
int imageByteCount = int(imageSize.width * imageSize.height * 4);
int mbytesPerRow = 4 * int(imageSize.width);

MTLRegion region = MTLRegionMake2D(0, 0, int(imageSize.width), int(imageSize.height));
CGSize resSize = CGSizeMake(drawable.texture.width, drawable.texture.height);
[drawable.texture getBytes:image.data bytesPerRow:mbytesPerRow  fromRegion:region mipmapLevel:0];
_

いくつかの観察:

1)残念ながら、_MTLTexture.getBytes_は高価に見え(GPUからCPUにデータをコピーしますか?)、iPhone 5Sでは約5ミリ秒かかります。これは、約100fpsで処理する場合は長すぎます。

2)次のメソッドでMTLTextureの代わりにMTLBufferを使用している人がいることに気付きました:metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)(参照: メモリ書き込みパフォーマンス-GPU CPU共有メモリ

ただし、CMSampleBufferRefとそれに付随するCVPixelBufferRefはCoreVideoによって管理されていると推測されます。

16
pzo

これを行う最も速い方法は、MTLBufferに裏打ちされたMTLTextureを使用することです。これは、MTLBufferとメモリを共有する特別な種類のMTLTextureです。ただし、C処理(openCV)は1〜2フレーム遅れて実行されます。これは、コマンドをGPU(エンコード)に送信する必要があり、waitUntilCompletedを使用してGPUを確認する場合は、GPUがレンダリングする必要があるため避けられません。 CPUをかみ砕くだけで無駄な仕上がりになります。

したがって、プロセスは次のようになります。最初にMTLBufferを作成し、次にMTLBufferメソッド "newTextureWithDescriptor:offset:bytesPerRow:"を使用して特別なMTLTextureを作成します。特別なMTLTextureを(インスタンス変数として)事前に作成する必要があります。次に、CMSampleBufferRefから作成されたMTLTextureを取得し、これを特別なMTLTextureに渡す標準のレンダリングパイプラインを(計算シェーダーを使用するよりも高速に)セットアップする必要があります。そのパスでは、1回のパスでダウンスケールし、必要に応じて色変換を行うことができます。次に、コマンドバッファーをGPUに送信します。後続のパスで、[theMTLbufferの内容]を呼び出すだけで、openCVで使用するために特別なMTLTextureをバックアップするバイトへのポインターを取得できます。

CPU/GPUの動作を強制的に停止させる手法は、待機に半分の時間が費やされるため、効率的ではありません。つまり、CPUはGPUが終了するのを待機し、GPUは次のエンコーディングも待機する必要があります(GPUが機能している場合)。 GPUが終了するのを待つのではなく、CPUが次のフレームをエンコードしてopenCV作業を実行するようにします)。

また、人々が通常リアルタイム処理を参照する場合、通常はリア​​ルタイムフィードバック(ビジュアル)を使用した処理を参照しますが、4以降のすべての最新のiOSデバイスは60Hzの画面リフレッシュレートを備えているため、フィードバックはそれよりも速く表示されますは無意味ですが、1(60Hz)にするために2フレーム(120Hz)が必要な場合は、カスタムタイマーを用意するか、CADisplayLinkを変更する必要があります。

5
Gary