2012-04-18 83 views
12

我在VB.NET中使用摄像头(在这种情况下是单色)。相机的API使用C++,制造商提供包装。正如在这些情况下通常那样,图像以Byte阵列的形式返回。在我的具体情况下,它是每像素8位,所以没有花哨的可以撤消的bitpacking。如何将字节数组转换为位图实例(.NET)?

我是大惊小怪 .NET对此类事物的支持很少。在线搜索我发现了各种主题,但在许多情况下,人们都没有对应于图像数据的字节数组(即将图像文件读入字节数组,然后让.NET将数组解释为文件,与标题和一切)。

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

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

所以我发现要做到这一点的唯一方法是创建一个Bitmap,然后逐个像素地复制像素值。在我的情况下,使用SetPixel时,800万像素的图像以最快的i7拍摄几秒

我发现使用SetPixel的替代方法是LockBits,然后您可以访问构成图像的(非托管)字节数组。这看起来很浪费,因为我必须在.NET中操纵数组,然后将其复制回unmananaged空间。我已经复制了一次原始图像数据(因此原始图像可以被重复使用或被剩余的部分丢弃),所以虽然比使用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不是用于显示的可行格式。

有没有更好的方法?

回答

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等等,以及每个附加数据那些需要创建一个有效的图像)它没有任何意义。

+0

FromStream要求流包含一个“满”位图(有头信息),不只是像素值---我想在我的问题来解释这一点。 – pelesl 2012-04-18 17:02:42

+3

参考bmp标题信息更新。预先附加大约128个字节,然后使用fromstream应该实质上更快。 – 2012-04-18 17:12:09

+1

这是值得一试的,特别是如果它可以让我在灰度调色板的前面添加一个位图标题,所以我不必复制像素值3次即可获得24 bpp。 – pelesl 2012-04-19 01:21:14

4

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

+1

+1这是一个奇妙的想法! – 2012-04-18 19:09:36

6

试试这个

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

我想你不必声明它是“var”。您可以将其声明为“位图”,不是吗?但当然,答案很棒! – tmighty 2015-01-09 00:50:01

+0

当然可以。 RTM https://msdn.microsoft.com/ru-ru/library/bb383973.aspx – Cheburek 2015-04-01 10:26:49