Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Convertir YUV-> RGB (procesamiento de imágenes) -> YUV durante onPreviewFrame en android?

Estoy captando la imagen usando SurfaceView y conseguir los datos preliminares de Yuv Raw en public void onPreviewFrame4 (byte [] data, cámara de la cámara)

Tengo que realizar un cierto preprocesamiento de la imagen en onPreviewFrame así que necesito convertir los datos de la previsualización de Yuv a los datos del RGB que el preprocesamiento de la imagen y de nuevo a los datos de Yuv.

He utilizado ambas funciones para codificar y decodificar datos de Yuv a RGB como sigue:

public void onPreviewFrame(byte[] data, Camera camera) { Point cameraResolution = configManager.getCameraResolution(); if (data != null) { Log.i("DEBUG", "data Not Null"); // Preprocessing Log.i("DEBUG", "Try For Image Processing"); Camera.Parameters mParameters = camera.getParameters(); Size mSize = mParameters.getPreviewSize(); int mWidth = mSize.width; int mHeight = mSize.height; int[] mIntArray = new int[mWidth * mHeight]; // Decode Yuv data to integer array decodeYUV420SP(mIntArray, data, mWidth, mHeight); // Converting int mIntArray to Bitmap and // than image preprocessing // and back to mIntArray. // Encode intArray to Yuv data encodeYUV420SP(data, mIntArray, mWidth, mHeight); } } static public void decodeYUV420SP(int[] rgba, byte[] yuv420sp, int width, int height) { final int frameSize = width * height; for (int j = 0, yp = 0; j < height; j++) { int uvp = frameSize + (j >> 1) * width, u = 0, v = 0; for (int i = 0; i < width; i++, yp++) { int y = (0xff & ((int) yuv420sp[yp])) - 16; if (y < 0) y = 0; if ((i & 1) == 0) { v = (0xff & yuv420sp[uvp++]) - 128; u = (0xff & yuv420sp[uvp++]) - 128; } int y1192 = 1192 * y; int r = (y1192 + 1634 * v); int g = (y1192 - 833 * v - 400 * u); int b = (y1192 + 2066 * u); if (r < 0) r = 0; else if (r > 262143) r = 262143; if (g < 0) g = 0; else if (g > 262143) g = 262143; if (b < 0) b = 0; else if (b > 262143) b = 262143; // rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & // 0xff00) | ((b >> 10) & 0xff); // rgba, divide 2^10 ( >> 10) rgba[yp] = ((r << 14) & 0xff000000) | ((g << 6) & 0xff0000) | ((b >> 2) | 0xff00); } } } static public void encodeYUV420SP_original(byte[] yuv420sp, int[] rgba, int width, int height) { final int frameSize = width * height; int[] U, V; U = new int[frameSize]; V = new int[frameSize]; final int uvwidth = width / 2; int r, g, b, y, u, v; for (int j = 0; j < height; j++) { int index = width * j; for (int i = 0; i < width; i++) { r = (rgba[index] & 0xff000000) >> 24; g = (rgba[index] & 0xff0000) >> 16; b = (rgba[index] & 0xff00) >> 8; // rgb to yuv y = (66 * r + 129 * g + 25 * b + 128) >> 8 + 16; u = (-38 * r - 74 * g + 112 * b + 128) >> 8 + 128; v = (112 * r - 94 * g - 18 * b + 128) >> 8 + 128; // clip y yuv420sp[index++] = (byte) ((y < 0) ? 0 : ((y > 255) ? 255 : y)); U[index] = u; V[index++] = v; } } 

El problema es que la codificación y decodificación Yuv datos podrían tener algún error, porque si saltar el paso de preprocesamiento que también codificados Yuv datos son diferentes de los datos originales de PreviewCallback.

Por favor, ayúdame a resolver este problema. Tengo que utilizar este código en la exploración de OCR, así que tengo que implementar este tipo de lógica.

Si cualquier otra forma de hacer lo mismo que por favor me proporciona.

Gracias por adelantado. 🙂

  • Convertir la cámara Android api YUV_420_888 a RGB
  • La manera más rápida de dibujar un marco de vídeo decodificado con MediaCodec a la pantalla?
  • Camera.PreviewCallback equivalente en Camera2 API
  • Rotar una matriz de bytes YUV en Android
  • Convierta la matriz de bytes NV21 en formato legible de mapa de bits
  • Cómo mostrar la imagen de YUV en Android
  • YUV_420_888 interpretación en Samsung Galaxy S7 (Camera2)
  • Convertir el marco de vista previa en un mapa de bits
  • 6 Solutions collect form web for “Convertir YUV-> RGB (procesamiento de imágenes) -> YUV durante onPreviewFrame en android?”

    ¿Por qué no especificar que la vista previa de la cámara debe proporcionar imágenes RGB?

    Es decir, Camera.Parameters.setPreviewFormat (ImageFormat.RGB_565) ;

    Aunque la documentación sugiere que puede configurar el formato de los datos de imagen debe llegar desde la cámara, en la práctica que a menudo tienen la opción de uno: NV21, un formato YUV. Para obtener mucha información sobre este formato, consulte http://www.fourcc.org/yuv.php#NV21 y para obtener información sobre la teoría detrás de la conversión a RGB, consulte http://www.fourcc.org/fccyvrgb.php . Hay una explicación basada en imagen en la imagen Extracto en blanco y negro del formato NV21 de la cámara con Android .

    Otro formato, llamado YUV420SP, es también muy frecuente.

    Sin embargo, una vez que haya configurado su rutina onPreviewFrame, la mecánica de ir desde la matriz de bytes que le envía a los datos útiles es algo, ummmm, poco claro. A partir de la API 8, la siguiente solución está disponible, para llegar a un ByteStream consolidando un JPEG de la imagen (compressToJpeg es la única opción de conversión ofrecida por YuvImage):

     // pWidth and pHeight define the size of the preview Frame ByteArrayOutputStream out = new ByteArrayOutputStream(); // Alter the second parameter of this to the actual format you are receiving YuvImage yuv = new YuvImage(data, ImageFormat.NV21, pWidth, pHeight, null); // bWidth and bHeight define the size of the bitmap you wish the fill with the preview image yuv.compressToJpeg(new Rect(0, 0, bWidth, bHeight), 50, out); 

    Este JPEG puede entonces necesitar ser convertido en el formato que usted desea. Si desea un mapa de bits:

     byte[] bytes = out.toByteArray(); Bitmap bitmap= BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 

    Si, por alguna razón, no puede hacer esto, puede realizar la conversión manualmente. Algunos problemas a superar al hacer esto:

    1. Los datos llegan en una matriz de bytes. Por definición, los bytes son números firmados, lo que significa que van de -128 a 127. Sin embargo, los datos son realmente sin firmar bytes (0 a 255). Si esto no se trata, el resultado está condenado a tener algunos efectos de recorte impares.

    2. Los datos están en un orden muy específico (según la página web mencionada anteriormente) y cada píxel debe extraerse con cuidado.

    3. Cada píxel debe colocarse en el lugar correcto en un mapa de bits, digamos. Esto también requiere un enfoque bastante desordenado (en mi opinión) de construir un búfer de los datos y luego llenar un mapa de bits de él.

    4. Si usted realmente tiene NV12 (o 420SP), entonces tendrá que cambiar las lecturas de U y V.

    Presento una solución (que parece funcionar), con peticiones de correcciones, mejoras y formas de hacer todo menos costoso de ejecutar. Crea un mapa de bits del tamaño de la imagen de vista previa:

    La variable de datos viene de la llamada a onPreviewFrame

     // the bitmap we want to fill with the image Bitmap bitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888); int numPixels = imageWidth*imageHeight; // the buffer we fill up which we then fill the bitmap with IntBuffer intBuffer = IntBuffer.allocate(imageWidth*imageHeight); // If you're reusing a buffer, next line imperative to refill from the start, // if not good practice intBuffer.position(0); // Set the alpha for the image: 0 is transparent, 255 fully opaque final byte alpha = (byte) 255; // Get each pixel, one at a time for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { // Get the Y value, stored in the first block of data // The logical "AND 0xff" is needed to deal with the signed issue int Y = data[y*imageWidth + x] & 0xff; // Get U and V values, stored after Y values, one per 2x2 block // of pixels, interleaved. Prepare them as floats with correct range // ready for calculation later. int xby2 = x/2; int yby2 = y/2; // make this V for NV12/420SP float U = (float)(data[numPixels + 2*xby2 + yby2*imageWidth] & 0xff) - 128.0f; // make this U for NV12/420SP float V = (float)(data[numPixels + 2*xby2 + 1 + yby2*imageWidth] & 0xff) - 128.0f; // Do the YUV -> RGB conversion float Yf = 1.164f*((float)Y) - 16.0f; int R = (int)(Yf + 1.596f*V); int G = (int)(Yf - 0.813f*V - 0.391f*U); int B = (int)(Yf + 2.018f*U); // Clip rgb values to 0-255 R = R < 0 ? 0 : R > 255 ? 255 : R; G = G < 0 ? 0 : G > 255 ? 255 : G; B = B < 0 ? 0 : B > 255 ? 255 : B; // Put that pixel in the buffer intBuffer.put(alpha*16777216 + R*65536 + G*256 + B); } } // Get buffer ready to be read intBuffer.flip(); // Push the pixel information from the buffer onto the bitmap. bitmap.copyPixelsFromBuffer(intBuffer); 

    Como @Timmmm señala a continuación, podría hacer la conversión en int multiplicando los factores de escala por 1000 (es decir, 1.164 se convierte en 1164) y luego dividiendo los resultados finales por 1000.

    Después de algunas pruebas en Samsung S4 mini código más rápido es (120% más rápido que Neil [flota!] Y 30% más rápido que el original de Hitesh):

     static public void decodeYUV420SP(int[] rgba, byte[] yuv420sp, int width, int height) { final int frameSize = width * height; // define variables before loops (+ 20-30% faster algorithm o0`) int r, g, b, y1192, y, i, uvp, u, v; for (int j = 0, yp = 0; j < height; j++) { uvp = frameSize + (j >> 1) * width; u = 0; v = 0; for (i = 0; i < width; i++, yp++) { y = (0xff & ((int) yuv420sp[yp])) - 16; if (y < 0) y = 0; if ((i & 1) == 0) { v = (0xff & yuv420sp[uvp++]) - 128; u = (0xff & yuv420sp[uvp++]) - 128; } y1192 = 1192 * y; r = (y1192 + 1634 * v); g = (y1192 - 833 * v - 400 * u); b = (y1192 + 2066 * u); // Java's functions are faster then 'IFs' r = Math.max(0, Math.min(r, 262143)); g = Math.max(0, Math.min(g, 262143)); b = Math.max(0, Math.min(b, 262143)); // rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & // 0xff00) | ((b >> 10) & 0xff); // rgba, divide 2^10 ( >> 10) rgba[yp] = ((r << 14) & 0xff000000) | ((g << 6) & 0xff0000) | ((b >> 2) | 0xff00); } } } 

    La velocidad es comparable a YuvImage.compressToJpeg () con ByteArrayOutputStream como salida (30-50 ms para la imagen 640×480).

    Resultado: Samsung S4 mini (2×1.7GHz) no puede comprimir a JPEG / convertir YUV a RGB en tiempo real (640×480 a 30fps)

    La implementación de Java es 10 veces más lenta que la versión c, le sugiero que utilice la biblioteca GPUImage o simplemente mueva esta parte del código.

    Hay una versión android de GPUImage: https://github.com/CyberAgent/android-gpuimage

    Puede incluir esta biblioteca si usa gradle y llame al método: GPUImageNativeLibrary.YUVtoRBGA (inputArray, WIDTH, HEIGHT, outputArray);

    Comparo el tiempo, para una imagen de NV21 que es 960×540, uso sobre código de java, costó 200ms +, con la versión de GPUImage, apenas 10ms ~ 20ms.

    Corregir el fragmento de código anterior

     static public void decodeYUV420SP(int[] rgba, byte[] yuv420sp, int width, int height) { final int frameSize = width * height; int r, g, b, y1192, y, i, uvp, u, v; for (int j = 0, yp = 0; j < height; j++) { uvp = frameSize + (j >> 1) * width; u = 0; v = 0; for (i = 0; i < width; i++, yp++) { y = (0xff & ((int) yuv420sp[yp])) - 16; if (y < 0) y = 0; if ((i & 1) == 0) { // above answer is wrong at the following lines. just swap ***u*** and ***v*** u = (0xff & yuv420sp[uvp++]) - 128; v = (0xff & yuv420sp[uvp++]) - 128; } y1192 = 1192 * y; r = (y1192 + 1634 * v); g = (y1192 - 833 * v - 400 * u); b = (y1192 + 2066 * u); r = Math.max(0, Math.min(r, 262143)); g = Math.max(0, Math.min(g, 262143)); b = Math.max(0, Math.min(b, 262143)); // combine ARGB rgba[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) | 0xff); } } } 

    Pruebe RenderScript ScriptIntrinsicYuvToRGB, que viene con JellyBean 4.2 (Api 17+).

    https://developer.android.com/reference/android/renderscript/ScriptIntrinsicYuvToRGB.html

    En Nexus 7 (2013, JellyBean 4.3) una conversión de la imagen de 1920×1080 (vista previa completa de la cámara HD) dura unos 7 ms.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.