Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Raw RGB pixel data into a bitmap

489 views
Skip to first unread message

Brian

unread,
Jul 30, 2007, 2:30:02 PM7/30/07
to
Hello there,

I have a 2-dimensional byte array of RGB pixel data. The first dimension is
the image width * 3, and the second is the image height. The format is as
follows:

R1G1B1R2G2B2..... etc. for each line in the image.

I've been searching for an easy fast way to get the 2-dimensional array into
a Bitmap or Image class so I can easily save it.

I first tried converting the byte array to a memorystream, but the
memorystream requires a 1-dimensional array. Then I found out that it may
need the header information. I'm getting a bit confused and possibly over my
head.

So my basic question is how to create a Bitmap/Image in memory using a
2-dimensional byte array of RGB values.

Any help would be greatly appreciated. I've been googling for a couple days
now, and I am more confused than when I started.

Thanks,

-Brian

Michael Phillips, Jr.

unread,
Jul 30, 2007, 2:55:03 PM7/30/07
to
> So my basic question is how to create a Bitmap/Image in memory using a
> 2-dimensional byte array of RGB values.

1) Create a MemoryStream.
Since a bitmap is aligned on an unsigned integer boundary you need to
allocate the necessary padding between scanlines.
The memory layout of a bitmap is as follows:
BITMAPFILEHEADER 14 bytes
BITMAPINFOHEADER 40 bytes
Color Table, if any... number of colors * 4 bytes
or bitfield array of 3 unsigned integers if your image uses RGB masks.
Bitmap's bits<----------1st scanline points to bottom of bitmap
3) After filling in the appropriate headers, write them to your stream
4) Now create a loop where you copy your RGB data one scanline at a time.
Assuming your image is 24bpp, copy width*3 bytes each scanline, pad when
necessary to fill out the unsigned integer aligned scanline boundary.
5) After you have finished with your stream, rewind it.
6) You now have a bitmap that is ready to be saved with the
Bitmap.Save(MemoryStream) method

"Brian" <Br...@discussions.microsoft.com> wrote in message
news:0CFA3B85-39F1-4152...@microsoft.com...

Michael C

unread,
Jul 30, 2007, 10:02:53 PM7/30/07
to
"Brian" <Br...@discussions.microsoft.com> wrote in message
news:0CFA3B85-39F1-4152...@microsoft.com...

Hi Brian,

Memory stream is not the way to go IMO. Instead, have a look into help on
the Bitmap.LockBits function. The basic steps would be:

1) Create a Bitmap of appropriate size and color depth.
2) Call LockBits
3) Copy data into bitmap from array
4) Call UnlockBits
5) You're done

If you're using C# this is easier because it supports points but it will
still work in VB just fine using Marshal.Copy (from memory).

Michael


Brian

unread,
Jul 31, 2007, 7:30:01 AM7/31/07
to
Thanks to both of you! I'll look into the header information and into
Lockbits. I've been reading a bunch on Lockbits being the way to go for fast
image operations. Every example I read though used an image that was already
in existence. I'll have to find an example of creating the header
information. And yes, I'm doing this in VB, not C#.

Thanks again,

-Brian

Michael C

unread,
Jul 31, 2007, 9:17:00 PM7/31/07
to
"Brian" <Br...@discussions.microsoft.com> wrote in message
news:C199889A-8B53-431B...@microsoft.com...

For your particular case it doesn't matter that you're using VB. With C# you
can use pointers and avoid copying the bitmap data to an array but because
your data is already in an array it doesn't matter.

Michael


Brian

unread,
Aug 1, 2007, 8:26:06 AM8/1/07
to

"Michael C" wrote:
> For your particular case it doesn't matter that you're using VB. With C# you
> can use pointers and avoid copying the bitmap data to an array but because
> your data is already in an array it doesn't matter.
>
> Michael

Ok . . . I think I'm getting somewhere now. Thanks again.

Here's my fuction:

Imports System.Drawing
Imports System.Runtime.InteropServices

Public Function saveImages(ByVal inBuf(,,) As Byte) As Boolean

Dim bmp As New Bitmap((inBuf.GetUpperBound(1) + 1) / 3,
inBuf.GetUpperBound(2) + 1, Imaging.PixelFormat.Format24bppRgb)
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As Imaging.BitmapData = bmp.LockBits(rect,
Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
Dim ptr As IntPtr = bmpData.Scan0

Dim bytes As Long = bmp.Width * bmp.Height * 3
Dim rgbValues(bytes - 1) As Byte
Dim intIndex As Long = 0

For i As Integer = 0 To inBuf.GetUpperBound(1)
For j As Integer = 0 To inBuf.GetUpperBound(2)
rgbValues(intIndex) = inBuf(0, i, j)
intIndex += 1
Next
Next

Marshal.Copy(rgbValues, 0, ptr, bytes)

bmp.UnlockBits(bmpData)

bmp.Save("C:\testSave.jpg", Imaging.ImageFormat.Jpeg)

End Function

I am actually using a 3-dimensional array. It's basically an array of
multiple images' RGB data "inBuf(#images, Width*3, Height)". But for testing
purposes, I'm only referencing the 1st image. Again, the 2nd dimension is
the image width * 3, and the 3rd dimension is the image height.

Here's the problem. I'm getting an image, but the colors are off. It's
almost like it's missing a channel, or that it's putting the RGB values in
the wrong place. I can't quite figure out what's going on. Here are the two
images: The original: http://www.jfreitasphotography.com/Temp/original.jpg
This is where I get 3-dimensional array from. After the function I get this:
http://www.jfreitasphotography.com/Temp/fromFunction.jpg

When I step through the code, everything looks good until after the save.
The first value in the rgbValues byte array is the the Red pixel value of the
first pixel in the original image. But after the save, you can see that the
first pixel in the new image is not the same number.

So I'm guessing this would be related to the header info again? Or is there
some sort of padding that I'm missing. I was kind of assuming that the
header info was taken care of with the new Bitmap declaration.

Michael Phillips, Jr.

unread,
Aug 1, 2007, 2:48:22 PM8/1/07
to
I do not know how your pixels are organized in the original raw data but
when you copy them
to the System.Drawing.Bitmap the pixels need to be in byte order, Blue,
Green, Red for each scanline in a 24bpp bitmap.

"Brian" <Br...@discussions.microsoft.com> wrote in message

news:04D38177-B1D6-436F...@microsoft.com...

Karsten Sosna

unread,
Aug 2, 2007, 2:53:56 AM8/2/07
to
> Dim bmp As New Bitmap((inBuf.GetUpperBound(1) + 1) / 3,
> inBuf.GetUpperBound(2) + 1, Imaging.PixelFormat.Format24bppRgb)
> Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
> Dim bmpData As Imaging.BitmapData = bmp.LockBits(rect,
> Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
> Dim ptr As IntPtr = bmpData.Scan0
>
> Dim bytes As Long = bmp.Width * bmp.Height * 3
> Dim rgbValues(bytes - 1) As Byte
Brian,
you have a 24Bit-Bitmap. The BitMapData have an alignment of 4 each line.
Now if you have a bitmap with 1x1 pixel, the BitMapData.Stride will need 4
bytes instead of 3 bytes. But your rgbValues-Array is too short. In Example:
A 24Bit-Bitmap(10x50 pixel) will need 10 * 3 = 30 bytes <<Alignment to 4 =
32 bytes each line. With 50 lines vou need 50 * 32 = 1600 bytes. But you
calculate 10 * 3 * 50 = 1500 bytes. Now correct is:
\\\
Dim rgbValues(bmpData.Stride * bmpData.Height - 1) As Byte
///

> For i As Integer = 0 To inBuf.GetUpperBound(1)
> For j As Integer = 0 To inBuf.GetUpperBound(2)
> rgbValues(intIndex) = inBuf(0, i, j)
> intIndex += 1
> Next
> Next

This is an accident that this code works a little bit. The reason is your
image is 640 pixel width. 640 * 3 = 1920 bytes/ line. 1920 Mod 4 = 0 >> No
alignment needed. You can control that, if you take a look to
bmpData.Stride-Property, it must be set to 1920. Untested:
\\\
Dim align As Integer = bmpData.Stride - bmpData.Width * 3


For i As Integer = 0 To inBuf.GetUpperBound(1)
For j As Integer = 0 To inBuf.GetUpperBound(2)
rgbValues(intIndex) = inBuf(0, i, j)
intIndex += 1
Next

intIndex += align
Next
///

> Here's the problem. I'm getting an image, but the colors are off. It's
> almost like it's missing a channel, or that it's putting the RGB values in
> the wrong place. I can't quite figure out what's going on. Here are the
> two
> images: The original:
> http://www.jfreitasphotography.com/Temp/original.jpg
> This is where I get 3-dimensional array from. After the function I get
> this:
> http://www.jfreitasphotography.com/Temp/fromFunction.jpg

This comes form the order of colors in the array B, G, R, B, G, R,..., not
R, G, B. For 32BitbppArgb i.e.is the order B, G, R, A not A, R, G , B.

Excuse my bad engilish.
--
Regards Karsten


Brian

unread,
Aug 2, 2007, 8:48:03 AM8/2/07
to
Awesome!! Thanks for the help guys! It's working like a charm.

I knew I was getting into some programming that I'm a little unfamiliar with
. . . bitmaps, padding, stride . . . etc. But I think I get it now.

Now why would they make the bitmap class GRB . . . not RGB?? I'm sure
there's a reason, but it doesn't seem logical.

Anyway, for the record, here's my function now that works:

Imports System.Drawing
Imports System.Runtime.InteropServices

Public Function saveImages(ByVal inBuf(,,) As Byte) As Boolean

Dim bmp As New Bitmap((inBuf.GetUpperBound(1) + 1) / 3,

inBuf.GetUpperBound(2) + 1, Imaging.PixelFormat.Format24bppRgb)
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As Imaging.BitmapData = bmp.LockBits(rect,
Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
Dim ptr As IntPtr = bmpData.Scan0

Dim bytes As Long = bmpData.Stride * bmpData.Height - 1


Dim rgbValues(bmpData.Stride * bmpData.Height - 1) As Byte

Dim intIndex As Long = 0

Dim align As Integer = bmpData.Stride - bmpData.Width * 3


For i As Integer = 0 To inBuf.GetUpperBound(1)

For j As Integer = 0 To inBuf.GetUpperBound(2) Step 3
rgbValues(intIndex) = inBuf(0, i, j + 2)
rgbValues(intIndex + 1) = inBuf(0, i, j + 1)
rgbValues(intIndex + 2) = inBuf(0, i, j)
intIndex += 3


Next
intIndex += align
Next

Marshal.Copy(rgbValues, 0, ptr, bytes)


bmp.UnlockBits(bmpData)
bmp.Save("C:\testSave.jpg", Imaging.ImageFormat.Jpeg)
End Function

Thank's again!

-Brian

Karsten Sosna

unread,
Aug 3, 2007, 12:09:32 AM8/3/07
to
> Now why would they make the bitmap class GRB . . . not RGB?? I'm sure
> there's a reason, but it doesn't seem logical.

Hello Brian,
not GRB, BGR!
It's logical. Colors are stored with 32 Bits. If you have A, R, G, B it was
stored as Int32 in format Low-Word, High Word, Low Byte, High Byte. If you
have a color no trancpareny(&FF), Red(&H80), Green(&H40), Blue(&H20) it was
&HFF804020. Stored in the memory it is &204080FF. Now you copy the
memoryblock to the bytearray then is arr(index + 0) = &H20(Blue),arr (index
+ 1) = &H40(Green), arr(index + 2) = &H80(Red),arr (index + 3) =
&HFF(Alpha). Analog to 24Bit the Format is &HFFrrggbb, stored in memory it
is bbggrr because the alphachannel are ignored.
--
Regards Karsten


Michael Phillips, Jr.

unread,
Aug 3, 2007, 12:20:21 PM8/3/07
to
> Now why would they make the bitmap class GRB . . . not RGB?? I'm sure
> there's a reason, but it doesn't seem logical.

In the Window's 3.1 Bitmap Specification, the bitmap is upside down.

The first scanline's first pixel is the bottom right of the bitmap.

The last scanline's last pixel is the top left of your bitmap.

Each pixel is BGR in memory.

If you look at the bitmap in your mind as if it righted itself, each pixel
is RGB.


Brian

unread,
Aug 3, 2007, 1:18:05 PM8/3/07
to
"Karsten Sosna" wrote:
> Hello Brian,
> not GRB, BGR!
> It's logical. Colors are stored with 32 Bits. If you have A, R, G, B it was
> stored as Int32 in format Low-Word, High Word, Low Byte, High Byte. If you
> have a color no trancpareny(&FF), Red(&H80), Green(&H40), Blue(&H20) it was
> &HFF804020. Stored in the memory it is &204080FF. Now you copy the
> memoryblock to the bytearray then is arr(index + 0) = &H20(Blue),arr (index
> + 1) = &H40(Green), arr(index + 2) = &H80(Red),arr (index + 3) =
> &HFF(Alpha). Analog to 24Bit the Format is &HFFrrggbb, stored in memory it
> is bbggrr because the alphachannel are ignored.
> --
> Regards Karsten

GRB! That's what I meant. :-)

Thanks for the explanation. I figured it had something to do with how they
were stored in memory.

-Brian

0 new messages