2016-10-17 41 views
6

Tôi nhận được khung preview sử dụng OnImageAvailableListener:
Android Camera2 API YUV_420_888 để JPEG

@Override 
public void onImageAvailable(ImageReader reader) { 
    Image image = null; 
    try { 
     image = reader.acquireLatestImage(); 
     Image.Plane[] planes = image.getPlanes(); 
     ByteBuffer buffer = planes[0].getBuffer(); 
     byte[] data = new byte[buffer.capacity()]; 
     buffer.get(data); 
     //data.length=332803; width=3264; height=2448 
     Log.e(TAG, "data.length=" + data.length + "; width=" + image.getWidth() + "; height=" + image.getHeight()); 
     //TODO data processing 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    if (image != null) { 
     image.close(); 
    } 
} 

Mỗi chiều dài thời gian của dữ liệu là khác nhau nhưng chiều rộng và chiều cao hình ảnh đều giống nhau.
Vấn đề chính: data.length quá nhỏ đối với độ phân giải như 3264x2448.
Kích thước mảng dữ liệu phải là 3264 * 2448 = 7,990,272, không phải 300.000 - 600.000.
Có gì sai?


imageReader = ImageReader.newInstance(3264, 2448, ImageFormat.JPEG, 5); 

Trả lời

8

tôi giải quyết vấn đề này bằng cách sử dụng định dạng hình ảnh YUV_420_888 và chuyển đổi nó sang định dạng hình ảnh JPEG bằng tay.

imageReader = ImageReader.newInstance(MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT, 
             ImageFormat.YUV_420_888, 5); 
imageReader.setOnImageAvailableListener(this, null); 

Surface imageSurface = imageReader.getSurface(); 
List<Surface> surfaceList = new ArrayList<>(); 
//...add other surfaces 
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 
previewRequestBuilder.addTarget(imageSurface); 
      surfaceList.add(imageSurface); 
cameraDevice.createCaptureSession(surfaceList, 
        new CameraCaptureSession.StateCallback() { 
//...implement onConfigured, onConfigureFailed for StateCallback 
}, null); 

@Override 
public void onImageAvailable(ImageReader reader) { 
    Image image = reader.acquireLatestImage(); 
    if (image != null) { 
     //converting to JPEG 
     byte[] jpegData = ImageUtils.imageToByteArray(image); 
     //write to file (for example ..some_path/frame.jpg) 
     FileManager.writeFrame(FILE_NAME, jpegData); 
     image.close(); 
    } 
} 

public final class ImageUtil { 

    public static byte[] imageToByteArray(Image image) { 
     byte[] data = null; 
     if (image.getFormat() == ImageFormat.JPEG) { 
      Image.Plane[] planes = image.getPlanes(); 
      ByteBuffer buffer = planes[0].getBuffer(); 
      data = new byte[buffer.capacity()]; 
      buffer.get(data); 
      return data; 
     } else if (image.getFormat() == ImageFormat.YUV_420_888) { 
      data = NV21toJPEG(
        YUV_420_888toNV21(image), 
        image.getWidth(), image.getHeight()); 
     } 
     return data; 
    } 

    private static byte[] YUV_420_888toNV21(Image image) { 
     byte[] nv21; 
     ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); 
     ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); 
     ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); 

     int ySize = yBuffer.remaining(); 
     int uSize = uBuffer.remaining(); 
     int vSize = vBuffer.remaining(); 

     nv21 = new byte[ySize + uSize + vSize]; 

     //U and V are swapped 
     yBuffer.get(nv21, 0, ySize); 
     vBuffer.get(nv21, ySize, vSize); 
     uBuffer.get(nv21, ySize + vSize, uSize); 

     return nv21; 
    } 

    private static byte[] NV21toJPEG(byte[] nv21, int width, int height) { 
     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null); 
     yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out); 
     return out.toByteArray(); 
    } 
} 

public final class FileManager { 
    public static void writeFrame(String fileName, byte[] data) { 
     try { 
      BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName)); 
      bos.write(data); 
      bos.flush(); 
      bos.close(); 
//   Log.e(TAG, "" + data.length + " bytes have been written to " + filesDir + fileName + ".jpg"); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
+0

Tôi đã sử dụng lớp '' 'ImageUtil''' từ câu trả lời để chuyển' '' YUV_420_888''' thành '' 'JPEG''' tuy nhiên tôi không nhận được kết quả chính xác. Bằng cách sử dụng lớp này sẽ có thể lưu đầu ra '' 'byte []' '' vào một tệp JPEG hoặc thậm chí có thể hiển thị nó trên '' 'SurfaceView'''?Tôi đang cố gắng làm như vậy tuy nhiên tôi nhận được hình ảnh méo mó. – ahasbini

+0

'dữ liệu = NV21toJPEG (YUV_420_888toNV21 (hình ảnh), image.getWidth(), image.getHeight());' có biến dạng không? –

+0

Có, tôi đã đặt lớp như là và thực hiện các tập tin văn bản tương tự như thực hiện của bạn, nhưng bằng cách sử dụng '' 'FileOutputStream''' thay vào đó, và hình ảnh cuối cùng bị bóp méo. Tôi có thể cho bạn thấy những triển khai hoàn chỉnh thông qua git repo, tôi sẽ thúc đẩy một chút. – ahasbini

3

Tôi không chắc chắn, nhưng tôi nghĩ rằng bạn đang dùng chỉ một trong những máy bay của định dạng YUV_420_888 (phần sáng).

Trong trường hợp của tôi, tôi thường chuyển hình ảnh thành byte [] theo cách này.

  Image m_img; 
      Log.v(LOG_TAG,"Format -> "+m_img.getFormat()); 
      Image.Plane Y = m_img.getPlanes()[0]; 
      Image.Plane U = m_img.getPlanes()[1]; 
      Image.Plane V = m_img.getPlanes()[2]; 

      int Yb = Y.getBuffer().remaining(); 
      int Ub = U.getBuffer().remaining(); 
      int Vb = V.getBuffer().remaining(); 

      data = new byte[Yb + Ub + Vb]; 
      //your data length should be this byte array length. 

      Y.getBuffer().get(data, 0, Yb); 
      U.getBuffer().get(data, Yb, Ub); 
      V.getBuffer().get(data, Yb+ Ub, Vb); 
      final int width = m_img.getWidth(); 
      final int height = m_img.getHeight(); 

Và tôi sử dụng bộ đệm byte này để chuyển thành rgb.

Hy vọng điều này sẽ hữu ích.

Chúc mừng. Unai.

+0

Cảm ơn bạn đã trả lời! Hiện tại tôi đang sử dụng 'ImageFormat.JPEG', vì vậy' m_img.getPlanes(). Length' bằng 1. –

+0

Được rồi, nếu tôi sử dụng 'ImageFormat.YUV_420_888', làm thế nào để nén' rgb' buffer thành 'JPEG'? –

+0

Tuyệt vời, bạn được chào đón, tôi không biết mục đích của bạn với byteArray này là gì. Xin lỗi vì câu trả lời bị trì hoãn của tôi. – uelordi

1

Mã của bạn tôi s yêu cầu hình ảnh định dạng JPEG, được nén. Chúng sẽ thay đổi kích thước cho mỗi khung hình và chúng sẽ nhỏ hơn nhiều so với hình ảnh không nén. Nếu bạn không muốn làm gì ngoài việc lưu ảnh JPEG, bạn có thể lưu những gì bạn có trong dữ liệu byte [] vào đĩa và bạn đã hoàn tất.

Nếu bạn muốn thực sự làm điều gì đó với JPEG, bạn có thể sử dụng BitmapFactory.decodeByteArray() để chuyển đổi nó thành một Bitmap, ví dụ, mặc dù đó là khá kém hiệu quả.

Hoặc bạn có thể chuyển sang YUV, hiệu quả hơn, nhưng bạn cần phải làm nhiều công việc hơn để lấy Bitmap ra khỏi nó.

+0

Chuyển đổi sang Bitmap và quay lại mỗi khung chỉ để giải mã không phải là một lựa chọn. –

+0

Giải pháp hiện tại của bạn có vẻ là cải tổ dữ liệu YUV thành NV21 và sau đó nén nó thành JPEG; nó không rõ ràng những gì bạn muốn YUV cho, nhưng nó chắc chắn rẻ hơn để chỉ yêu cầu JPEG nếu tất cả các bạn muốn làm là để lưu dữ liệu. Bạn có thể yêu cầu cả JPEG và YUV, tùy thuộc vào độ phân giải, có thể là tùy chọn hiệu quả nhất, mặc dù đầu ra JPEG có thể có tốc độ khung hình thấp hơn 30 khung hình/giây. –

+0

Tôi đang sử dụng NV21 để nhận dạng/giải mã QR. JPEG để gửi khung đến máy chủ. –

Các vấn đề liên quan