C# - RGB-буфер из Bitmap, отличный от C++

Я использую стороннюю DLL, которая имеет в качестве параметра буфер RGB.

Я использовал следующий код для чтения буфера RGB из Bitmap:

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 некорректен?

3 ответа

Вы правильно заметили - нужно брать 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;
}

Подробнее вы можете прочитать в этом ответе: преобразование байтового массива в изображение

Также, возможно, это изображение поможет вам понять Stride цель:

длина строки против шага

Вам нужно пропустить белую область справа, когда вы получаете байты от Bitmap,

Убедитесь, что вы смотрите, что порядок BGR вместо RGB. Вы можете попробовать этот небезопасный код, который преобразует значения в 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#.

Цветовые каналы растрового изображения в памяти представлены в следующем порядке: синий, зеленый, красный и альфа, несмотря на то, что на них обычно ссылаются как аббревиатура ARGB! http://softwarebydefault.com/2013/03/22/bitmap-swap-argb/

Другие вопросы по тегам