在 iPhone 上处理 GPU(金属)和 CPU (OpenCV) 上的相机馈送数据
Processing camera feed data on GPU (metal) and CPU (OpenCV) on iPhone
我正在 iOS 上以 120 fps 进行实时视频处理,并希望首先在 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) 我注意到有些人通过以下方法使用 MTLBuffer 而不是 MTLTexture:
metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)
(参见:Memory write performance - GPU CPU Shared Memory)
然而 CMSampleBufferRef
和伴随的 CVPixelBufferRef
由 CoreVideo 管理是猜测。
最快的方法是使用由 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。
我正在 iOS 上以 120 fps 进行实时视频处理,并希望首先在 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) 我注意到有些人通过以下方法使用 MTLBuffer 而不是 MTLTexture:
metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)
(参见:Memory write performance - GPU CPU Shared Memory)
然而 CMSampleBufferRef
和伴随的 CVPixelBufferRef
由 CoreVideo 管理是猜测。
最快的方法是使用由 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。