使用 UIImage.draw() 写入 CVPixelBuffer 太慢了

Posted

技术标签:

【中文标题】使用 UIImage.draw() 写入 CVPixelBuffer 太慢了【英文标题】:CVPixelBuffer Writing using UIImage.draw() is TOO SLOW 【发布时间】:2018-03-09 15:22:50 【问题描述】:

我是一名本科生,我现在正在使用 CoreML 框架在 iPhone 上制作一些视频 HumanSeg 应用程序,但正如标题所示,我遇到了一个 huuuuuge 问题。

我有一个 UIImage,我必须调整它的大小和填充它,并将它绘制到一个 CVPixelBuffer 中以提供给 MobileNet 模型,但是这样的过程太慢了,大约需要 30 毫秒,这是不可接受的。

具体来说,在我的代码中,方法 UIImage.draw(in: CGRect(x: Int, y: Int, width: Int, height: Int)) 太慢了,花了我 20 多毫秒,也就是主要问题。

我的代码如下:

func dealRawImage(image : UIImage, dstshape : [Int], pad : UIImage) -> CVPixelBuffer?

    // decide whether to shrink in height or width
    let height = image.size.height
    let width = image.size.width
    let ratio = width / height
    let dst_width = Int(min(CGFloat(dstshape[1]) * ratio, CGFloat(dstshape[0])))
    let dst_height = Int(min(CGFloat(dstshape[0]) / ratio, CGFloat(dstshape[1])))
    let origin = [Int((dstshape[0] - dst_height) / 2), Int((dstshape[1] - dst_width) / 2)]

    // init a pixelBuffer to store the resized & padded image
    var pixelBuffer: CVPixelBuffer?
    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue,
                 kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue]
    CVPixelBufferCreate(kCFAllocatorDefault,
                        dstshape[1],
                        dstshape[0],
                        kCVPixelFormatType_32ARGB,
                        attrs as CFDictionary,
                        &pixelBuffer)

    // get the pointer of this pixelBuffer
    CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
    let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer!)

    // init a context that contains this pixelBuffer to draw in
    let context = CGContext(data: pixelData,
                            width: dstshape[1],
                            height: dstshape[0],
                            bitsPerComponent: 8,
                            bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer!),
                            space: CGColorSpaceCreateDeviceRGB(),
                            bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)!

    // push context
    UIGraphicsPushContext(context)
    context.translateBy(x: 0, y: CGFloat(dstshape[0]))
    context.scaleBy(x: 1, y: -1)

    pad.draw(in:CGRect(x: 0, y: 0, width: dstshape[1], height: dstshape[0]))
    // THIS SINGLE FUNCTION COSTS ME 20+ ms AND IS THE MAJOR ISSUE !
    image.draw(in: CGRect(x: origin[1], y: origin[0], width: dst_width, height: dst_height))

    UIGraphicsPopContext()

    // unlock
    CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))

    return pixelBuffer

我只是这样调用这个函数:

let input = dealRawImage(image: raw_input_image, dstshape: [224, 224], pad: black_image)

其中 raw_input_image 是我从内存中读取的 UIImage,dstshape 是我想将此图像调整为的形状,black_image 是用于填充的全黑 UIImage。

我在这个网站上搜索过,但没有发现熟悉的问题。

有什么方法可以加快这个过程并保存这个项目吗?我只是不想放弃我为期两周的工作。

【问题讨论】:

我不确定为什么image.draw() 的行比pad.draw() 慢得多[可能是因为调整大小] 但这里想到了两个问题:1)你为什么需要在您的输入图像上进行填充? 2)如果您需要速度,为什么您的输入数据在 UIImage 中? 哦,我会尝试的一件事是让 image.draw() 使用 0、0 以及完整的宽度和高度,看看是否更快。 谢谢。让我先回答你的问题。 1)我从我的实验室得到了训练好的MoblieNet模型,我的前辈在训练这个模型时对图像做了黑色填充,所以我也需要这样做才能让这个网络正确运行。 2) 如果我需要从 iPhone 相机或相册中挑选图像,我不确定我可以使用什么。我也尝试使用opencv-swift,但是出了点问题,最后我回到了UIImage。 3)你的意思是使用其他图像格式可能会更快地生成一个CVPixelBuffer? 那么如果使用 (0, 0) 而不是 (1, 1) 和全宽和全高来绘制图像会更快吗?如果是的话,那么我会这样做而忘记黑色边框。 (或者如果你真的真的必须有一个黑色边框,只需将它画在图像的顶部。) 如果您正在处理视频,您应该能够从一开始就对帧使用 GPU 加速的像素缓冲区。无论中间步骤如何让您进入使用 UIImage 进行基于 CPU 的绘图都是瓶颈,而尝试从 UIImage 返回到 GPU 支持的像素缓冲区只会使您的原始问题更加复杂。那么你是如何使用 UIImage 的呢? 【参考方案1】:

处理CVPixelBuffers已经有一段时间了,我还没有使用CoreML

当我确实使用CVPixelBuffers 时,我发现通过以目标大小创建单个像素缓冲区并将其保留在周围,我获得了最佳性能。我从相机中获取像素,将它们作为纹理传递给 OpenGL,对其进行操作,并将输出映射到相同的 CVPixelBuffer 中。我能够为所有这些使用相同的内存结构。我建议采用这种方法。

【讨论】:

是的,但即使我创建了一个 CVPixelBuffer 并在整个项目中使用它,向其中写入数据仍然太慢。事实上,创建一个我只需要 9 毫秒,但要写入 UIImage 数据(使用 draw() 方法)我需要 20+ 毫秒,我认为这是主要问题。有什么解决办法吗? 使用 OpenGL 或金属绘制纹理,并将像素缓冲区设置为 OpenGL“上下文”的输出。我已经很久没有研究这些东西了,我什至不记得它的条款了。 在我的应用程序中,我能够从相机中获取帧,将它们映射到纹理,在纹理上进行“网格变形”,将输出映射回像素缓冲区并渲染到屏幕以 60 fps 的速度运行。

以上是关于使用 UIImage.draw() 写入 CVPixelBuffer 太慢了的主要内容,如果未能解决你的问题,请参考以下文章

php怎么把数据写入文本文件

使用 POI 写入现有的 excel 文件

我有 json 文件,对于每个用户,我正在使用 file.write() 写入文件,但它只使用 javascript 写入最后一个用户 [关闭]

使用指针写入内存[关闭]

delphi 如何写入Excel

使用 pandas 和 XlsxWriter 写入现有的 .xlsm