如何将字节数组转换为Bitmap实例(.NET)?

时间:2012-04-18 16:32:49

标签: .net vb.net imaging

我在VB.NET中使用相机(在本例中为单色)。相机的API使用C ++,制造商提供包装。在这些情况下,通常会将图像作为Byte数组返回。在我的特定情况下,它是每像素8比特,所以没有花哨的比特包撤消。

大吃一惊,.NET对这类事情的支持很少。在线搜索我发现了各种主题,但在许多情况下,人们有一个与图像数据相对应的字节数组(即,将图像文件读入字节数组,然后让.NET将数组解释为文件,这没有帮助)标题和所有内容)。

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

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

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

我发现使用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()的调用,顺序版本消耗大约220毫秒。并行版本更不一致,但范围在60到80毫秒之间。考虑到我同时从四台摄像机中获取并且有足够的时间流式传输到磁盘,这非常令人失望。该API带有C ++示例代码,可以全帧速率(17 fps)同时显示所有四个摄像机。当然,它从不处理Bitmap,因为GDI访问原始像素值的数组。

总之,我必须跨越托管/非托管屏障,因为没有直接的方法来获取像素值数组并将其“填充”到Bitmap实例中。在这种情况下,我也将像素值复制到24bpp Bitmap,因为我无法在索引图像中“设置”调色板,因此8bpp不是可行的显示格式。

有没有更好的方法?

3 个答案:

答案 0 :(得分:10)

创建一个valid bitmap header,并将其预先添加到字节数组中。您只需手动创建56-128个字节的数据。

其次,create a stream from a byte array

接下来,create an image or bitmap class by using Image.FromStream()/Bitmap.FromStream()

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

答案 1 :(得分:6)

试试这个

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

答案 2 :(得分:4)

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