从Yuv 420转RGB再转Bitmap

Convert from Yuv 420 to RGB and then to Bitmap

我在将图像从 byte YUV420p[] 转换为 byte RGB[] 然后再转换为 Bitmap 时遇到问题。

这是我正在使用的从 YUV 转换为 RGB 的方法:

    double[,] YUV2RGB_CONVERT_MATRIX = new double[3, 3] 
    {
        { 1, 0, 1.4022 }, 
        { 1, -0.3456, -0.7145 }, 
        { 1, 1.771, 0 } 
    };
    
    unsafe Bitmap ConvertYUV2RGB(byte[] YUVFrame, int width, int height)
    {
        int uIndex = width * height;
        int vIndex = uIndex + ((width * height) >> 2);
        int gIndex = width * height;
        int bIndex = gIndex * 2;
        byte[] rgbFrame = new byte[uIndex * 3];

        //图片为pic1,RGB颜色的二进制数据转换得的int r,g,b;
        Bitmap bitmap = new Bitmap(width, height);

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                // R分量
                int temp = (int)(YUVFrame[y * width + x] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[0, 2]);
                rgbFrame[y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                // G分量
                temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 1] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 2]);
                rgbFrame[gIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                // B分量
                temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[2, 1]);
                rgbFrame[bIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                System.Drawing.Color c = System.Drawing.Color.FromArgb(RGBFrame[y * width + x], RGBFrame[gIndex + y * width + x], RGBFrame[bIndex + y * width + x]);
                bitmap.SetPixel(x, y, c);

            }
        }

        return bitmap;
    }

该功能 100% 有效,但由于显而易见的原因它非常慢:

                System.Drawing.Color c = System.Drawing.Color.FromArgb(RGBFrame[y * width + x], RGBFrame[gIndex + y * width + x], RGBFrame[bIndex + y * width + x]);
                bitmap.SetPixel(x, y, c);

这是生成的图像:

因此,为了避免在循环内调用 bitmap.SetPixel(x, y, c),我将代码更改为:

    unsafe Bitmap ConvertYUV2RGB(byte[] YUVFrame, int width, int height)
    {
        int uIndex = width * height;
        int vIndex = uIndex + ((width * height) >> 2);
        int gIndex = width * height;
        int bIndex = gIndex * 2;
        byte[] RGBFrame = new byte[uIndex * 3];

        //图片为pic1,RGB颜色的二进制数据转换得的int r,g,b;
        //Bitmap bitmap = new Bitmap(width, height);

        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                // R分量
                int temp = (int)(YUVFrame[y * width + x] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[0, 2]);
                RGBFrame[y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                // G分量
                temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 1] + (YUVFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[1, 2]);
                RGBFrame[gIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                // B分量
                temp = (int)(YUVFrame[y * width + x] + (YUVFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * YUV2RGB_CONVERT_MATRIX[2, 1]);
                RGBFrame[bIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                // Commented to avoid calling functions from inside the for loop
                // System.Drawing.Color c = System.Drawing.Color.FromArgb(RGBFrame[y * width + x], RGBFrame[gIndex + y * width + x], RGBFrame[bIndex + y * width + x]);
                // bitmap.SetPixel(x, y, c);
            }
        }

        return CreateBitmap(RGBFrame, width, height);
    }

    private Bitmap CreateBitmap(byte[] RGBFrame, int width, int height)
    {
        PixelFormat pxFormat = PixelFormat.Format24bppRgb;
        Bitmap bmp = new Bitmap(width, height, pxFormat);
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, pxFormat);

        IntPtr pNative = bmpData.Scan0;
        Marshal.Copy(RGBFrame, 0, pNative, RGBFrame.Length);
        bmp.UnlockBits(bmpData);
        return bmp;
    }

但是我无法正确创建图像。这是结果:

这是怎么回事?

我终于用这个函数解决了我的问题:

    unsafe Bitmap ConvertYUV2RGB(byte[] YUVFrame, int width, int height)
    {
        int numOfPixel = width * height;
        int positionOfV = numOfPixel;
        int positionOfU = numOfPixel / 4 + numOfPixel;
        byte[] rgb = new byte[numOfPixel * 3];

        int R = 0;
        int G = 1;
        int B = 2;

        for (int i = 0; i < height; i++)
        {
            int startY = i * width;
            int step = (i / 2) * (width / 2);
            int startU = positionOfU + step;
            int startV = positionOfV + step;

            for (int j = 0; j < width; j++)
            {
                int Y = startY + j;
                int U = startU + j / 2;
                int V = startV + j / 2;
                int index = Y * 3;

                double r = ((YUVFrame[Y] & 0xff) + 1.4075 * ((YUVFrame[V] & 0xff) - 128));
                double g = ((YUVFrame[Y] & 0xff) - 0.3455 * ((YUVFrame[U] & 0xff) - 128) - 0.7169 * ((YUVFrame[V] & 0xff) - 128));
                double b = ((YUVFrame[Y] & 0xff) + 1.779 * ((YUVFrame[U] & 0xff) - 128));

                r = (r < 0 ? 0 : r > 255 ? 255 : r);
                g = (g < 0 ? 0 : g > 255 ? 255 : g);
                b = (b < 0 ? 0 : b > 255 ? 255 : b);

                rgb[index + R] = (byte)r;
                rgb[index + G] = (byte)g;
                rgb[index + B] = (byte)b;
            }
        }

        return CreateBitmap(rgb, width, height);
    }

并从 RGB[]:

创建一个 Bitmap
    Bitmap CreateBitmap(byte[] RGBFrame, int width, int height)
    {
        PixelFormat pxFormat = PixelFormat.Format24bppRgb;
        Bitmap bmp = new Bitmap(width, height, pxFormat);
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, pxFormat);

        IntPtr pNative = bmpData.Scan0;
        Marshal.Copy(RGBFrame, 0, pNative, RGBFrame.Length);
        bmp.UnlockBits(bmpData);
        return bmp;
    }

或 WPF 的 BitmapSource

    BitmapSource FromArray(byte[] data, int w, int h, int ch)
    {
        System.Windows.Media.PixelFormat format = PixelFormats.Default;

        if (ch == 1)
            format = PixelFormats.Gray8; //grey scale image 0-255
        if (ch == 3)
            format = PixelFormats.Bgr24; //RGB
        if (ch == 4)
            format = PixelFormats.Bgr32; //RGB + alpha

        WriteableBitmap wbm = new WriteableBitmap(w, h, 96, 96, format, null);
        wbm.WritePixels(new Int32Rect(0, 0, w, h), data, ch * w, 0);
        return wbm;
    }