2012-04-18 60 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