2015-11-05 320 views
8

我試圖將圖像從YUV_420_888轉換爲rgb,並且在輸出圖像時遇到了一些麻煩。在ImageReader中,我以YUV_420_888格式(使用相機2 api獲取此圖像預覽)獲取圖像。camera2 api將yuv420轉換爲rgb綠色

imageReader = ImageReader.newInstance(1920,1080,ImageFormat.YUV_420_888,10); 

在爲YuvImage類寫作Android SDK中,該YuvImage只使用NV21,YUY2。

我們可以看到差異N21和YUV420之間並不大,我嘗試數據轉換爲N21

YUV420: enter image description here

N21: enter image description here

onImageAvailable我分別得到每架飛機,並把它們放在正確的地方(如圖)

ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 

ByteBuffer bufferY = image.getPlanes()[0].getBuffer(); 
byte[] data0 = new byte[bufferY.remaining()]; 
bufferY.get(data0); 

ByteBuffer bufferU = image.getPlanes()[1].getBuffer(); 
byte[] data1 = new byte[bufferU.remaining()]; 
bufferU.get(data1); 

ByteBuffer bufferV = image.getPlanes()[2].getBuffer(); 
byte[] data2 = new byte[bufferV.remaining()]; 
bufferV.get(data2); 
... 
outputStream.write(data0); 
for (int i=0;i<bufferV.remaining();i++) { 
    outputStream.write(data1[i]); 
    outputStream.write(data2[i]); 
} 

後創建YuvImage,轉換爲位圖,查看和保存

final YuvImage yuvImage = new YuvImage(outputStream.toByteArray(), ImageFormat.NV21, 1920,1080, null); 
ByteArrayOutputStream outBitmap = new ByteArrayOutputStream(); 

yuvImage.compressToJpeg(new Rect(0, 0,1920, 1080), 95, outBitmap); 

byte[] imageBytes = outBitmap.toByteArray(); 

final Bitmap imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); 
mImageView.setImageBitmap(imageBitmap); 
... 
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 95, out); 

但我保存的圖像是綠色和粉紅色: capture image capture image

我錯過了什麼?

+2

小點:爲NV21圖其實是錯誤的,並描述了NV12編碼。除了交換U和V之外,NV21是相同的,即VUVUVUVUV而不是UVUVUVUVUV。 – samgak

回答

2

bufferV.get(data2)增加了字節緩衝區的位置。這就是爲什麼循環for (int i=0;i<bufferV.remaining();i++)產生0次迭代。您可以輕鬆地把它改寫爲

for (int i=0; i<data1.length; i++) { 
    outputStream.write(data1[i]); 
    outputStream.write(data2[i]); 
} 
+0

我改變這個......但是一樣。 我查了一下,裏面有數組** ** **,** data2 **和他們差不多。數組長度爲518400,並且從這個長度爲517440是0的值。 可能只能使用960? –

+0

這可能是您設備上camera2 API的錯誤實現。 –

3

有您的轉換的嘗試兩個主要問題:

  1. 我們不能假定U和V平面是孤立的,它們可能包含交錯的數據(例如U-PLANE = {U1, V1, U2, V2, ...} )。事實上,它甚至可能已經是NV21格式的交錯了。這裏的關鍵是看飛機的row stridepixel stride,並檢查我們可以假設的關於YUV_420_888 format
  2. 事實上,你已經評論過大部分U和V平面數據都是零,這表明你正在經歷Android bug on the generation of images in YUV_420_888。這意味着,即使您獲得了轉換權限,如果受到僅在Android 5.1.1及更高版本中修復的bug的影響,該圖像仍然顯示爲綠色,因此除了檢查您正在使用的版本之外,還值得一看修復代碼。
+0

感謝與Android的錯誤鏈接,我檢查我的版本,我也有5.0.1。所以我會嘗試更新我的android,並會看到有什麼變化 –

3

我已經實現了YUV_420邏輯(完全按照上面的圖中示出)中的renderScript,看到完整的代碼在這裏:

Conversion YUV_420 _888 to Bitmap, complete code

它產生完美bimaps爲API 22,但對於API 21它顯示了「綠色田園詩」。從這我可以確認,你找到的結果。正如上面Silvaren已經提到的那樣,原因似乎是API 21中的一個Android bug。看看我的rs代碼,很明顯,如果缺少U和V信息(即零),G(reen)ARGB組件在轉換。

我在我的Galaxy S5(仍然是API 21)上看到類似的綠色圖片 - 這裏甚至顛倒了;-)。我懷疑API 21中的大多數設備目前還沒有將Camera2用於設備相機應用程序。有一個名爲「手動相機兼容性」的免費應用程序,可以對此進行測試。從這我看到,確實S5/API21仍然不使用Camera2 ...幸運的是...

2

我得到了ImageFormat.YUV_420_888的圖像,併成功保存到JPEG文件,並可以在Windows上正確地查看它。
我分享這裏:

private final Image mImage; 
private final File mFile; 
private final int mImageFormat; 


ByteArrayOutputStream outputbytes = new ByteArrayOutputStream(); 

ByteBuffer bufferY = mImage.getPlanes()[0].getBuffer(); 
byte[] data0 = new byte[bufferY.remaining()]; 
bufferY.get(data0); 

ByteBuffer bufferU = mImage.getPlanes()[1].getBuffer(); 
byte[] data1 = new byte[bufferU.remaining()]; 
bufferU.get(data1); 

ByteBuffer bufferV = mImage.getPlanes()[2].getBuffer(); 
byte[] data2 = new byte[bufferV.remaining()]; 
bufferV.get(data2); 

try 
{ 
    outputbytes.write(data0); 
    outputbytes.write(data2); 
    outputbytes.write(data1); 


    final YuvImage yuvImage = new YuvImage(outputbytes.toByteArray(), ImageFormat.NV21, mImage.getWidth(),mImage.getHeight(), null); 
    ByteArrayOutputStream outBitmap = new ByteArrayOutputStream(); 

    yuvImage.compressToJpeg(new Rect(0, 0,mImage.getWidth(), mImage.getHeight()), 95, outBitmap); 


    FileOutputStream outputfile = null; 
    outputfile = new FileOutputStream(mFile); 
    outputfile.write(outBitmap.toByteArray()); 
} 
catch (IOException e) 
{ 
    e.printStackTrace(); 
} 
finally 
{ 
    mImage.close(); 
}