2017-09-28 43 views
0

我是一名本科生,我现在使用CoreML框架在iPhone上制作一些视频HumanSeg应用程序,但正如标题所示,我有一个huuuuuge问题。CVPixelBuffer使用UIImage.draw()写作太慢

我有一个UIImage,我必须调整它的大小并将它填充到一个CVPixelBuffer中,以提供MobileNet模型,但是这样的过程只是太慢,耗费大约30ms,这是不可接受的。在我的代码中,方法UIImage.draw(in:CGRect(x:Int,y:Int,width:Int,height:Int))是TOO SLOW,并且花了我20+ ms,这是主要问题。

我的代码如下所示:

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用于填充。

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

有没有什么办法可以让这个过程更快并且保存这个项目?我只是不想放弃我的两周工作。

+1

我不确定为什么使用'image.draw()'的行比'pad.draw()'[可能是因为调整大小]慢得多,但是这里有两个问题想到:1)为什么你需要填充你的输入图像? 2)如果你需要速度,为什么你的输入数据在UIImage中? –

+0

噢,我会尝试的一件事是使'image.draw()'使用0,0和全宽和高,并看看是否有更快。 –

+0

谢谢。让我先回答你的问题。 1)我从我的实验室获得训练有素的MoblieNet模型,我的老年人在训练此模型时将黑色填充图像添加到图像中,因此我还需要这样做才能使网络正确运行。 2)如果我需要从iPhone相机或相册中选择图像,我不确定我可以使用什么。我也尝试过使用opencv-swift,但是出现了一些错误,最后我又回到了UIImage。 3)你的意思是使用其他图像格式可能会更快地生成CVPixelBuffer? – AXIHIXA

回答

1

我已经处理了CVPixelBuffer s,而且我还没有使用过CoreML

当我确实CVPixelBuffer一起工作时,我发现通过在目标尺寸处创建单个像素缓冲区并保持它达到最佳性能。我从相机中取像素,将它们传递给OpenGL作为纹理,操作它们,并将输出映射到相同的CVPixelBuffer。我能够为所有这些使用相同的内存结构。我建议采取这种方法。

+0

是的,但即使我创建了一个CVPixelBuffer并在整个项目中使用它,但将数据写入它仍然太慢。事实上,创建一个我只需要9毫秒,但写入UIImage数据(使用draw()方法),我需要20 +毫秒,这是我认为的主要问题。有没有解决方法? – AXIHIXA

+1

使用OpenGL或金属并使用像素缓冲区集作为OpenGL“上下文”的输出来绘制纹理。自从我从事这些工作已经很久了,我甚至都不记得它的条款。 –

+0

在我的应用程序中,我可以从相机拍摄相框,将它们映射到纹理,在纹理上执行“网格扭曲”,将输出映射回像素缓冲区,并以全屏60 fps渲染到屏幕。 –