2017-09-28 108 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渲染到屏幕。 –