Estoy tratando de implementar el procesamiento de datos de imagen de vista previa de la cámara usando la api de camera2 como se propone aquí: Procesamiento de datos de imagen de vista previa de la cámara con Android L y Camera2 API.

Recibo con éxito devoluciones de llamada usando onImageAvailableListener, pero para un procesamiento futuro necesito obtener un mapa de bits de YUV_420_888 android.media.Image. Busqué preguntas similares, pero ninguna ayudó.

¿Podría sugerirme cómo convertir android.media.Image (YUV_420_888) a mapa de bits o tal vez hay una mejor manera de escuchar los marcos de vista previa?

17
mol 10 feb. 2015 a las 14:01

3 respuestas

La mejor respuesta

Escribo un código sobre esto, y es la vista previa de datos YUV y lo cambio a datos JPEG, y puedo usarlo para guardar como mapa de bits, byte [] u otros (puede ver la clase "Asignación").

Y el documento del SDK dice:
"Para un procesamiento eficiente de YUV con android.renderscript: cree una asignación de RenderScript con un tipo de YUV compatible, la marca IO_INPUT y uno de los tamaños devueltos por getOutputSizes (Allocation.class), luego obtenga Superficie con getSurface () ".

aquí está el código, espero que le ayude: https://github.com/pinguo-yuyidong/Camera2/blob/master/camera2/src/main/rs/yuv2rgb.rs

5
yydcdut 11 feb. 2015 a las 01:51

Puede hacer esto usando el Renderscript intrínseco integrado, ScriptIntrinsicYuvToRGB . Código tomado de Camera2 api Imageformat.yuv_420_888 resultados en la imagen rotada:

@Override
public void onImageAvailable(ImageReader reader)
{
    // Get the YUV data

    final Image image = reader.acquireLatestImage();
    final ByteBuffer yuvBytes = this.imageToByteBuffer(image);

    // Convert YUV to RGB

    final RenderScript rs = RenderScript.create(this.mContext);

    final Bitmap        bitmap     = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
    final Allocation allocationRgb = Allocation.createFromBitmap(rs, bitmap);

    final Allocation allocationYuv = Allocation.createSized(rs, Element.U8(rs), yuvBytes.array().length);
    allocationYuv.copyFrom(yuvBytes.array());

    ScriptIntrinsicYuvToRGB scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
    scriptYuvToRgb.setInput(allocationYuv);
    scriptYuvToRgb.forEach(allocationRgb);

    allocationRgb.copyTo(bitmap);

    // Release

    bitmap.recycle();

    allocationYuv.destroy();
    allocationRgb.destroy();
    rs.destroy();

    image.close();
}

private ByteBuffer imageToByteBuffer(final Image image)
{
    final Rect crop   = image.getCropRect();
    final int  width  = crop.width();
    final int  height = crop.height();

    final Image.Plane[] planes     = image.getPlanes();
    final byte[]        rowData    = new byte[planes[0].getRowStride()];
    final int           bufferSize = width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8;
    final ByteBuffer    output     = ByteBuffer.allocateDirect(bufferSize);

    int channelOffset = 0;
    int outputStride = 0;

    for (int planeIndex = 0; planeIndex < 3; planeIndex++)
    {
        if (planeIndex == 0)
        {
            channelOffset = 0;
            outputStride = 1;
        }
        else if (planeIndex == 1)
        {
            channelOffset = width * height + 1;
            outputStride = 2;
        }
        else if (planeIndex == 2)
        {
            channelOffset = width * height;
            outputStride = 2;
        }

        final ByteBuffer buffer      = planes[planeIndex].getBuffer();
        final int        rowStride   = planes[planeIndex].getRowStride();
        final int        pixelStride = planes[planeIndex].getPixelStride();

        final int shift         = (planeIndex == 0) ? 0 : 1;
        final int widthShifted  = width >> shift;
        final int heightShifted = height >> shift;

        buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));

        for (int row = 0; row < heightShifted; row++)
        {
            final int length;

            if (pixelStride == 1 && outputStride == 1)
            {
                length = widthShifted;
                buffer.get(output.array(), channelOffset, length);
                channelOffset += length;
            }
            else
            {
                length = (widthShifted - 1) * pixelStride + 1;
                buffer.get(rowData, 0, length);

                for (int col = 0; col < widthShifted; col++)
                {
                    output.array()[channelOffset] = rowData[col * pixelStride];
                    channelOffset += outputStride;
                }
            }

            if (row < heightShifted - 1)
            {
                buffer.position(buffer.position() + rowStride - length);
            }
        }
    }

    return output;
}
7
Sam 14 abr. 2019 a las 06:19

Para una solución más simple, vea mi implementación aquí:

Conversión YUV 420_888 a mapa de bits (código completo)

La función toma media.image como entrada y crea tres asignaciones de RenderScript basadas en los planos y, u y v. Sigue la lógica YUV_420_888 como se muestra en esta ilustración de Wikipedia.

enter image description here

Sin embargo, aquí tenemos tres planos de imagen separados para los canales Y, U y V, por lo que los tomo como tres bytes [], es decir, asignaciones U8. La asignación y tiene bytes de tamaño ancho * alto, mientras que las asignaciones u y v tienen tamaño ancho * alto / 4 bytes cada una, lo que refleja el hecho de que cada byte u cubre 4 píxeles (lo mismo que cada byte v).

7
Community 23 may. 2017 a las 10:29