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

问题描述 投票:0回答:4

我正在 VB.NET 中使用相机(在本例中为单色)。相机的 API 采用 C++ 语言,制造商提供包装器。正如这些情况下的典型情况一样,图像以

Byte
数组的形式返回。在我的特定情况下,它是每像素 8 位,因此没有需要撤消的花哨的位打包。

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

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

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

因此,我发现执行此操作的唯一方法是创建一个

Bitmap
,然后逐像素复制像素值。就我而言,使用 SetPixel 时,在最快的 i7 上拍摄 8 MPixel 图像需要花费
几秒钟

我发现使用

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 不是可行的显示格式。

有没有更好的办法?

.net vb.net imaging
4个回答
11
投票
创建一个

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

第二,

从字节数组创建流

接下来,

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

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


7
投票
试试这个

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

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


0
投票
这里是一些简洁的代码,不需要写Bitmap头,也不需要复制。

Dim XyBmp As Bitmap Dim ColorPalette As Imaging.ColorPalette Dim XyBuffer(0 To 128 * 256 - 1) As Byte Dim XyBufferHandle As GCHandle Private Sub createXyBmp() Dim i As Integer XyBufferHandle = GCHandle.Alloc(XyBuffer, GCHandleType.Pinned) XyBmp = New Bitmap(128, 256, 128, Imaging.PixelFormat.Format8bppIndexed, XyBufferHandle.AddrOfPinnedObject()) ColorPalette = XyBmp.Palette For i = 0 To 255 ColorPalette.Entries(i) = Color.FromArgb(255, i, i, i) Next i XyBmp.Palette = ColorPalette End Sub Friend Sub DrawXyBmp() Do Static b As Byte For i = 0 To XyBuffer.Length - 1 XyBuffer(i) = b Next i Me.Refresh() Me.BackgroundImage = XyBmp b = CByte((b + 1) And &HFF) Loop End Sub Private Sub close() XyBmp.Dispose() XyBufferHandle.Free() End Sub
    
© www.soinside.com 2019 - 2024. All rights reserved.