2015-08-13 70 views
2

我用camera2 API構建了我自己的相機應用程序。我從樣本「camera2Raw」開始,我添加了YUV_420_888支持而不是JPEG。但現在我想知道如何將圖像保存在ImageSaver中!?如何保存YUV_420_888圖片?

這裏是我的run方法的代碼:

@Override 
    public void run() { 
     boolean success = false; 
     int format = mImage.getFormat(); 
     switch(format) { 
      case ImageFormat.RAW_SENSOR:{ 
       DngCreator dngCreator = new DngCreator(mCharacteristics, mCaptureResult); 
       FileOutputStream output = null; 
       try { 
        output = new FileOutputStream(mFile); 
        dngCreator.writeImage(output, mImage); 
        success = true; 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } finally { 
        mImage.close(); 
        closeOutput(output); 
       } 
       break; 
      } 
      case ImageFormat.YUV_420_888:{ 
       ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); 
       byte[] bytes = new byte[buffer.remaining()]; 
       buffer.get(bytes); 
       FileOutputStream output = null; 
       try { 
        output = new FileOutputStream(mFile); 
        output.write(bytes); 
        success = true; 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } finally { 
        mImage.close(); 
        closeOutput(output); 
       } 
       break; 
      } 
      default: 
       Log.e(TAG, "Cannot save image, unexpected image format:" + format); 

     } 
     // Decrement reference count to allow ImageReader to be closed to free up resources. 
     mReader.close(); 

     // If saving the file succeeded, update MediaStore. 
     if (success) { 
      MediaScannerConnection.scanFile(mContext, new String[] { mFile.getPath()}, 
      /*mimeTypes*/ null, new MediaScannerConnection.MediaScannerConnectionClient() { 
       @Override 
       public void onMediaScannerConnected() { 
        // Do nothing 
       } 

       @Override 
       public void onScanCompleted(String path, Uri uri) { 
        Log.i(TAG, "Scanned " + path + ":"); 
        Log.i(TAG, "-> uri=" + uri); 
       } 
      }); 
     } 
    } 

我試圖保存YUV圖像像JPEG,但這種方式,我只能得到一個平面和保存的數據沒有任何意義我...

什麼是保存YUV圖像的正確方法?把它轉換成RGB(YUV的意義是什麼?)?或者與YuvImage類?

回答

3

通常,您不需要將YUV圖像保存爲文件,因此沒有內置函數可以這樣做。此外,這種YUV數據沒有標準的圖像格式編碼。 YUV通常是一種中等形式的數據,便於相機流水線以及之後轉換爲其他格式。

如果您真的想要這樣做,您可以將三個通道的緩衝區作爲未編碼字節數據寫入文件,然後在其他位置打開並重新構建它。儘管如此,請確保保存其他重要信息,例如跨步數據。這就是我所做的。下面是我用的,用在推理評論沿着文件格式switch語句中的相關線路:

File file = new File(SAVE_DIR, mFilename); 
FileOutputStream output = null; 
ByteBuffer buffer; 
byte[] bytes; 
boolean success = false; 

switch (mImage.getFormat()){ 

    (... other image data format cases ...) 

    // YUV_420_888 images are saved in a format of our own devising. First write out the 
    // information necessary to reconstruct the image, all as ints: width, height, U-,V-plane 
    // pixel strides, and U-,V-plane row strides. (Y-plane will have pixel-stride 1 always.) 
    // Then directly place the three planes of byte data, uncompressed. 
    // 
    // Note the YUV_420_888 format does not guarantee the last pixel makes it in these planes, 
    // so some cases are necessary at the decoding end, based on the number of bytes present. 
    // An alternative would be to also encode, prior to each plane of bytes, how many bytes are 
    // in the following plane. Perhaps in the future. 
    case ImageFormat.YUV_420_888: 
     // "prebuffer" simply contains the meta information about the following planes. 
     ByteBuffer prebuffer = ByteBuffer.allocate(16); 
     prebuffer.putInt(mImage.getWidth()) 
     .putInt(mImage.getHeight()) 
     .putInt(mImage.getPlanes()[1].getPixelStride()) 
     .putInt(mImage.getPlanes()[1].getRowStride()); 

     try { 
      output = new FileOutputStream(file); 
      output.write(prebuffer.array()); // write meta information to file 
      // Now write the actual planes. 
      for (int i = 0; i<3; i++){ 
       buffer = mImage.getPlanes()[i].getBuffer(); 
       bytes = new byte[buffer.remaining()]; // makes byte array large enough to hold image 
       buffer.get(bytes); // copies image from buffer to byte array 
       output.write(bytes); // write the byte array to file 
      } 
      success = true; 
     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } finally { 
      Log.v(appFragment.APP_TAG,"Closing image to free buffer."); 
      mImage.close(); // close this to free up buffer for other images 
      if (null != output) { 
       try { 
        output.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
     break; 
    } 

,因爲它可以達到數據究竟如何是隔行掃描的裝置,它可以是具有挑戰性的提取Y,U,V頻道。要查看如何讀取和提取文件的MATLAB實現,請參閱here

+0

非常酷!我只添加了一個「FileOutputStream輸出= null;」第一次嘗試之前,一切正常!非常感謝你! – esihemohen

+0

啊,是的,試圖從其他代碼段中剪切片段的危險。對於那個很抱歉!我將修改上面的代碼,使其對於未來發現它的人來說是相當獨立的。 – rcsumner

+0

如果我以這種方式將'YUV_420_888'圖像保存到'.jpg'文件中,則此圖像文件不可讀。 –

1

如果您有YuvImage對象,那麼您可以使用compressToJpeg函數將它轉換爲Jpeg,如下所示。

ByteArrayOutputStream out = new ByteArrayOutputStream(); 
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null); 
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 50, out); 
byte[] imageBytes = out.toByteArray(); 
Bitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); 
iv.setImageBitmap(image); 

雖然你必須明確地設置圖像的寬度和高度。

+0

什麼是「數據」?我有一個「圖像」,我需要一個字節[]。我可以將圖像的所有平面轉換爲單個字節[]嗎? – esihemohen

+1

此外YuvImage不支持YUV_420_888格式...日誌:「現在只支持ImageFormat.NV21和ImageFormat.YUY2」 – esihemohen

+0

數據不過是字節數組。是的,顯然你已經說過它不支持YUV_420_888。 – Bhargav