Aplicaciones Android

Uso de la imagen YUV (YUV_420_888) en Android

ImageFormat # YUV_420_888 es uno de los formatos de imagen más populares compatibles con las cámaras Android. Es un formato YUV (YCbCr) de varios niveles que está representado por tres niveles separados en android.media. La imagen y el orden de las capas están garantizados:

  • [0]: Y avión (Luma)
  • [1]: Plan U (Cb)
  • [2]: Plano V (Cr)

post21 image1
Figura: Representación de YUV420, 8 bits para Y y 4 bits para UV (anidados). Fuente: Wikipedia

Este formato se puede utilizar para procesar los fotogramas de entrada antiguamente de guardarlos en el disco u otra batalla. Una pregunta muy popular en torno a YUV es cómo consumirlo en Android. En este artículo describiría diferentes formas en que se puede usar. La pregunta más popular es cómo convertir YUV a formato de carta de bits o JPEG en Android.

Este artículo fue importado de blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg/.

teoría

Y’UV se inventó cuando los ingenieros querían televisión en color en una infraestructura en blanco y enfadado. Necesitaban un método de señalización que fuera compatible con televisores en blanco y enfadado y que pudiera unir color al mismo tiempo. El componente luma ya existía como una señal en blanco y enfadado; Agregaron la señal UV como decisión.

Super ocurrente, ¿no? Fuente: Wikipedia

Las imágenes YUV se pueden mostrar en varios formatos, como YUV444, YUV422, YUV4111 o YUV420p. Es más realizable convertir entre RGB888 y YUV444.

Pseudocódigo

En genérico, puede convertir un YUV420 to RGB usando la subsiguiente método

void yuv2rgb(uint8_t y, uint8_t u, uint8_t v) 
    int r = y + (1.370705 * (v - 128));
    int g = y - (0.698001 * (v - 128)) - (0.337633 * (u - 128));;
    int b = y + (1.732446 * (u - 128));
    r = clamp(r, 0, 255);
    g = clamp(g, 0, 255);
    b = clamp(b, 0, 255);
    return r, g, b;

¿Convertir YUV_420_888 en ——–?

En esta sección, cubriré varios how to Pedir. Cuando haya trabajado en la parte teórica, las soluciones deberían parecer más intuitivas.

Cómo convertir YUV_420_888 Image a Bitmap?

Hay algunas preguntas frecuentes sobre esto en StackOverflow:

Las sugerencias más comunes son usar un enfoque basado en RenderScript o un enfoque mucho más trillado en el que convertimos eso primero Image a YuvImage como se menciona aquí. Y luego codifica igualmente jpeg como se menciona aquí. Luego use android.graphics.BitmapFactory para decodificar la matriz de bytes JPEG.

// .. get YUV420_888 Image
byte[] jpegByteArray = getJpegFromImage(image);
BitmapFactory bitmapFactory = new BitmapFactory();
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = bitmapFactory.decodeByteArray(
     jpegByteArray, /* offset= */ 0, jpegByteArray.length);

Al observar la última decisión, decidí escribir este artículo. Si está leyendo este artículo, no convierta el Yuv Image a jpeg solo para convertirlo Bitmap.

Enfoque de Java puro

Si miras cómo se ve la imagen de YUV nuevamente:

post21 image2

Verá que hay un valía U&V (Chroma) para cuatro títulos Luma. Intenté usar esta información cercano con esa yuv to rgb Traducción de hacia lo alto para penetrar a algún código Java.

Esto puede convertirse fácilmente en un enfoque costoso cuando se procesa en la capa de Java una vez que las resoluciones de la imagen superan un cierto remate. Veamos los puntos de narración ejecutándolos en un dispositivo de matiz desaparecido específico (con el visor en funcionamiento):

resolución Tiempo de conversión (ms)
320 x 240 29,26 ms
1600 x 1200 (2 MP) 683.0 ms
3264 x 2488 (8 MP) 2826,9 ms

Tabla 1: Tiempo de ejecución para el operación antedicho en un dispositivo Android específico (matiz desaparecido)

Igualmente existe una entrada probabilidad de que vea registros con este aspecto:

Copia simultánea en segundo plano GC liberada 1648 (142 KB) objetos AllocSpace, 2 (38 MB) objetos LOS, 50% libres, 20 MB / 41 MB, suspendidos 1,135 ms en total, 128,325 ms

Ingreso frecuencia de tales registros o parada valía de paused <some value> ms son un indicador de aumento de la presión de GC.

Como resultado, es probable que la JVM no sea adecuada para imágenes verdaderamente grandes, tanto en términos de latencia como de confiabilidad. JVM igualmente establece un remate en la asignación máxima de memoria del montón por una aplicación en Android, que se puede suceder por parada a un remate más parada, pero un remate aún más parada tiene limitaciones. Esto es solo para su información, la conversión de una sola imagen no debería resultar en tales asignaciones. Cuando necesitas conocer un YUV_420_888 La imagen toma 1.5 bytes per pixel tal 8MP (3264 x 2488) = 8120832 pixels La imagen debe ser 11.61 Mb en la memoria mientras que un solo carta de bits ARGB_8888 duraría 4 bytes per pixel lleva a 30.97 Mb por imagen del mismo tamaño.

Enfoque de renderizado

RenderScript es un entorno para realizar tareas de parada rendimiento y de parada rendimiento informático en Android. RenderScript está diseñado principalmente para estar de moda con cálculos en paralelo de datos, aunque las cargas de trabajo en serie igualmente pueden beneficiarse de él. El tiempo de ejecución de RenderScript paraleliza el trabajo entre procesadores que están disponibles en un dispositivo, p. Ej. B. CPU y GPU de varios núcleos. De esa forma, puede concentrarse en expresar algoritmos en empleo de planificar el trabajo. RenderScript es particularmente útil para aplicaciones que realizan procesamiento de imágenes, fotografía por computadora o visión por computadora.

Fuente: developer.android.com

El equipo de Android publicó un documentación sobre la conversión de un búfer YUV de Android a RGB. La asignación de entrada se entrega como 8bit NV12 YUV byte array y la salida es 4 channel 8 bit ARGB Búfer que se puede convertir en un Bitmap. El nombre de intrinsic es ScriptIntrinsicYuvToRGB.

Aquí hay un ejemplo de código Java para usar:

Para la evaluación comparativa, miraré la parte de configuración (es sostener, crear asignaciones de RenderScrip) por separado y las conversiones reales por separado. En este artículo solo estoy explorando el aspecto de latencia del operación, pero al usarlo en las aplicaciones, vale la pena rastrear el uso mayor del montón con Android Studio Profiler.

resolución Tiempo de preparación (ms) Tiempo de conversión (ms) Celeridad en comparación con el enfoque de Java
320 x 240 ~ 188 ms 2,88 ms ms 10,2 veces
1600 x 1200 (2 MP) ~ 175 ms 35,64 ms 19,2 veces
3264 x 2488 (8 MP) ~ 253 ms 44,14 ms 64x

Tabla 2: tiempo de ejecución del operación antedicho en un dispositivo Android específico (matiz desaparecido)

RenderScript es un campeón En comparación con el enfoque de Java puro, y teniendo en cuenta para qué fue diseñado, tiene mucho sentido que este enfoque sea rápido. Dadas las aplicaciones en tiempo positivo, este puede ser un enfoque amoldonado incluso para dispositivos de matiz desaparecido.

Enfoque locorregional

Otro enfoque es utilizar JNI (Java Native Interface) en Android. No tengo ejemplos de código exactos aquí, pero en mi experiencia, JNI funciona muy admisiblemente para el procesamiento de imágenes de desaparecido latencia en Android. Igualmente puede utilizar sombreadores como OpenGL ES para aplicaciones en tiempo positivo. Para nuestro YUV_420_888 a Bitamp La conversión de la API se vería así:

// java delegate.
Bitmap yuv420ToBitmap(Image image) 
    int width = image.getWidth();
    int height = image.getHeight();
    // Pass this array to native code to write to.
    int argbResult = new int[width * height];
    Plane yPlane = image.getPlanes()[0];
    Plane uPlane = image.getPlanes()[1];
    Plane vPlane = image.getPlanes()[2];
    ByteBuffer yBuffer = yPlane.getBuffer();
    ByteBuffer uBuffer = uPlane.getBuffer();
    ByteBuffer vBuffer = vPlane.getBuffer();

    // call the JNI method
    yuv420ToBitmapNative(
        width,
        height,
        yBuffer,
        yPlane.getPixelStride(),
        yPlane.getRowStride(),
        uBuffer,
        uPlane.getPixelStride(),
        uPlane.getRowStride(),
        vBuffer,
        vPlane.getPixelStride(),
        vPlane.getRowStride(),
        argbResult);
    return Bitmap.createBitmap(argbResult, width, height, Config.ARGB_888);


// native interface
static native void yuv420ToBitmapNative(
    int width,
    int height,
    ByteBuffer yBuffer,
    int yPixelStride,
    int yRowStride,
    ByteBuffer uBuffer,
    int uPixelStride,
    int uRowStride,
    ByteBuffer vBuffer,
    int vPixelStride,
    int vRowStride,
    int[] argbResult);

Luego consuma estos datos en el código nativo y use la misma método que se mencionó anteriormente para convertir YUV_420_888 to argb. No tengo las cifras exactas de latencia de rendimiento conmigo.

Cómo convertir YUV_420_888 Image a YuvImage?

Aquí hay un ejemplo de código para convertir android.media.Image a android.graphics.YuvImage:

código

YuvImage toYuvImage(Image image) 
    if (image.getFormat != ImageFormat.YUV_420_888) 
        throw new IllegalArgumentException("Invalid image format");
    

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

    byte[] 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);

    int width = image.getWidth();
    int height = image.getHeight();
    return new YuvImage(nv21, NV21, width, height, /* strides= */ null);

Tiempo de ejecución en diferentes resoluciones.

resolución Promedio (ms) Min (ms) Máx. (Ms)
320 x 240 11,14 ms 6,00 ms 65,00 ms
1600 x 1200 (2 MP) 10.30 ms 6,00 ms 40,00 ms
3264 x 2488 (8 MP) 38,66 ms 25,00 ms 109,00 ms

Tabla 4: Tiempo de ejecución para el operación antedicho en un dispositivo Android específico (matiz desaparecido).

En genérico, estos algoritmos no parecían tener un parada costo por sí mismos: prácticamente su copia de memoria.

Cómo convertir YUV_420_888 Image en formato jpeg?

Puede convertir android.media.Image a formato JPEG (como una sola capa) byte[]):

código

byte[] toJpegImage(Image image, int imageQuality) 
    if (image.getFormat != ImageFormat.YUV_420_888) 
        throw new IllegalArgumentException("Invalid image format");
    

    YuvImage yuvImage = toYuvImage(image);
    int width = image.getWidth();
    int height = image.getHeight();

    // Convert to jpeg
    byte[] jpegImage = null;
    try (ByteArrayOutputStream out = new ByteArrayOutputStream()) 
        yuvImage.compressToJpeg(new Rect(0, 0, width, height), imageQuality, out);
        jpegImage = out.getBytes();
    

    return jpegImage;

Tiempo de ejecución en imageQuality = 100 con diferente resolución

resolución Promedio (ms) Min (ms) Máx. (Ms)
320 x 240 3.60 ms 3,00 ms 6,00 ms
1600 x 1200 (2 MP) 74,20 ms 72,00 ms 85,00 ms
3264 x 2488 (8 MP) 98,35 ms 94,00 ms 110,00 ms

Tabla 5: Tiempo de ejecución para el operación antedicho en un dispositivo Android específico (matiz desaparecido).

Este artículo fue importado de blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg/.

LEER  Según los informes, Google canceló el plegado de píxeles.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba