关于vb.net:如何将字节数组转换为位图实例(.net)?

How do you convert a byte array to a Bitmap instance (.NET)?

我在vb.net中使用相机(在本例中是单色的)。相机的API在C++中,制造商提供包装器。在这些情况下,图像通常以Byte数组的形式返回。在我的特殊情况下,它是8位每像素,所以没有花哨的位压缩要撤消。

我被震惊了,.NET对这类事情的支持太少了。在网上搜索时,我发现了各种各样的主题,但在很多情况下,人们有一个与图像数据相对应的字节数组(即,将图像文件读取到一个字节数组中,然后让.NET将数组解释为一个文件,包括头和所有内容)。

在这种情况下,我需要将一个原始像素值数组转换成,特别是可以在屏幕上显示的内容。

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

所以我找到的唯一方法是创建一个Bitmap,然后逐像素复制像素值。在我的例子中,使用SetPixel时,8mpixel图像在最快的i7上花费了几秒钟。

我发现使用SetPixel的替代方法是LockBits,它可以让您访问构成映像的(非托管)字节数组。这看起来很浪费,因为我必须在.NET中操作数组,然后将其复制回未管理的空间。我已经复制了原始图像数据一次(这样原始图像就可以被其他图像重复使用或丢弃),所以尽管比使用SetPixel快得多,但这仍然是浪费。

这是我的VB代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

对于一个8mpixel图像,从创建TheBitmap到(包括)调用TheBitmap.UnlockBits()的顺序版本大约需要220毫秒。并行版本更不一致,但范围在60到80毫秒之间。考虑到我同时从四个摄像头中获取数据,并且有足够的时间将数据流传输到磁盘,这真是令人失望。API附带了C++中的样例代码,它可以在全帧速率(17 fps)下同时显示所有四个摄像机。当然,它从不处理Bitmap,因为gdi访问原始像素值数组。

总之,我必须穿越托管/非托管屏障,因为没有直接方法将一系列像素值"填充"到Bitmap实例中。在本例中,我还将像素值复制到24bpp Bitmap中,因为我无法"设置"索引图像中的调色板,因此8bpp不是一种可供显示的可行格式。

没有更好的方法吗?


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

第二,从字节数组创建流。

接下来,使用image.fromstream()/bitmap.fromstream()创建一个image或bitmap类

我不认为.NET中有任何原始图像功能。对于一个图像来说,需要的信息太多了,对于一个接受原始字节并创建一个图像的方法来说,它将是一个非常长的参数列表(它是一个位图、jpeg、gif、png等,以及所有这些都需要的额外数据来创建一个有效的图像),这是没有意义的。


试试这个

1
var img = new Bitmap(new MemoryStream(yourByteArray));


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