15

我正在使用 VB.NET 中的相机(在本例中为单色)。相机的 API 采用 C++ 语言,制造商提供包装器。在这些情况下很典型,图像作为Byte数组返回。在我的特殊情况下,它是每像素 8 位,所以没有花哨的位打包可以撤消。

令我大吃一惊的是 .NET 对这种类型的支持如此之少。网上搜索找到了各种主题,但是很多情况下人们都有对应图像数据的字节数组(也就是将图像文件读入字节数组,然后让.NET将数组解释为文件),这于事无补,带有标题和所有内容)。

在这种情况下,我需要将原始像素值数组转换为可以在屏幕上显示的东西。

似乎没有任何内置方法可以做到这一点,因为 .NET 中内置的 8bpp 格式已编入索引(需要调色板),但似乎不支持设置调色板。再一次,这真的让我感到惊讶。是我发现的最有希望的话题。

所以我发现这样做的唯一方法是创建一个Bitmap然后逐个像素地复制像素值。在我的情况下,在最快的 i7 上使用8 MPixel 图像需要几秒钟SetPixel

我发现使用的替代方法SetPixelLockBits,然后您可以访问构成图像的(非托管)字节数组。这似乎很浪费,因为我必须在 .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 不是一种可行的显示格式。

没有更好的办法吗?

4

3 回答 3

11

创建一个有效的位图标头,并将其预先附加到字节数组中。您只需要手动创建 56-128 字节的数据。

其次,从字节数组创建一个流

接下来,使用 Image.FromStream()/Bitmap.FromStream() 创建图像或位图类

我不会想象.Net 中有任何类型的原始成像功能。图像有太多需要的信息,对于接受原始字节并创建图像(是位图、jpeg、gif、png 等,以及每个附加数据的方法)来说,这将是一个非常冗长的参数列表这些需要创建一个有效的图像)这是没有意义的。

于 2012-04-18T17:01:04.547 回答
7

试试这个

var img = new Bitmap(new MemoryStream(yourByteArray));
于 2013-02-15T15:09:50.813 回答
4

好像你所有的相机都是同一类型的,所以图像数据。我不知道这在您的情况下是否可行,但是您能否从任何图像创建一个“完整位图”,然后仅使用标题作为模板(因为它对于所有图像都是相同的)。

于 2012-04-18T17:13:26.507 回答