2011-03-11 18 views
30

He realizado algunas búsquedas en Google y no he podido encontrar suficiente información sobre este formato. Es el formato predeterminado para la vista previa de la cámara. ¿Alguien puede sugerir buenas fuentes de información al respecto y cómo extraer datos de una foto/imagen de vista previa con ese formato? Para ser más específico, necesito la imagen en blanco y negro extraída.Extracto de la imagen en blanco y negro del formato NV21 de la cámara Android

EDIT: Parece que el formato también se denomina YCbCr 420 Semi planar

+0

Comenzando con Android API nivel 17, la mejor solución es usar un RenderScript intrínseco - 'ScriptIntrinsicYuvToRGB'. Este RenderScript está incorporado, por lo que no tiene que escribir una línea de código tipo C. Consulte esta pregunta para obtener más detalles: [Cómo utilizar ScriptIntrinsicYuvToRGB convertir yuv a rgba] (http://stackoverflow.com/q/20358803/377657) – rwong

Respuesta

7

También tuve un montón de dolores de cabeza a causa de este formato de vista previa.

El mejor que pude encontrar son los siguientes:

http://www.fourcc.org/yuv.php#NV21

http://v4l2spec.bytesex.org/spec/r5470.htm

Parece que el componente Y es la primera anchura * altura bytes int la matriz que se obtiene.

enlaces Algunos más informativos:

http://msdn.microsoft.com/en-us/library/ms867704.aspx#yuvformats_yuvsampling

http://msdn.microsoft.com/en-us/library/ff538197(v=vs.85).aspx

Espero que esto ayude.

4

Los datos están en formato YUV420.
Si solo está interesado en el canal monocromático, es decir, "blanco y negro", este es el primer width x height bytes del búfer de datos que ya tiene.
El canal Y es el primer plano de imagen. Es exactamente el canal gris/intensidad/luminosidad, etc.

12

NV21 es básicamente YUV420 pero en lugar del formato planar donde Y, U y V tienen planos independientes, NV21 tiene 1 plano para Luma y segundo plano para Chroma. El formato se parece

YYYYYYYYYYYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYYYYYYYYYYY . . . .

VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU . . . . .

+0

Buena explicación – WillC

79

Desarrollé el siguiente código para convertir NV21 a RGB, y está funcionando.

/** 
* Converts YUV420 NV21 to RGB8888 
* 
* @param data byte array on YUV420 NV21 format. 
* @param width pixels width 
* @param height pixels height 
* @return a RGB8888 pixels int array. Where each int is a pixels ARGB. 
*/ 
public static int[] convertYUV420_NV21toRGB8888(byte [] data, int width, int height) { 
    int size = width*height; 
    int offset = size; 
    int[] pixels = new int[size]; 
    int u, v, y1, y2, y3, y4; 

    // i percorre os Y and the final pixels 
    // k percorre os pixles U e V 
    for(int i=0, k=0; i < size; i+=2, k+=2) { 
     y1 = data[i ]&0xff; 
     y2 = data[i+1]&0xff; 
     y3 = data[width+i ]&0xff; 
     y4 = data[width+i+1]&0xff; 

     u = data[offset+k ]&0xff; 
     v = data[offset+k+1]&0xff; 
     u = u-128; 
     v = v-128; 

     pixels[i ] = convertYUVtoRGB(y1, u, v); 
     pixels[i+1] = convertYUVtoRGB(y2, u, v); 
     pixels[width+i ] = convertYUVtoRGB(y3, u, v); 
     pixels[width+i+1] = convertYUVtoRGB(y4, u, v); 

     if (i!=0 && (i+2)%width==0) 
      i+=width; 
    } 

    return pixels; 
} 

private static int convertYUVtoRGB(int y, int u, int v) { 
    int r,g,b; 

    r = y + (int)(1.402f*v); 
    g = y - (int)(0.344f*u +0.714f*v); 
    b = y + (int)(1.772f*u); 
    r = r>255? 255 : r<0 ? 0 : r; 
    g = g>255? 255 : g<0 ? 0 : g; 
    b = b>255? 255 : b<0 ? 0 : b; 
    return 0xff000000 | (b<<16) | (g<<8) | r; 
} 

Esta imagen ayuda a comprender. YUV420 NV21

Si solo quieres una imagen en escala de grises es más fácil. Puedes descartar toda la información de U y V, y solo tomar la información de Y.El código se puede ser así:

/** 
* Converts YUV420 NV21 to Y888 (RGB8888). The grayscale image still holds 3 bytes on the pixel. 
* 
* @param pixels output array with the converted array o grayscale pixels 
* @param data byte array on YUV420 NV21 format. 
* @param width pixels width 
* @param height pixels height 
*/ 
public static void applyGrayScale(int [] pixels, byte [] data, int width, int height) { 
    int p; 
    int size = width*height; 
    for(int i = 0; i < size; i++) { 
     p = data[i] & 0xFF; 
     pixels[i] = 0xff000000 | p<<16 | p<<8 | p; 
    } 
} 

para crear su mapa de bits simplemente:

Bitmap bm = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); 

Dónde píxeles es su matriz int [].

+0

¿Cómo extraer el canal Cr de los bytes [] data? –

+0

@YuriyChernyshov Suponiendo que YUV = YCrCb, puede extraer el canal Cr adaptando el método convertYUV420_NV21toRGB8888 y poniendo los valores U en una matriz. ¿Pero por qué solo quieres el Cr? Preste atención a que U y V están entrelazados, el primer U1 de la imagen se usa con Y1, Y2, Y7, Y8. – Derzu

+0

Derzu, comienzo a conocer la detección de señales de tráfico. Y uno de los primeros pasos es tomar el canal Cr y comenzar a procesarlo: de forma adaptativa con dos umbrales para proporcionar aislamiento robusto del componente de escena roja. –

3

Aquí es código para simplemente extraer los datos de imagen en escala de grises:

private int[] decodeGreyscale(byte[] nv21, int width, int height) { 
    int pixelCount = width * height; 
    int[] out = new int[pixelCount]; 
    for (int i = 0; i < pixelCount; ++i) { 
     int luminance = nv21[i] & 0xFF; 
     out[i] = Color.argb(0xFF, luminance, luminance, luminance); 
    } 
    return out; 
} 
+0

Sam, ¿cómo podría eliminar el color rojo y azul del nv21? Quiero hacer que mi imagen sea verdosa (efecto de visión nocturna). – Nepster

+0

@Nepster, no estoy seguro de la parte superior de mi cabeza. Puede usar la luminancia como el valor verde haciendo este cambio en el código anterior: 'out [i] = Color.argb (0xFF, 0, luminancia, 0);'. O tal vez podría alterar el código de una implementación a todo color para descartar la información azul y roja. Creo que deberías hacer una nueva pregunta de StackOverflow para eso. – Sam

+0

Funciona Sam. Muchas gracias. Pero cuando abro Front Camera. Gira la imagen verticalmente (boca arriba) – Nepster

1

Cuando sólo se necesita una previa de la cámara en escala de grises, se puede utilizar una muy simple renderScript:

# pragma version(1) 
# pragma rs java_package_name(com.example.name) 
# pragma rs_fp_relaxed 

rs_allocation gIn; // Allocation filled with camera preview data (byte[]) 
int previewwidth; // camera preview width (int) 

// the parallel executed kernel 
void root(uchar4 *v_out, uint32_t x,uint32_t y){ 
    uchar c = rsGetElementAt_uchar(gIn,x+y*previewwidth); 
    *v_out = (uchar4){c,c,c,255}; 
} 

Nota: Este es no más rápido que ScriptIntrinsicYuvToRGB (y una siguiente ScriptIntrinsicColorMatrix para hacer RGBA-> gris), pero funciona con API 11+ (donde los Intrinsics necesitan Api 17+).

Cuestiones relacionadas