Domanda Come si converte un array di byte in un'istanza Bitmap (.NET)?


Sto lavorando con le telecamere (in questo caso monocromatiche) in VB.NET. L'API delle telecamere è in C ++ e il produttore fornisce i wrapper. Come è tipico in questi casi, l'immagine viene restituita come a Byte array. Nel mio caso particolare è di 8 bit per pixel, quindi non c'è nessun pacchetto di bit di fantasia da annullare.

ero sbalordito che .NET ha così scarso supporto per questo tipo di cose. Cercando online ho trovato vari argomenti, ma non aiuta che in molti casi le persone abbiano un array di byte corrispondente ai dati di immagine (cioè legge un file di immagine in un array di byte e poi .NET interpreta l'array come un file, con intestazione e tutto).

In questo caso ho bisogno di convertire una matrice di valori di pixel grezzi in, in particolare, qualcosa che posso visualizzare sullo schermo.

Non sembra esserci alcun modo integrato per farlo, perché il formato 8bpp incorporato in .NET è indicizzato (necessita di una tavolozza) ma non sembra che l'impostazione della tavolozza sia supportata. Di nuovo, questo mi ha davvero sorpreso. Questo è l'argomento più promettente che ho trovato.

Quindi l'unico modo che ho trovato per fare questo è creare un Bitmap e quindi copiare i valori dei pixel pixel per pixel. Nel mio caso è stata scattata un'immagine a 8 MPixel diversi secondi sul i7 più veloce quando si usa SetPixel.

L'alternativa che ho trovato usando SetPixel è LockBits, che quindi ti dà accesso alla matrice (non gestita) di byte che costituisce l'immagine. Sembra uno spreco, perché devo manipolare l'array in .NET e quindi copiarlo nello spazio non gestito. Sto già copiando i dati dell'immagine originale una volta (quindi l'originale può essere riutilizzato o scartato dal resto di ciò che sta succedendo) quindi, sebbene sia molto più veloce dell'uso SetPixel questo sembra ancora dispendioso.

Ecco il codice VB che ho:

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

Per un'immagine a 8 MPixel, la versione sequenziale consuma circa 220 millisecondi dalla creazione TheBitmap fino a (e incluso) la chiamata a TheBitmap.UnlockBits(). La versione parallela è molto più incoerente ma varia tra 60 e 80 millisecondi. Considerando che sto acquisendo da quattro telecamere contemporaneamente e lo streaming su disco con un sacco di tempo libero, questo è piuttosto deludente. L'API viene fornita con un codice di esempio in C ++ che può essere visualizzato da tutte e quattro le telecamere contemporaneamente a una frequenza fotogrammi completa (17 fps). Non ha mai a che fare Bitmap, ovviamente, dal momento che GDI accede a matrici di valori di pixel non elaborati.

In sintesi, devo attraversare la barriera gestita / non gestita semplicemente perché non esiste un modo diretto per prendere una serie di valori di pixel e "farcela" in una Bitmap esempio. In questo caso copio anche i valori dei pixel in un 24bpp Bitmap perché non posso "impostare" la tavolozza in un'immagine indicizzata, quindi 8bpp non è un formato valido per la visualizzazione.

Non c'è modo migliore?


12
2018-04-18 16:32


origine


risposte:


Creare un intestazione bitmap validae preinstallarlo nell'array di byte. Devi solo creare manualmente 56-128 byte di dati.

Secondo, crea un flusso da un array di byte.

Il prossimo, creare una classe immagine o bitmap usando Image.FromStream () / Bitmap.FromStream ()

Non immaginerei che ci sia alcun tipo di funzionalità di imaging raw in .Net. Ci sono così tante informazioni necessarie per un'immagine, sarebbe un elenco di parametri estremamente lungo per un metodo per accettare byte grezzi e creare un'immagine (è una bitmap, jpeg, gif, png, ecc. E tutti i dati aggiuntivi ciascuno di quelli che richiedono di creare un'immagine valida) non avrebbe senso.


10
2018-04-18 17:01



Prova questo

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

6
2018-02-15 15:09



Sembra che tutte le tue fotocamere siano dello stesso tipo, quindi i dati delle immagini. Non so se questo è possibile nel tuo caso, ma potresti creare questa "full bitmap" da qualsiasi immagine e quindi usare semplicemente l'intestazione come modello (poiché sarà lo stesso per tutte le immagini).


4
2018-04-18 17:13