dotnet drawing api:
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,
[quoted text, click to view] > 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 [quoted text, click to view] "Brian" <Brian@discussions.microsoft.com> wrote in message news:0CFA3B85-39F1-4152-8D32-E579C7A311C7@microsoft.com... > 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
[quoted text, click to view] "Brian" <Brian@discussions.microsoft.com> wrote in message news:0CFA3B85-39F1-4152-8D32-E579C7A311C7@microsoft.com... > 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.
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 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,
[quoted text, click to view] "Brian" <Brian@discussions.microsoft.com> wrote in message news:C199889A-8B53-431B-823E-3607D3856459@microsoft.com... > 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#.
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
[quoted text, click to view] "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
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. [quoted text, click to view] "Brian" <Brian@discussions.microsoft.com> wrote in message news:04D38177-B1D6-436F-89C3-A4ECDECCB41D@microsoft.com... > > "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.
[quoted text, click to view] > 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 /// [quoted text, click to view] > 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 /// [quoted text, click to view] > 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
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
[quoted text, click to view] > 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
[quoted text, click to view] "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
[quoted text, click to view] > 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.
Don't see what you're looking for? Try a search.
|