我正在使用 VB.NET 中的相机(在本例中为单色)。相机的 API 采用 C++ 语言,制造商提供包装器。在这些情况下很典型,图像作为Byte
数组返回。在我的特殊情况下,它是每像素 8 位,所以没有花哨的位打包可以撤消。
令我大吃一惊的是 .NET 对这种类型的支持如此之少。网上搜索找到了各种主题,但是很多情况下人们都有对应图像数据的字节数组(也就是将图像文件读入字节数组,然后让.NET将数组解释为文件),这于事无补,带有标题和所有内容)。
在这种情况下,我需要将原始像素值数组转换为可以在屏幕上显示的东西。
似乎没有任何内置方法可以做到这一点,因为 .NET 中内置的 8bpp 格式已编入索引(需要调色板),但似乎不支持设置调色板。再一次,这真的让我感到惊讶。这是我发现的最有希望的话题。
所以我发现这样做的唯一方法是创建一个Bitmap
然后逐个像素地复制像素值。在我的情况下,在最快的 i7 上使用8 MPixel 图像需要几秒钟SetPixel
。
我发现使用的替代方法SetPixel
是LockBits
,然后您可以访问构成图像的(非托管)字节数组。这似乎很浪费,因为我必须在 .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 MPixel 图像,顺序版本从创建TheBitmap
到(包括)调用TheBitmap.UnlockBits()
. 并行版本更加不一致,但范围在 60 到 80 毫秒之间。考虑到我同时从四个摄像头获取数据并流式传输到磁盘并且有足够的空闲时间,这非常令人失望。该 API 附带 C++ 示例代码,可以同时以全帧速率 (17 fps) 从所有四个摄像头显示。当然,它从不处理Bitmap
,因为 GDI 访问的是原始像素值数组。
总之,我必须穿越托管/非托管障碍,因为没有直接的方法来获取像素值数组并将其“填充”到Bitmap
实例中。在这种情况下,我还将像素值复制到 24bppBitmap
中,因为我无法在索引图像中“设置”调色板,因此 8bpp 不是一种可行的显示格式。
没有更好的办法吗?