Ladislav
Ladislav

Reputation: 349

Android Camera2 YUV_420_888 image to RGBA Bitmap conversion produces a greenish image

On my Samsung Galaxy A54 5G device with Android 14 I am trying the camera2 API and to take image in YUV 420 format. Device only supports JPEG and YUV 420. I tried many of methods to convert from YUV 420 to RGBA bitmap and display/save it but no matter which method, all of them works more or less and returns a correct but a greenish image. Among others I tried the below methods:

As mentioned earlier, all of the above methods are returning a greenish (or yellowish) image, where the greenish is visible mostly on white/gray areas, like the below example taken with my dev app: something

What I would expect as a correct conversion result is something very similar to the below reference screenshot: anything

Update #1: Tried some of the available codes & examples on a Huawei device and there are the colors correct! For example the simplest way using ScriptIntrinsicYuvToRGB gives correct colors on the Huawei device but returns greenish image on Samsung. Apparently it's a weird Samsung behaviour.

Update #2: The HDRViewFinder demo available in google camera samples gives correct color but unfortunately it's renderscript part I can't use on Xamarin.Android platform. Is it possible to somehow rewrite it's hdrmerge code to a native Android code?

Upvotes: 1

Views: 458

Answers (2)

Ladislav
Ladislav

Reputation: 349

It took me ages to figure out but problem finally solved!

In the camera capture request settings before capture I had to change the WhiteBalance settings from Auto to Fluorescent and the green tint from final image is gone!

Upvotes: 0

Ladislav
Ladislav

Reputation: 349

Still not the 100% statisfying solution but at least it gives much much better colors after the YUV -> RGB conversion. Plus it is slow for very high resolution images. The below code is a combination of this answer and this renderscript kernel:

case ImageFormatType.Yuv420888:
    var planes = img.GetPlanes();
    var yPlane = planes[0];
    var uPlane = planes[1];
    var vPlane = planes[2];

    var rgbBytes = new int[img.Height * img.Width];
    var idx = 0;

    var yBuffer = yPlane.Buffer;
    var yPixelStride = yPlane.PixelStride;
    var yRowStride = yPlane.RowStride;

    var uBuffer = uPlane.Buffer;
    var uPixelStride = uPlane.PixelStride;
    var uRowStride = uPlane.RowStride;

    var vBuffer = vPlane.Buffer;
    var vPixelStride = vPlane.PixelStride;
    var vRowStride = vPlane.RowStride;

    for (var row = 0; row < img.Height; row++) {
        for (var col = 0; col < img.Width; col++) {
            var Y = (byte)yBuffer.Get(col * yPixelStride + row * yRowStride);
            var U = (byte)uBuffer.Get(col / 2 * uPixelStride + row / 2 * uRowStride);
            var V = (byte)vBuffer.Get(col / 2 * vPixelStride + row / 2 * vRowStride);

            // Convert YUV to RGB, JFIF transform with fixed-point math.
            // Formulas can be found here: https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion
            // R = Y + 1.402 * (V - 128)
            // G = Y - 0.34414 * (U - 128) - 0.71414 * (V - 128)
            // B = Y + 1.772 * (U - 128)
            var R = Y + 1436 / 1024 * (V - 128);
            var G = Y - 46549 / 131072 * (U - 128) - 93604 / 131072 * (V - 128);
            var B = Y + 1814 / 1024 * (U - 128);

            R = MathUtils.Clamp(R, 0, 255);
            G = MathUtils.Clamp(G, 0, 255);
            B = MathUtils.Clamp(B, 0, 255);

            rgbBytes[idx++] = BitConverter.ToInt32(new byte[] { (byte)B, (byte)G, (byte)R, 0xFF }, 0);
        }
    }

    FinalBitmap = Bitmap.CreateBitmap(rgbBytes, img.Width, img.Height, Bitmap.Config.Argb8888);
    img.Close();
    break;

Upvotes: 1

Related Questions