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

时间:2016-06-05 06:57:04

标签: ios objective-c opencv gpgpu metal

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

使用Metal在GPU和CPU之间共享相机Feed的最快方法是什么?

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

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上大约需要5ms,这在处理~100fps时太多了

2)我注意到有些人使用MTLBuffer代替MTLTexture,方法如下:     metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared) (见:Memory write performance - GPU CPU Shared Memory

然而,CMSampleBufferRef和随附的CVPixelBufferRef由CoreVideo管理。

1 个答案:

答案 0 :(得分: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。