¿Cómo se convierte una matriz de bytes a una instancia de Bitmap(.NET)?
vb.net imaging (3)
Estoy trabajando con cámaras (en este caso, monocromo) en VB.NET. La API de las cámaras está en C ++ y el fabricante proporciona envolturas. Como es típico en estos casos, la imagen se devuelve como una matriz de Byte
. En mi caso particular, es de 8 bits por píxel, así que no hay ningún bitpack de fantasía para deshacer.
Me quedé pasmado de que .NET tenga tan poco soporte para este tipo de cosas. Buscando en línea encontré varios temas, pero no ayuda que en muchos casos las personas tengan una matriz de bytes correspondiente a los datos de imagen (es decir, lean un archivo de imagen en una matriz de bytes y luego interpreten .NET la matriz como un archivo, con encabezado y todo).
En este caso, necesito convertir una matriz de valores de píxeles en bruto, particularmente, algo que pueda mostrar en pantalla.
No parece haber ninguna forma integrada de hacerlo, porque el formato 8bpp incorporado en .NET está indexado (necesita una paleta) pero no parece que sea compatible con la configuración de la paleta. Nuevamente, esto realmente me sorprendió. Este es el tema más prometedor que encontré.
Entonces, la única forma que encontré para hacerlo es crear un Bitmap
y luego copiar los valores de píxel píxel por píxel. En mi caso, una imagen de 8 MPixel tardó varios segundos en el i7 más rápido al usar SetPixel
.
La alternativa que encontré al usar SetPixel
es LockBits
, que luego le da acceso a la matriz (no administrada) de bytes que constituye la imagen. Parece un desperdicio, porque tengo que manipular el arreglo en .NET y luego copiarlo nuevamente en el espacio no manufacturado. Ya estoy copiando los datos de la imagen original una vez (por lo que el original puede reutilizarse o descartarse por el resto de lo que está sucediendo) así que, aunque es significativamente más rápido que usar SetPixel
esto todavía parece un desperdicio.
Aquí está el código VB que tengo:
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
Para una imagen de 8 MPixel, la versión secuencial consume alrededor de 220 milisegundos desde la creación de TheBitmap
hasta (e incluyendo) la llamada a TheBitmap.UnlockBits()
. La versión paralela es mucho más inconsistente pero oscila entre 60 y 80 milisegundos. Teniendo en cuenta que estoy comprando desde cuatro cámaras simultáneamente y transmitiendo al disco con tiempo de sobra, esto es bastante decepcionante. La API viene con código de muestra en C ++ que puede mostrarse desde las cuatro cámaras simultáneamente a una velocidad de cuadro completa (17 fps). Nunca trata con Bitmap
, por supuesto, ya que GDI accede a matrices de valores de píxel en bruto.
En resumen, tengo que viajar a través de la barrera gestionada / no gestionada simplemente porque no hay una forma directa de tomar una matriz de valores de píxeles y "rellenarlos" en una instancia de Bitmap
. En este caso, también estoy copiando los valores de píxel en un mapa de Bitmap
24bpp porque no puedo "configurar" la paleta en una imagen indexada, por lo que 8bpp no es un formato viable para mostrar.
¿No hay mejor manera?
Prueba esto
var img = new Bitmap(new MemoryStream(yourByteArray));
Parece que todas sus cámaras son del mismo tipo, por lo que los datos de las imágenes. No sé si esto es posible en su caso, pero podría crear un "mapa de bits completo" a partir de cualquier imagen y luego simplemente usar el encabezado como plantilla (ya que será el mismo para todas las imágenes).
Cree un encabezado de mapa de bits válido , y preámbgalo a la matriz de bytes. Solo necesitará crear manualmente de 56 a 128 bytes de datos.
En segundo lugar, cree una secuencia a partir de una matriz de bytes .
A continuación, cree una imagen o clase de mapa de bits utilizando Image.FromStream () / Bitmap.FromStream ()
No me imagino que haya ningún tipo de funciones de imágenes crudas en .Net. Hay tanta información necesaria para una imagen, sería una lista de parámetros extremadamente larga para un método que acepte bytes sin procesar y cree una imagen (es un mapa de bits, jpeg, gif, png, etc. y todos los datos adicionales de cada uno aquellos requieren para crear una imagen válida) no tendría sentido.