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
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...
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
Thanks again,
-Brian
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.
"Brian" <Br...@discussions.microsoft.com> wrote in message
news:04D38177-B1D6-436F...@microsoft.com...
> 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
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
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
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.
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