API de la cámara Android2 YUV_420_888 a JPEG

Estoy obteniendo marcos de vista previa usando OnImageAvailableListener :

@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(); } } 

Cada vez que la longitud de los datos es diferente, pero el ancho y la altura de la imagen son los mismos.
Problema principal: data.length es demasiado pequeño para una resolución como 3264×2448.
El tamaño de la matriz de datos debe ser 3264 * 2448 = 7,990,272, no 300,000 – 600,000.
¿Qué está mal?


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

Solucioné este problema usando el formato de imagen YUV_420_888 y convirtiéndolo al formato de imagen JPEG manualmente.

 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(); } } } 

No estoy seguro, pero creo que está tomando sólo uno del plano del formato YUV_420_888 (parte de luminancia).

En mi caso, por lo general transformar mi imagen en byte [] de esta manera.

  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(); 

Y utilizo este buffer de bytes para transformar a rgb.

Espero que esto ayude.

Aclamaciones. Unai.

Su código solicita imágenes en formato JPEG, que se comprimen. Cambiarán de tamaño para cada fotograma y serán mucho más pequeños que la imagen sin comprimir. Si no desea hacer nada aparte de guardar imágenes JPEG, sólo puede guardar lo que tiene en el byte [] datos en el disco y ya está.

Si desea hacer algo con el JPEG, puede utilizar BitmapFactory.decodeByteArray () para convertirlo en un mapa de bits, por ejemplo, aunque es bastante ineficiente.

O usted puede cambiar a YUV, que es más eficiente, pero usted necesita hacer más trabajo para conseguir un mapa de bits fuera de él.

  • JAVAH no puede encontrar clase (android ndk)
  • Android: obtenga el resultado del cuadro de diálogo predeterminado de cambios de la aplicación de SMS
  • Error de compilación de Gradle (Android Studio 2.2.2)
  • Android notification show Imagen de cadena sin unicode
  • NullPointerException acceder a las vistas en onCreate ()
  • Cómo hacer los métodos en beanshell?
  • Java Realm obtiene el último elemento insertado
  • Hacer que Android Service escuche Hardware-Key Press Events
  • Optimizaciones de Java: (Hotspot / Dalvik) Optimización del método final devolver una constante?
  • No se puede resolver el método getMap ()
  • recv falló (errno = 11) en la aplicación de streaming (MediaPlayer) en Nexus
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.