2016-12-14 79 views
1

我使用Camera 2 API將JPEG圖像保存在磁盤上。我目前在我的Nexus 5X上有3-4 fps,我想將其提高到20-30。可能嗎?攝像頭2,增加FPS

將圖像格式更改爲YUV我設法生成30 fps。是否有可能以這個幀率保存它們,還是應該放棄並以我的3-4 fps生活?

顯然我可以共享代碼,如果需要的話,但如果大家都認爲這是不可能的,我會放棄。使用NDK(例如libjpeg)是一個選項(但顯然我寧願避免它)。

由於

編輯:這裏是如何我轉換YUV android.media.Image到單個字節[]:

private byte[] toByteArray(Image image, File destination) { 

    ByteBuffer buffer0 = image.getPlanes()[0].getBuffer(); 
    ByteBuffer buffer2 = image.getPlanes()[2].getBuffer(); 
    int buffer0_size = buffer0.remaining(); 
    int buffer2_size = buffer2.remaining(); 

    byte[] bytes = new byte[buffer0_size + buffer2_size]; 

    buffer0.get(bytes, 0, buffer0_size); 
    buffer2.get(bytes, buffer0_size, buffer2_size); 

    return bytes; 
} 

編輯2:另一種方法,我發現了YUV圖像轉換成一個字節[]:在移動電話上

private byte[] toByteArray(Image image, File destination) { 

    Image.Plane yPlane = image.getPlanes()[0]; 
    Image.Plane uPlane = image.getPlanes()[1]; 
    Image.Plane vPlane = image.getPlanes()[2]; 

    int ySize = yPlane.getBuffer().remaining(); 

    // be aware that this size does not include the padding at the end, if there is any 
    // (e.g. if pixel stride is 2 the size is ySize/2 - 1) 
    int uSize = uPlane.getBuffer().remaining(); 
    int vSize = vPlane.getBuffer().remaining(); 

    byte[] data = new byte[ySize + (ySize/2)]; 

    yPlane.getBuffer().get(data, 0, ySize); 

    ByteBuffer ub = uPlane.getBuffer(); 
    ByteBuffer vb = vPlane.getBuffer(); 

    int uvPixelStride = uPlane.getPixelStride(); //stride guaranteed to be the same for u and v planes 

    if (uvPixelStride == 1) { 

     uPlane.getBuffer().get(data, ySize, uSize); 
     vPlane.getBuffer().get(data, ySize + uSize, vSize); 
    } 
    else { 

     // if pixel stride is 2 there is padding between each pixel 
     // converting it to NV21 by filling the gaps of the v plane with the u values 
     vb.get(data, ySize, vSize); 
     for (int i = 0; i < uSize; i += 2) { 
      data[ySize + i + 1] = ub.get(i); 
     } 
    } 

    return data; 
} 
+0

您是否使用方法追蹤或其他技術來精確確定您的時間花費在哪裏?你使用什麼決議?你在一個單獨的線程上做你的磁盤I/O嗎? – CommonsWare

+0

是的,這個時間用在YUV - > JPEG轉換和磁盤I/O上。我正在使用最大分辨率(4000 * 3000左右)。是的,磁盤I/O是線程化的。但是,如果我多線程保存圖像,並且如果花費比圖像創建更多的時間,那麼Ill可能會碰到OOME(或者沒有磁盤空間),對吧? –

+0

「時間花在YUV→JPEG轉換」 - 什麼是YUV-> JPEG轉換?爲什麼Camera2不是直接給你JPEG,而是利用專用於該轉換的任何設備硬件? – CommonsWare

回答

1

專用JPEG編碼器單元是有效的,但通常不用於吞吐量優化。 (歷史上,用戶每隔一兩秒就拍一張照片)。在全分辨率下,5X的相機流水線不會以比FPS更快的速度生成JPEG。

如果您需要更高的費率,您需要捕獲未壓縮的YUV。正如CommonsWare所提到的,沒有足夠的磁盤帶寬來將全分辨率未壓縮的YUV流式傳輸到磁盤,因此在內存不足之前,只能保留一定數量的幀。

您可以使用libjpeg-turbo或其他一些高效JPEG編碼器,查看您可以自己壓縮多少幀 - 這可能比硬件JPEG單元更高。最大化速率的最簡單方法是以30fps捕獲YUV,並行運行一些JPEG編碼線程。爲了獲得最大速度,您需要手動編寫與JPEG編碼器交談的代碼,因爲您的源數據是YUV,而不是RGB,而大多數JPEG編碼接口傾向於接受這些數據(即使通常編碼的JPEG的色彩空間實際上是YUV)。當編碼器線程完成前一幀時,它可以抓取來自攝像機的下一幀(您可以保留最新YUV圖像的小型循環緩衝區,以使其更簡單)。

+0

感謝您的詳細解答!你知道OpenCV是否可以有效地對JPEG進行編碼(我已經在我的項目中設置了OpenCV)? YUV圖像來自android.media.Image,它將數據存儲在3個平面中。你知道如何爲這些數據提供OpenCV(或libjpeg)嗎?我用示例代碼更新了我的問題,將其轉換爲字節[],但它不適用於所有分辨率(1920 * 1080正在工作,2592 * 1944不正常),我不確定它是否可靠。你有這種格式的一些經驗嗎? –

+0

您的示例代碼僅適用於底層數據爲NV21;一般來說這並不能得到保證(其他Android設備可能有不同的佈局,這就是爲什麼你需要注意圖像行和像素跨度等),儘管Nexus 5X就是這種情況。 和示例代碼可能如果rowStride!=寬度,因爲你會被複制垃圾值(儘管你不指定什麼「不工作」,具體是指)不起作用。 –

+0

我不知道OpenCV的JPEG編碼有多快,不幸的是,我真的不記得OpenCV支持哪種輸入YUV格式。如果您使用OpenCV NV21,則可能需要將像素數據從平面重新佈局到半平面,如果輸入圖像具有pixelStride = 1或其他平面排序。 –