2012-04-18 2 views
12

Ich arbeite mit Kameras (in diesem Fall monochrom) in VB.NET. Die API der Kameras ist in C++ und der Hersteller stellt Wrapper zur Verfügung. Wie in diesen Fällen üblich, wird das Bild als Array Byte zurückgegeben. In meinem speziellen Fall sind es 8 Bits pro Pixel, also gibt es kein schickes Bitpacking zum Rückgängigmachen.Wie konvertiert man ein Byte-Array in eine Bitmap-Instanz (.NET)?

Ich war flambergasted, dass .NET so wenig Unterstützung für diese Art von Sache hat. Bei der Online-Suche habe ich verschiedene Themen gefunden, aber es hilft nicht, dass Leute in vielen Fällen ein Byte-Array haben, das Bilddaten entspricht (also eine Bilddatei in ein Byte-Array liest und dann .NET das Array als Datei interpretiert, mit Header und alles).

In diesem Fall muss ich ein Array von rohen Pixelwerten insbesondere in etwas konvertieren, das ich auf dem Bildschirm anzeigen kann.

Es scheint keine eingebaute Möglichkeit dafür zu geben, da das in .NET integrierte 8bpp-Format indiziert ist (benötigt eine Palette), aber es scheint nicht so zu sein, dass das Einstellen der Palette unterstützt wird. Auch das hat mich wirklich überrascht. This ist das vielversprechendste Thema, das ich gefunden habe.

Also die einzige Möglichkeit, dies zu tun ist, erstellen Sie eine Bitmap und kopieren Sie dann die Pixelwerte Pixel für Pixel. In meinem Fall dauerte ein 8 MPixel Bild mehrere Sekunden auf dem schnellsten i7, wenn SetPixel verwendet wurde.

Die Alternative, die ich mit SetPixel gefunden habe, ist LockBits, die dann Zugriff auf das (nicht verwaltete) Array von Bytes gibt, das das Bild darstellt. Es scheint verschwenderisch, weil ich das Array in .NET manipulieren muss und es dann in nicht verwalteten Speicherplatz kopieren muss. Ich kopiere bereits die originalen Bilddaten einmal (so kann das Original wiederverwendet oder durch den Rest von dem weggeworfen werden), obwohl dies deutlich schneller ist als die Verwendung von SetPixel, dies scheint immer noch verschwenderisch.

Hier ist der VB-Code ich habe:

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 

Für ein 8 MPixel Bild der sequentielle Version von etwa 220 Millisekunden verbraucht TheBitmap bis (einschließlich), den Anruf zu TheBitmap.UnlockBits() zu schaffen. Die parallele Version ist viel inkonsistenter, liegt aber zwischen 60 und 80 Millisekunden. Bedenkt man, dass ich von vier Kameras gleichzeitig akquiriere und mit viel Zeit auf die Festplatte streame, ist das ziemlich enttäuschend. Die API enthält Beispielcode in C++, der von allen vier Kameras gleichzeitig mit voller Bildrate (17 fps) angezeigt werden kann. Es befasst sich natürlich nie mit Bitmap, da GDI auf Arrays von Rohpixelwerten zugreift.

Zusammenfassend muss ich über die verwaltete/unmanaged Barriere gehen, einfach weil es keinen direkten Weg gibt, ein Array von Pixelwerten aufzunehmen und es in eine Bitmap Instanz zu stopfen. In diesem Fall kopiere ich auch die Pixelwerte in eine 24bpp Bitmap, weil ich die Palette in einem indizierten Bild nicht "einstellen" kann, so dass 8bpp kein brauchbares Format für die Anzeige ist.

Gibt es keinen besseren Weg?

Antwort

10

Erstellen Sie eine valid bitmap header und preapend es auf die Byte-Array. Sie müssen nur 56-128 Byte Daten manuell erstellen.

Zweite, create a stream from a byte array.

Als nächstes create an image or bitmap class by using Image.FromStream()/Bitmap.FromStream()

würde ich mir nicht vorstellen, dass es irgendeine Art von Roh-Imaging-Funktionen in .NET ist. Es gibt so viele benötigte Informationen für ein Bild, es wäre eine extrem lange Parameterliste für eine Methode, rohe Bytes zu akzeptieren und ein imaginary zu erstellen (ist es eine Bitmap, JPEG, GIF, PNG usw. und alle zusätzlichen Daten von jedem von das erfordert ein gültiges Bild) es würde keinen Sinn ergeben.

+0

FromStream erfordert, dass der Strom ein „voll“ Bitmap enthalten (mit Kopf info), nicht nur Werte Pixel --- Ich habe versucht, diese in meine Frage zu erklären. – pelesl

+3

Aktualisiert mit Bezug auf bmp header info. Voranfügen von etwa 128 Bytes, dann Verwendung von fromstream sollte wesentlich schneller sein. –

+1

Es ist einen Versuch wert, besonders wenn es mir erlaubt, einen Bitmap-Header mit einer Graustufenpalette vorzugeben, also muss ich Pixelwerte nicht dreimal kopieren, um 24 bpp zu erhalten. – pelesl

4

Scheint wie alle Ihre Kameras vom gleichen Typ sind, so die Bilder Daten. Ich weiß nicht, ob das in Ihrem Fall möglich ist, aber könnten Sie aus einem beliebigen Bild eine solche "vollständige Bitmap" erstellen und dann den Header als Vorlage verwenden (da dies für alle Bilder gleich ist).

+1

+1 Das ist eine fantastische Idee! –

6

Versuchen Sie, diese

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

Ich denke, Sie müssen es nicht als "var" deklarieren. Sie können es stattdessen als "Bitmap" deklarieren, oder? Aber die Antwort ist natürlich hervorragend! – tmighty

+0

Natürlich können Sie. RTM https://msdn.microsoft.com/ru-ru/library/bb383973.aspx – Cheburek

Verwandte Themen