3

I'm using a third party DLL which has as parameter a RGB buffer.

I have used the following code to read RGB buffer from 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;
    }

The problem is that the generated RGB buffer is not correct. If I open this buffer in IrfanView, supplying correct parameters, the generated image is not correct (looks like it is shifted).

If a get a buffer that I read using C++ code it works.

I have noticed that bmpData.Stride is 1 unity greater than what I was expecting (width * channels). (I know that .NET uses 4 bytes alignment).

The question is: why is the RGB buffer not correct?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
user1862876
  • 171
  • 2
  • 9
  • *...why the RGB buffer is not correct?*. The rgb buffer is correct. *(I know that .NET uses 4bytes alignment)*. It is not .net that uses 4 bytes alignment. The image it self uses it. c++ or .net gives the same buffer. Don't jump to conclusions so fast. – γηράσκω δ' αεί πολλά διδασκόμε Oct 02 '14 at 22:21
  • The bitmap I supply to this method is 24bpp and has width = 897 and height = 1281. Why stride value is 2692 and not 2691? If I save the generated buffer to file and open on IrfanView, the image is not correct. Why? – user1862876 Oct 02 '14 at 23:32
  • You're not accessing a buffer of only the pixels, you're accessing an in-memory representation of the image optimized for fast access, the two are different. As you have observed, the in-memory representation will have optimizations for alighment and similar which means stride can be bigger than the image width. In other words, if you afterwards try to interpret that byte array as a 897*1281 set of 3-byte pixels, you're going to have problems. – Lasse V. Karlsen Nov 01 '16 at 08:52
  • If you want to get only the pixel bytes, loop through each line, copy width*3 bytes from the start of each line into a memorystream (or similar), and skip "Stride" bytes before the next copy, ignore the last bytes at the end if Stride > Width*3. That should give you something more akin to other programs. – Lasse V. Karlsen Nov 01 '16 at 08:55

3 Answers3

5

You noticed right - you need to take Stride into account. In general you cannot simply copy image in one Copy call. Stride include both row length and padding and could be greater then row length. So you need to copy only bytes you need from each row, ignore padding bytes and advance to next row by adding Stride.

I guess this is what you see with your code:

original and valid result - original image and expected result

invalid result - invalid result without stride

Here is working code:

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;
}

More details you can read in this answer: Byte Array to Image Conversion

Also maybe this image will help you to understand Stride purpose:

row length vs stride

You need to skip white area at the right when you getting bytes from Bitmap.

lorond
  • 3,856
  • 2
  • 37
  • 52
  • Sometimes, a picture says more than 1000k :-) Finally, I could understand what the whole "stride"-thing is about! P.S.: I use AForge now, it takes care of most of these things. – Xan-Kun Clark-Davis May 11 '17 at 12:35
0

Be sure you watch that the order is B-G-R instead of R-G-B You can try this unsafe code which converts the values to uint. So you have RGB converted to 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;
   }
}
0

I remember a library I working on years ago - the colors were shifted strangely. The underlying Microsoft Library had a (feature) in that the RGB had been reversed inside the library - unbeknownst to us we tried a transform and discovered that little gem.

Also do not forget the Alpha Channel in your C# buffer.

Bitmap color channels in memory are represented in the order Blue, Green, Red and Alpha despite being commonly referred to by abbreviation ARGB! http://softwarebydefault.com/2013/03/22/bitmap-swap-argb/

Stix
  • 485
  • 4
  • 13