在iPhone上处理GPU(金属)和CPU(OpenCV)上的摄像头输入数据

pzo*_*pzo 16 opencv gpgpu objective-c ios metal

我在iOS上以120 fps进行实时视频处理,并希望首先在GPU上预处理图像(下采样,转换颜色等在CPU上不够快),然后使用OpenCV在CPU上后处理帧.

使用Metal在GPU和CPU之间共享摄像头馈送的最快方法是什么?

换句话说,管道看起来像:

CMSampleBufferRef -> MTLTexture or MTLBuffer -> OpenCV Mat
Run Code Online (Sandbox Code Playgroud)

我正在通过以下方式转换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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的金属着色器完成后,我将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];
Run Code Online (Sandbox Code Playgroud)

一些观察:

1)不幸的是MTLTexture.getBytes看起来很昂贵(将数据从GPU复制到CPU?)并且在我的iphone 5S上需要大约5ms,这在处理~100fps时太多了

2)我注意到有些人用以下方法使用MTLBuffer而不是MTLTexture :( metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared) 参见:内存写入性能 - GPU CPU共享内存)

然而CMSampleBufferRef,伴随CVPixelBufferRef由CoreVideo管理是猜测.

Gar*_*ary 5

最快的方法是使用MTLBuffer支持的MTLTexture; 它是一种特殊的MTLTexture,它与MTLBuffer共享内存.但是,您的C处理(openCV)将运行一两帧,这是不可避免的,因为您需要将命令提交到GPU(编码)并且GPU需要渲染它,如果您使用waitUntilCompleted来确保GPU完成后只是嚼起CPU而浪费.

因此,过程将是:首先创建MTLBuffer然后使用MTLBuffer方法"newTextureWithDescriptor:offset:bytesPerRow:"来创建特殊的MTLTexture.您需要事先创建特殊的MTLTexture(作为实例变量),然后您需要设置一个标准渲染管道(比使用计算着色器更快),它将从CMSampleBufferRef创建MTLTexture并将其传递到您的特殊MTLTexture中,通过你可以缩小规模,并在一次通过必要的颜色转换.然后你将命令缓冲区提交给gpu,在后续的传递中你可以调用[theMTLbuffer contents]来获取指向特殊MTLTexture的字节的指针,以便在openCV中使用.

任何强制停止CPU/GPU行为的技术都将永远不会有效,因为等待的时间有一半,即CPU等待GPU完成,GPU也必须等待下一次编码(当GPU正在工作时)你希望CPU编码下一帧并执行任何openCV工作,而不是等待GPU完成).

此外,当人们通常参考实时处理时,他们通常指的是一些具有实时反馈(视觉)的处理,所有来自4s及以上的现代iOS设备都具有60Hz的屏幕刷新率,因此任何反馈都比这更快没有意义,但如果您需要2帧(120Hz)制作1(60Hz),那么您必须拥有自定义计时器或修改CADisplayLink.