web-dev-qa-db-fra.com

API Android Camera2 YUV_420_888 au format JPEG

Je reçois des aperçus des cadres avec 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();
    }
}

Chaque durée de données est différente mais la largeur et la hauteur de l’image sont identiques.
Problème principal: data.length est trop petit pour une résolution telle que 3264x2448.
La taille de la matrice de données doit être 3264 * 2448 = 7 990 272, et non 300 000 - 600 000.
Qu'est-ce qui ne va pas?


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

J'ai résolu ce problème en utilisantYUV_420_888format d'image et en le convertissant au formatJPEGimage manuellement.

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

Je ne suis pas sûr, mais je pense que vous ne prenez qu'un des plans du format YUV_420_888 (partie luminance). 

Dans mon cas, je transforme généralement mon image en octet [] de cette manière. 

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

Et j'utilise ce tampon d'octets pour transformer en rgb.

J'espère que cela t'aides. 

À votre santé. Unai.

4
uelordi

Votre code demande des images au format JPEG, qui sont compressées. Leur taille changera pour chaque image et sera beaucoup plus petite que l'image non compressée. Si vous ne voulez rien faire à part sauvegarder les images JPEG, vous pouvez simplement sauvegarder ce que vous avez dans les données d'octet [] sur le disque et le tour est joué.

Si vous voulez réellement utiliser quelque chose avec le JPEG, vous pouvez utiliser BitmapFactory.decodeByteArray () pour le convertir en Bitmap, par exemple, bien que ce soit plutôt inefficace.

Ou vous pouvez passer à YUV, ce qui est plus efficace, mais vous devez faire plus de travail pour en extraire un bitmap.

2
Eddy Talvala