C# 不同于C++的位图中的C-RGB缓冲区

C# 不同于C++的位图中的C-RGB缓冲区,c#,.net,image-processing,bitmap,C#,.net,Image Processing,Bitmap,我正在使用一个第三方DLL,它的参数是RGB缓冲区 我使用以下代码从位图读取RGB缓冲区: 问题是生成的RGB缓冲区不正确。如果我在IrfanView中打开此缓冲区,并提供正确的参数,则生成的图像不正确,看起来像是移位了 如果得到一个用C++代码读取的缓冲区,它就工作了。 我注意到bmpData.Stride比我预期的宽度*通道大1个单位。我知道.NET使用4字节对齐 问题是:为什么RGB缓冲区不正确 一定要注意顺序是B-G-R而不是R-G-B 您可以尝试将值转换为uint的不安全代码。因此,您

我正在使用一个第三方DLL,它的参数是RGB缓冲区

我使用以下代码从位图读取RGB缓冲区:

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

如果得到一个用C++代码读取的缓冲区,它就工作了。 我注意到bmpData.Stride比我预期的宽度*通道大1个单位。我知道.NET使用4字节对齐


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

一定要注意顺序是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;
   }
}

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

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

内存中的位图颜色通道以蓝色、绿色、红色和Alpha的顺序表示,尽管通常使用缩写词ARGB!

你注意到了,对-你需要考虑步幅。通常,您不能在一次复制调用中简单地复制图像。步幅包括行长和填充,可以大于行长。所以您只需要从每一行复制所需的字节,忽略填充字节,并通过添加跨步前进到下一行

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

-原始图像和预期结果

-没有跨步的结果无效

以下是工作代码:

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;
}
您可以在此答案中阅读更多详细信息:

此外,此图像可能有助于您了解STERE的用途:


从位图中获取字节时,需要跳过右侧的白色区域。

…为什么RGB缓冲区不正确?。rgb缓冲区是正确的。我知道.NET使用4字节对齐。不是.net使用4字节对齐。它自己使用的图像。C++或.NET提供相同的缓冲区。不要这么快下结论。我提供给这个方法的位图是24bpp,宽度=897,高度=1281。为什么步幅值是2692而不是2691?如果我将生成的缓冲区保存到文件并在IrfanView上打开,则图像不正确。为什么?你不是在访问一个只有像素的缓冲区,你是在访问一个为快速访问而优化的图像的内存表示,两者是不同的。正如您所观察到的,内存中的表示形式将对对齐和类似操作进行优化,这意味着步幅可以大于图像宽度。换句话说,如果您以后试图将该字节数组解释为一组897*1281的3字节像素,您将遇到问题。如果您只想获取像素字节,请循环每行,将每行开头的宽度*3字节复制到memorystream或类似文件中,并在下一次复制之前跳过跨步字节,如果跨步>宽度*3,则忽略末尾的最后一个字节。这应该会给你一些与其他程序更相似的东西。有时,一张图片上会显示超过1000k:-最后,我可以理解整个STERE程序是关于什么的!附言:我现在用的是一个RGE,它可以处理大部分这些事情。
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;
}