我正在使用VB.NET处理相机(在这种情况下是单色相机)。 相机的API是用C++编写的,并且制造商提供了包装器。 在这些情况下往往返回图像作为 Byte
数组。在我的特定情况下,每个像素有8位,因此没有需要解开的复杂的位压缩。
我感到非常震惊的是 .NET 对于这种类型的事情提供的支持如此之少。 在网上搜索后,我发现各种主题,但是问题在于在许多情况下人们都有一个字节数组对应于图像数据(也就是将图像文件读入字节数组,然后使 .NET 将该数组解释为带标题等内容的文件)。
在这种情况下,我需要将一系列原始像素值转换为可以在屏幕上显示的东西。
似乎没有内置的方法来完成此操作,因为.NET中内置的8bpp格式是索引格式(需要调色板),但似乎不支持设置调色板。再次惊讶。 这 是我找到的最有前途的主题。
因此,我找到的唯一方法是创建一个 Bitmap
,然后逐像素复制像素值。在我这种情况下,使用SetPixel
在最快的i7上需要几秒钟来处理一个800万像素的图像。
我发现使用 LockBits
是替代使用 SetPixel
的一种方法,它可以让你访问(不受管理的)字节数组,它构成了图像。这似乎是浪费的,因为我必须在.NET中操纵数组,然后将其复制回不受管理的空间。我已经复制了原始图像数据一次(这样原始数据可以被重用或丢弃),因此尽管比使用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不是可用于显示的格式。难道就没有更好的方法吗?