Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract black and white image from android camera's NV21 format

I have done some google-ing around and couldn't find enough information about this format. It is the default format for camera preview. Can anyone suggest good sources of information about it and how to extract data from a photo/preview image with that format? To be more specific, I need the black and white image extracted.

EDIT: Seems like that format is also called YCbCr 420 Semi Planar

like image 773
Ethan Avatar asked Mar 11 '11 11:03

Ethan


2 Answers

I developed the following code to convert the NV21 to RGB, and it is working.

/**  * 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; } 

This image helps to understand. YUV420 NV21

If you wanna just grayscale image is easer. You can discard all the U and V info, and take just the Y info. The code would can be like this:

/**  * 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;     } } 

To create your Bitmap just:

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

Where pixels is your int [] array.

like image 151
Derzu Avatar answered Oct 07 '22 23:10

Derzu


NV21 is basically YUV420 but instead of planar format where Y, U and V have independent planes, NV21 has 1 plane for Luma and 2nd plane for Chroma. The format looks like

YYYYYYYYYYYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYYYYYYYYYYY . . . .

VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU . . . . .

like image 23
Mayank Avatar answered Oct 08 '22 01:10

Mayank