【问题标题】:C# - RGB Buffer from Bitmap different from C++C# - 来自位图的 RGB 缓冲区与 C++ 不同
【发布时间】:2014-10-02 19:17:23
【问题描述】:

我正在使用第三方 DLL,其参数为 RGB 缓冲区。

我使用下面的代码从Bitmap读取RGB缓冲区:

private byte[] GetBGRValues(Bitmap bmp)
    {

        // Lock the bitmap's bits. 
        Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        // Get the address of the first line.
        IntPtr ptr = bmpData.Scan0;
        // Declare an array to hold the bytes of the bitmap.
        int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
        byte[] rgbValues = new byte[bytes];


        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); 
        bmp.UnlockBits(bmpData);

        return rgbValues;
    }

问题是生成的 RGB 缓冲区不正确。如果我在 IrfanView 中打开此缓冲区,并提供正确的参数,则生成的图像不正确(看起来像是移位了)。

如果得到一个我使用 C++ 代码读取的缓冲区,它就可以工作。

我注意到bmpData.Stride 比我预期的大 1 个单位 (width * channels)。 (我知道 .NET 使用 4 字节对齐)。

问题是:为什么 RGB 缓冲区不正确?

【问题讨论】:

  • ...为什么 RGB 缓冲区不正确?。 rgb 缓冲区是正确的。 (我知道 .NET 使用 4 字节对齐)。使用 4 字节对齐的不是 .net。它自己使用的图像。 c++ 或 .net 提供相同的缓冲区。不要这么快下结论。
  • 我提供给这个方法的位图是 24bpp,宽度 = 897,高度 = 1281。为什么步幅值是 2692 而不是 2691?如果我将生成的缓冲区保存到文件并在 IrfanView 上打开,则图像不正确。为什么?
  • 您访问的不是只有像素的缓冲区,而是访问为快速访问而优化的图像的内存表示,两者是不同的。正如您所观察到的,内存中的表示将对 alighment 等进行优化,这意味着步幅可以大于图像宽度。换句话说,如果您之后尝试将该字节数组解释为 897*1281 的 3 字节像素集,您将会遇到问题。
  • 如果你只想获取像素字节,遍历每一行,从每行的开头复制宽度*3字节到内存流(或类似),并在下一个副本,如果 Stride > Width*3,则忽略末尾的最后一个字节。这应该会给你一些更类似于其他程序的东西。

标签: c# .net image-processing bitmap


【解决方案1】:

您没看错 - 您需要考虑 Stride。一般来说,您不能简单地在一个Copy 调用中复制图像。 Stride 包括行长和填充,并且可能大于行长。所以你只需要从每一行复制你需要的字节,忽略填充字节并通过添加Stride前进到下一行。

我猜这就是你在代码中看到的:

- 原始图像和预期结果

- 没有跨度的无效结果

这是工作代码:

public static byte[] GetBGRValues(Bitmap bmp)
{
    var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);

    var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
    var imgBytes = bmp.Height * rowBytes;
    byte[] rgbValues = new byte[imgBytes];

    var ptr = bmpData.Scan0;
    for (var i = 0; i < bmp.Height; i++)
    {
        Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);
        ptr += bmpData.Stride; // next row
    }

    bmp.UnlockBits(bmpData);

    return rgbValues;
}

您可以在此答案中阅读更多详细信息:Byte Array to Image Conversion

也许这张图片会帮助你理解Stride的目的:

当你从Bitmap获取字节时,你需要跳过右边的白色区域。

【讨论】:

  • 有时,一张图片说超过 1000k :-) 最后,我可以理解整个“步幅”是什么意思了! P.S.:我现在使用 AForge,它可以处理大部分这些事情。
【解决方案2】:

请务必注意顺序是 B-G-R 而不是 R-G-B 您可以尝试将值转换为 uint 的不安全代码。所以你已经将 RGB 转换为 uint

/// <summary>
///     Locks a Bitmap into system memory.
/// </summary>
public unsafe void LockBits()
{
    var width = this.Bitmap.Width;
    var height = this.Bitmap.Height;

    var imageLockMode = ImageLockMode.UserInputBuffer;

    // Setting imageLockMode
    imageLockMode = imageLockMode | ImageLockMode.ReadOnly;
    imageLockMode = imageLockMode | ImageLockMode.WriteOnly;

    // Save the bouunds
    this._bounds = new Rectangle(0, 0, width, height);

    // Create Pointer
    var someBuffer = new uint[width*height];
    // Pin someBuffer
    fixed (uint* buffer = someBuffer) //pin
    {
        // Create new bitmap data.
        var temporaryData = new BitmapData
        {
            Width = width,
            Height = height,
            PixelFormat = PixelFormat.Format32bppArgb,
            Stride = width*4,
            Scan0 = (IntPtr) buffer
        };

        // Get the data
        this.BitmapData = this.Bitmap.LockBits(this._bounds, imageLockMode,     PixelFormat.Format32bppArgb,
        temporaryData);
        // Set values
        this.Buffer = someBuffer;
   }
}

【讨论】:

    【解决方案3】:

    我记得几年前我在工作的一个库 - 颜色发生了奇怪的变化。底层的 Microsoft 库有一个(功能),即 RGB 在库中已被反转 - 我们不知道的是,我们尝试了转换并发现了那个小宝石。

    也不要忘记 C# 缓冲区中的 Alpha 通道。

    内存中的位图颜色通道按蓝色、绿色、红色和 Alpha 的顺序表示,尽管通常缩写为 ARGB! http://softwarebydefault.com/2013/03/22/bitmap-swap-argb/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-10
      • 2020-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 2018-02-27
      相关资源
      最近更新 更多