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

前端 未结 6 1733
你的背包
你的背包 2020-11-28 02:18

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 i

相关标签:
6条回答
  • 2020-11-28 02:30

    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 . . . . .

    0 讨论(0)
  • 2020-11-28 02:34

    When you only need a grayscale camera preview, you could use a very 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};
    }
    

    Note : This is not faster than ScriptIntrinsicYuvToRGB (and a following ScriptIntrinsicColorMatrix to do the RGBA-> gray), but it runs with API 11+ (where the Intrinsics need Api 17+).

    0 讨论(0)
  • 2020-11-28 02:35

    I also had lots of headache because of this preview format.

    The best I could find are these:

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

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

    It seems that the Y component is the first width*height bytes int the array you get.

    Some more informational links:

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

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

    Hope this helps.

    0 讨论(0)
  • 2020-11-28 02:39

    The data is in YUV420 format.
    If you are only interested in the monochrome channel, i.e. "black and white", then this the first width x height bytes of the data buffer you already have.
    The Y channel is the first image plane. It is exactly the grey/intensity/luminosity etc. channel.

    0 讨论(0)
  • 2020-11-28 02:41

    Here's code to just extract the greyscale image data:

    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 讨论(0)
  • 2020-11-28 02:45

    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.

    0 讨论(0)
提交回复
热议问题