我在VB.NET中使用摄像头(在这种情况下是单色)。相机的API使用C++,制造商提供包装。正如在这些情况下通常那样,图像以Byte
阵列的形式返回。在我的具体情况下,它是每像素8位,所以没有花哨的可以撤消的bitpacking。如何将字节数组转换为位图实例(.NET)?
我是大惊小怪 .NET对此类事物的支持很少。在线搜索我发现了各种主题,但在许多情况下,人们都没有对应于图像数据的字节数组(即将图像文件读入字节数组,然后让.NET将数组解释为文件,与标题和一切)。
在这种情况下,我需要将原始像素值的数组转换为,特别是我可以在屏幕上显示的东西。
似乎没有任何内置的方法来做到这一点,因为内置到.NET中的8bpp格式被索引(需要调色板),但似乎并不支持设置调色板。再次,这真的让我感到惊讶。 This是我发现的最有希望的主题。
所以我发现要做到这一点的唯一方法是创建一个Bitmap
,然后逐个像素地复制像素值。在我的情况下,使用SetPixel
时,800万像素的图像以最快的i7拍摄几秒。
我发现使用SetPixel
的替代方法是LockBits
,然后您可以访问构成图像的(非托管)字节数组。这看起来很浪费,因为我必须在.NET中操纵数组,然后将其复制回unmananaged空间。我已经复制了一次原始图像数据(因此原始图像可以被重复使用或被剩余的部分丢弃),所以虽然比使用SetPixel
快得多,但这仍然看起来很浪费。
这里是VB代码我有:
Public Function GetBitmap(ByRef TheBitmap As System.Drawing.Bitmap) As Integer
Dim ImageData() As Byte
Dim TheWidth, TheHeight As Integer
'I've omitted the code here (too specific for this question) which allocates
'ImageData and then uses Array.Copy to store a copy of the pixel values
'in it. It also assigns the proper values to TheWidth and TheHeight.
TheBitmap = New System.Drawing.Bitmap(TheWidth, TheHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb)
Dim TheData As System.Drawing.Imaging.BitmapData = TheBitmap.LockBits(
New System.Drawing.Rectangle(0, 0, TheWidth, TheHeight), Drawing.Imaging.ImageLockMode.ReadWrite, TheBitmap.PixelFormat)
Dim Image24(Math.Abs(TheData.Stride) * TheHeight) As Byte
'Then we have two choices: to do it in parallel or not. I tried both; the sequential version is commented out below.
System.Threading.Tasks.Parallel.For(0, ImageData.Length, Sub(i)
Image24(i * 3 + 0) = ImageData(i)
Image24(i * 3 + 1) = ImageData(i)
Image24(i * 3 + 2) = ImageData(i)
End Sub)
'Dim i As Integer
'For i = 0 To ImageData.Length - 1
' Image24(i * 3 + 0) = ImageData(i)
' Image24(i * 3 + 1) = ImageData(i)
' Image24(i * 3 + 2) = ImageData(i)
'Next
System.Runtime.InteropServices.Marshal.Copy(Image24, 0, TheData.Scan0, Image24.Length)
TheBitmap.UnlockBits(TheData)
'The above based on
'http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx
Return 1
End Function
对于8百万像素图像的顺序版本从创建TheBitmap
到(并包括)调用TheBitmap.UnlockBits()
消耗大约220毫秒。并行版本更不一致,但范围在60到80毫秒之间。考虑到我从四台摄像机中同时购买,并有充足的时间备份到磁盘,这是非常令人失望的。该API带有C++中的示例代码,可以全帧速率(17 fps)同时显示所有四台摄像机。当然,它从不处理Bitmap
,因为GDI访问原始像素值的数组。
总之,我必须跨越托管/非托管屏障,因为没有直接的方法来获取像素值数组并将其“填充”到Bitmap
实例中。在这种情况下,我还将像素值复制到24bpp Bitmap
中,因为我无法在索引图像中“设置”调色板,因此8bpp不是用于显示的可行格式。
有没有更好的方法?
FromStream要求流包含一个“满”位图(有头信息),不只是像素值---我想在我的问题来解释这一点。 – pelesl 2012-04-18 17:02:42
参考bmp标题信息更新。预先附加大约128个字节,然后使用fromstream应该实质上更快。 – 2012-04-18 17:12:09
这是值得一试的,特别是如果它可以让我在灰度调色板的前面添加一个位图标题,所以我不必复制像素值3次即可获得24 bpp。 – pelesl 2012-04-19 01:21:14