在 iPhone 上处理 GPU(金属)和 CPU(OpenCV)上的摄像头馈送数据
Posted
技术标签:
【中文标题】在 iPhone 上处理 GPU(金属)和 CPU(OpenCV)上的摄像头馈送数据【英文标题】:Processing camera feed data on GPU (metal) and CPU (OpenCV) on iPhone 【发布时间】:2016-10-04 23:44:50 【问题描述】:我正在 ios 上以 120 fps 的速度进行实时视频处理,并希望首先在 GPU 上预处理图像(下采样、转换颜色等,在 CPU 上不够快),然后在 CPU 上使用 OpenCV 对帧进行后处理。
使用 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 上大约需要 5ms,这在以 ~100fps 处理时太多了
2) 我注意到有些人使用 MTLBuffer 而不是 MTLTexture 使用以下方法:
metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)
(见:Memory write performance - GPU CPU Shared Memory)
但是 CMSampleBufferRef
和随附的 CVPixelBufferRef
由 CoreVideo 管理是猜测。
【问题讨论】:
并非所有分辨率都支持 GPU。我知道,这不是你的答案。我只是提供有关 GPU 的信息。 你试过GPUImagegithub.com/BradLarson/GPUImage 我试过 GPUImage,但最大的瓶颈是将数据从 GPU 传输到 CPU。 GPUImage 使用 OpenGL 下的好和相反的 Metal API 不能有共享内存。 我也会想办法在 GPU 上进行 OpenCV 工作。 OpenCV 的某些部分在 MetalPerformanceShaders.framework 中可用,主要是图像处理的东西。 iOS 10 增加了卷积神经网络。如果您需要其他运算符,请向 Apple 提交功能请求错误。 我正在尝试将一个简单的晕影滤镜应用到使用金属的实时摄像头馈送中。结果非常缓慢且滞后,如果您能告诉我缺少什么,请检查一下:***.com/q/53898780/1364053 【参考方案1】:最快的方法是使用由 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 完成)。
此外,当人们通常提到实时处理时,他们通常指的是一些具有实时反馈(视觉)的处理,所有现代 iOS 设备从 4s 及以上都具有 60Hz 的屏幕刷新率,所以任何反馈都呈现比这更快是没有意义的,但如果你需要 2 帧(120Hz)来制作 1 帧(60Hz),那么你必须有一个自定义计时器或修改 CADisplayLink。
【讨论】:
很好的提示,GPU 渲染(纹理着色器)可能被限制在 60fps - 这是有道理的。我实际上需要尽可能小的延迟——我有自定义的自然用户界面,它使用声音作为反馈给用户,而不是渲染显示。我不介意 CPU 等待 GPU 完成 - 我只想将一些预处理移至 GPU(调整对比度、过滤颜色调整大小)考虑到我的计算预算紧张,它们在 GPU 上非常快而在 CPU 上非常慢(NEON 事件) .无法将其他部分(似乎不可能?)移动到 GPU,例如轮廓分析。似乎 GPU 对我来说是一条死胡同。 我不认为这是一个死胡同,至少设置一个以 60Hz 运行的管道相对容易,您可以在其中对每帧进行编码和进行轮廓分析,同时使用 GPU进行必要的预处理,一旦你开始并在 60Hz 下进行优化(Metal Frame Debugger 和 Metal System Trace 是非常有用的工具),尝试将其提升到 120Hz。我从来没有尝试过这么快使用计时器或 CADisplayLink,所以我无法帮助你,但请查看:***.com/questions/23885638/…。 另外,我对轮廓分析不是很熟悉,但是使用 Metal 的计算功能你可能能够执行它,因为对比度调整或调整大小不会影响 GPU(如果过滤器很复杂,使用 LUT)。即使使用标准的顶点和片段着色器,通常也有一些技巧可以在 GPU 上执行非友好的 GPU 操作,我使用 Metal 实现了连接组件标记算法,它与小图像的 C 版本相差不远跨度> 我正在尝试使用金属将一个简单的晕影滤镜应用于实时摄像机源。结果非常缓慢且滞后,如果您能告诉我缺少什么,请检查一下:***.com/questions/53898780/…以上是关于在 iPhone 上处理 GPU(金属)和 CPU(OpenCV)上的摄像头馈送数据的主要内容,如果未能解决你的问题,请参考以下文章