Search code examples
androidimagebitmapargb

NV21 to Bitmap on Android, Very dark image, grayscale, or yellow tint?


I have been looking at converting the NV21 byte[] that I get from onPreviewFrame(). I have searched the forums and google for various solutions. I have tried RenderScripts and some other code examples. Some of them give me an image with a yellow tint, some give me an image with red and blue flipped (after I flip it back in the code, I get yellow tint back), some give me strange color features all throughout the image (almost like a negative), some give me a grayscale image, some give me an image so dark you can't really make anything out.

Since I am the one typing the question, I realize I must be the idiot in the room so we will start with this post. This particular solution gives me a very dark image, but I am not cool enough to be able to comment yet. Has anyone tried this solution or has one that produces an image with the same quality as the original NV21 format?

I need either a valid ARGB byte[] or a valid Bitmap, I can modify my project to deal with either. Just for reference I have tried these (and a few others that are really just carbon copies of these):

One solution I tried
Another solution I tried


Solution

  • If you are trying to convert YUV from camera to Bitmap, here is something you can try:

    // import android.renderscript.*
    // RenderScript mRS;
    // ScriptIntrinsicYuvToRGB mYuvToRGB;
    // Allocation yuvPreviewAlloc;
    // Allocation rgbOutputAlloc;
    
    // Create RenderScript context, ScriptIntrinsicYuvToRGB and Allocations and keep reusing them.
    if (NotInitialized) {
        mRS = RenderScript.create(this).
        mYuvToRGB = ScriptIntrinsicYuvToRGB.create(mRS, Element.YUV(mRS));    
    
        // Create a RS Allocation to hold NV21 data.
        Type.Builder tYuv = new Type.Builder(mRS, Element.YUV(mRS));
        tYuv.setX(width).setY(height).setYuvFormat(android.graphics.ImageFormat.NV21);
        yuvPreviewAlloc = Allocation.createTyped(mRS, tYuv.create(), Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT);
    
        // Create a RS Allocation to hold RGBA data.
        Type.Builder tRgb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
        tRgb.setX(width).tRgb(height);
        rgbOutputAlloc = Allocation.createTyped(mRS, tRgb.create(), Allocation.USAGE_SCRIPT);
    
        // Set input of ScriptIntrinsicYuvToRGB
        mYuvToRGB.setInput(yuvPreviewAlloc);
    }
    
    // Use rsPreviewSurface as one of the output surface from Camera API.
    // You can refer to https://github.com/googlesamples/android-HdrViewfinder/blob/master/Application/src/main/java/com/example/android/hdrviewfinder/HdrViewfinderActivity.java#L504
    Surface rsPreviewSurface = yuvPreviewAlloc.getSurface();
    ...
    
    // Whenever a new frame is available
    // Update the yuv Allocation with a new Camera buffer without any copy.
    // You can refer to https://github.com/googlesamples/android-HdrViewfinder/blob/master/Application/src/main/java/com/example/android/hdrviewfinder/ViewfinderProcessor.java#L109
    yuvPreviewAlloc.ioReceive();
    // The actual Yuv to Rgb conversion.
    mYuvToRGB.forEach(rgbOutputAlloc);
    
    // Copy the rgb Allocation to a Bitmap.
    rgbOutputAlloc.copyTo(mBitmap);
    
    // continue processing mBitmap.
    ...