【问题标题】:Greyscale Image from YUV420p data来自 YUV420p 数据的灰度图像
【发布时间】:2021-01-13 03:41:35
【问题描述】:

根据我在互联网上阅读的内容,Y 值是亮度值,可用于创建灰度图像。以下链接:https://web.archive.org/web/20141230145627/http://bobpowell.net/grayscale.aspx,有一些 C# 代码用于计算位图图像的亮度:

{
  Bitmap bm = new Bitmap(source.Width,source.Height);
  for(int y=0;y<bm.Height;y++)        public Bitmap ConvertToGrayscale(Bitmap source)
  {
    for(int x=0;x<bm.Width;x++)
    {
      Color c=source.GetPixel(x,y);
      int luma = (int)(c.R*0.3 + c.G*0.59+ c.B*0.11);
      bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma));
    }
  }
  return bm;
}

我有一个方法可以返回 YUV 值并将 Y 数据放在一个字节数组中。我有当前的一段代码,但它在 Marshal.Copy 上失败 - 试图读取或写入受保护的内存。

public Bitmap ConvertToGrayscale2(byte[] yuvData, int width, int height)
        {
            Bitmap bmp;
            IntPtr blue = IntPtr.Zero;

            int inputOffSet = 0;
            long[] pixels = new long[width * height];

            try
            {
                for (int y = 0; y < height; y++)
                {
                    int outputOffSet = y * width;
                    for (int x = 0; x < width; x++)
                    {
                        int grey = yuvData[inputOffSet + x] & 0xff;
                        unchecked
                        {
                            pixels[outputOffSet + x] = UINT_Constant | (grey * INT_Constant);
                        }
                    }

                    inputOffSet += width;
                }
                
                blue = Marshal.AllocCoTaskMem(pixels.Length);
                Marshal.Copy(pixels, 0, blue, pixels.Length); // fails here : Attempted to read or write protected memory


                bmp = new Bitmap(width, height, width, PixelFormat.Format24bppRgb, blue);

            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                if (blue != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(blue);
                    blue = IntPtr.Zero;
                }

            }

            return bmp;
        }

任何帮助将不胜感激?

【问题讨论】:

    标签: image image-processing yuv grayscale marshalling


    【解决方案1】:

    我认为你已经分配了pixels.Length bytes,但是正在复制pixels.Length longs,这是内存的8倍(long是64位或8字节大小)。

    你可以试试:

    blue = Marshal.AllocCoTaskMem(Marshal.SizeOf(pixels[0]) * pixels.Length); 
    

    您可能还需要对像素使用 int[] 并在 Bitmap 构造函数中使用 PixelFormat.Format32bppRgb(因为它们都是 32 位)。使用 long[] 可为您提供每像素 64 位,这不是 24 位像素格式所期望的。

    您最终可能会得到蓝色而不是灰色的阴影 - 取决于您的 UINT_Constant 和 INT_Constant 的值。

    不需要"& 0xff",因为yuvData[]已经包含了一个字节。

    【讨论】:

    • 我已将像素更改为 int[] 并按照建议更改了 pixelFormat。现在我得到:无法将类型“long”隐式转换为“int”。我猜我应该将这些更改为其他内容: private const uint UINT_Constant = 0xFF000000; private const int INT_Constant = 0x00010101;
    • 像素[outputOffSet + x] = UINT_Constant | (灰色 * INT_Constant);
    • 试试这个:像素[outputOffSet + x] = (int)(UINT_Constant | (grey * INT_Constant))。另外我建议将 INT_Constant 也设为 uint,以删除按位或警告。
    • 我得到了 StackOverflowException。 UINT_Constant 被转换为 4278190080
    • 如果我将 unchecked 更改为已选中,那么我在该行上得到一个 OverflowException(不是 StackOverflowException),但是使用您的代码,如图所示,我没有在该行上得到一个异常。还是您在其他地方收到 StackOverflowException 异常?理想情况下,像素应该是 uint[] 而不是 int[],但没有 Marshal.Copy() 覆盖需要 uint[]。另一种选择是将像素改为字节数组。
    【解决方案2】:

    您可以尝试以下几种方法。

    public Bitmap ConvertToGrayScale(byte[] yData, int width, int height)
    {
        // 3 * width bytes per scanline, rounded up to a multiple of 4 bytes
        int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
    
        byte[] pixels = new byte[stride * height];
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                byte grey = yData[y * width + x];
                pixels[y * stride + 3 * x] = grey;
                pixels[y * stride + 3 * x + 1] = grey;
                pixels[y * stride + 3 * x + 2] = grey;
            }
        }
    
        IntPtr pixelsPtr = Marshal.AllocCoTaskMem(pixels.Length);
        try
        {
            Marshal.Copy(pixels, 0, pixelsPtr, pixels.Length);
    
            Bitmap bitmap = new Bitmap(
                width, 
                height, 
                stride, 
                PixelFormat.Format24bppRgb, 
                pixelsPtr);
            return bitmap;
        }
        finally
        {
            Marshal.FreeHGlobal(pixelsPtr);
        }
    }
    
    public Bitmap ConvertToGrayScale(byte[] yData, int width, int height)
    {
        // 3 * width bytes per scanline, rounded up to a multiple of 4 bytes
        int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
    
        IntPtr pixelsPtr = Marshal.AllocCoTaskMem(stride * height);
        try
        {
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    byte grey = yData[y * width + x];
                    Marshal.WriteByte(pixelsPtr, y * stride + 3 * x, grey);
                    Marshal.WriteByte(pixelsPtr, y * stride + 3 * x + 1, grey);
                    Marshal.WriteByte(pixelsPtr, y * stride + 3 * x + 2, grey);
                }
            }
    
            Bitmap bitmap = new Bitmap(
                width,
                height,
                stride,
                PixelFormat.Format24bppRgb,
                pixelsPtr);
            return bitmap;
        }
        finally
        {
            Marshal.FreeHGlobal(pixelsPtr);
        }
    }
    

    【讨论】:

    • 两个例子都试过了。一段时间后出现“AccessViolationException”,主要发生在我调整表单大小时,在这两个示例中。 "试图读取或写入受保护的内存。显示的图像是灰度睡衣条纹。
    • 您在哪一行得到访问冲突?
    【解决方案3】:

    如果我使用此代码,我会在左上角得到一个带有几个像素的黑色图像,并且在运行时这是稳定的:

     public static Bitmap ToGrayscale(byte[] yData, int width, int height)
        {
            Bitmap bm = new Bitmap(width, height, PixelFormat.Format32bppRgb);
            Rectangle dimension = new Rectangle(0, 0, bm.Width, bm.Height);
            BitmapData picData = bm.LockBits(dimension, ImageLockMode.ReadWrite, bm.PixelFormat);
            IntPtr pixelStateAddress = picData.Scan0;
    
            int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
            byte[] pixels = new byte[stride * height];
    
            try
            {
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        byte grey = yData[y * width + x];
                        pixels[y * stride + 3 * x] = grey;
                        pixels[y * stride + 3 * x + 1] = grey;
                        pixels[y * stride + 3 * x + 2] = grey;
    
                    }
                }
    
                Marshal.Copy(pixels, 0, pixelStateAddress, pixels.Length);
                bm.UnlockBits(picData);
            }
            catch (Exception)
            {
                throw;
            }
    
            return bm;
        }
    

    【讨论】:

    • 使用 LockBits() 时,您应该使用 picData.Stride 而不是自己计算。还要注意它可以是负数(在这种情况下 Scan0 指向最后一个扫描线)。同样对于 3 * x 乘数,您应该使用 Format24bppRb 而不是 32。
    • 好的,原来我没有得到正确的 Y 数据,所以现在已经修复,我现在使用上面的代码得到一个灰度图像。谢谢你帮助撒克逊人。
    猜你喜欢
    • 2015-02-25
    • 1970-01-01
    • 2013-04-23
    • 1970-01-01
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    • 2014-09-27
    • 2020-05-23
    相关资源
    最近更新 更多