Convierta un mapa de bits en una matriz de bytes

233

Usando C #, ¿hay una mejor manera de convertir un Windows Bitmapa un byte[]archivo que guardar en un archivo temporal y leer el resultado usando un FileStream?

Jeremy McGee
fuente

Respuestas:

419

Hay un par de maneras.

ImageConverter

public static byte[] ImageToByte(Image img)
{
    ImageConverter converter = new ImageConverter();
    return (byte[])converter.ConvertTo(img, typeof(byte[]));
}

Este es conveniente porque no requiere mucho código.

Flujo de memoria

public static byte[] ImageToByte2(Image img)
{
    using (var stream = new MemoryStream())
    {
        img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
        return stream.ToArray();
    }
}

Este es equivalente a lo que está haciendo, excepto que el archivo se guarda en la memoria en lugar de en el disco. Aunque tiene más código, tiene la opción de ImageFormat y puede modificarse fácilmente entre guardarlo en la memoria o en el disco.

Fuente: http://www.vcskicks.com/image-to-byte.php

Prestomanifesto
fuente
2
También puede usar bitmap.Lockbits y Marshal. Copie para una copia rápida simple sin ningún flujo de memoria intermedio, etc.
deegee
44
Tenga en cuenta que el ImageConvertermétodo guardará la imagen como Png, lo que dará como resultado archivos ENORMES.
skolima
8
no es necesario cerrar la transmisión cuando se usa 'using'
LastTr tribunal
2
¿Alguien puede darme un poco más de información sobre cómo está estructurada esta matriz? ¿Comienza con x = 0 y recorre cada y y luego incrementa x? Y luego guárdelo así: [0,0,1,1,1,1,0,0,1,1]?
Raymond Timmermans
1
ImageConverterno es el estándar .net que podrías usarMemoryStream
Alexandre
95

Un MemoryStream puede ser útil para esto. Podría ponerlo en un método de extensión:

public static class ImageExtensions
{
    public static byte[] ToByteArray(this Image image, ImageFormat format)
    {
        using(MemoryStream ms = new MemoryStream())
        {
            image.Save(ms, format);
            return ms.ToArray();
        }
    }
}

Podrías usarlo como:

var image = new Bitmap(10, 10);
// Draw your image
byte[] arr = image.ToByteArray(ImageFormat.Bmp);

Estoy parcialmente en desacuerdo con la respuesta de prestomanifto con respecto al ImageConverter. No use ImageConverter. No hay nada técnicamente malo en ello, pero el simple hecho de que use boxing / unboxing from object me dice que es código de los viejos lugares oscuros del marco .NET y no es ideal para usar con el procesamiento de imágenes (es excesivo para convertir a un byte [] al menos), especialmente cuando considera lo siguiente.

Eché un vistazo al ImageConvertercódigo utilizado por el marco .Net, e internamente usa un código casi idéntico al que proporcioné anteriormente. Crea uno nuevo MemoryStream, guarda el Bitmapformato en el que estaba cuando lo proporcionó y devuelve la matriz. Omita la sobrecarga adicional de crear una ImageConverterclase utilizandoMemoryStream

Christopher Currens
fuente
Encantador. ¡Eso servirá! Sin embargo, supongo que querrás deshacerte de MemoryStream. ¿Quieres actualizar?
Jeremy McGee
He actualizado mi respuesta con un poco de discusión sobre por qué no usar ImageConverter, como sugiere su respuesta seleccionada, así como la eliminación de la eliminación.
Christopher Currens
Buen uso de un método de extensión, ¡me gusta!
Amigo Pascalou
3
+1 por buscar en ImageConverter e informar los resultados de su investigación. Pero no creo que lo que haya descubierto justifique la afirmación "No use ImageConverter". Definitivamente proporciona servicios útiles en sentido contrario, desde la matriz de bytes hasta la Imagen, por ejemplo, configurando la resolución de la imagen (dpi). Y la "sobrecarga adicional de crear una clase ImageConverter" es presumiblemente insignificante, y solo debe hacerse una vez, independientemente de cuántas veces la use.
RenniePet
1
También me pareció útil ImageConverter, ya que puede determinar el tipo de imagen automáticamente, por ejemplo, si tiene una matriz de bytes de la imagen, pero no conoce el formato, podría intentar leer encabezados y obtener sugerencias a partir de ahí, pero, bueno, ... usar (Image img = (Image) myImgConverter.ConvertFrom (byteImage)) y luego verificar img.PixelFormat, etc. es simplemente más fácil ... - por supuesto, dependiendo de lo que quieras hacer
hello_earth
46

También puede simplemente Marshal. Copie los datos del mapa de bits. Sin memoria intermedia, etc. y una copia rápida de la memoria. Esto debería funcionar tanto en mapas de bits de 24 bits como de 32 bits.

public static byte[] BitmapToByteArray(Bitmap bitmap)
{

    BitmapData bmpdata = null;

    try
    {
        bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        int numbytes = bmpdata.Stride * bitmap.Height;
        byte[] bytedata = new byte[numbytes];
        IntPtr ptr = bmpdata.Scan0;

        Marshal.Copy(ptr, bytedata, 0, numbytes);

        return bytedata;
    }
    finally
    {
        if (bmpdata != null)
            bitmap.UnlockBits(bmpdata);
    }

}

.

deegee
fuente
1
Hola, utilicé este método, pero no puedo volver a convertir esta matriz de bytes en una imagen. Bitmap bitmap1 = nuevo Bitmap (nuevo MemoryStream (array)); arroja una excepción de parámetros no válidos. ¿Hay algún error?
Howard
1
Hola, la sección de comentarios es demasiado pequeña para que pueda publicar un código limpio completo. La forma más simple es anclar la matriz: GCHandle handle = GCHandle.Alloc (bufferarray, GCHandleType.Pinned); obtener el puntero a la matriz IntPtr iptr = Marshal.UnsafeAddrOfPinnedArrayElement (bufferarray, 0); luego cree un nuevo mapa de bits utilizando IntPtr: mapa de bits = nuevo mapa de bits (ancho, alto, (ancho * 4), PixelFormat.Format32bppArgb, iptr); pero deberá usar los parámetros de creación de mapas de bits adecuados para su formato de mapa de bits. Asegúrese de limpiar: iptr = IntPtr.Zero; handle.Free ();
deegee
3
¿Cuál es el error? Muchos programadores, incluido yo mismo, estamos usando este estilo de código y funciona bien.
deegee
44
@ mini-me: recomiendo buscar qué es el paso de mapa de bits frente al ancho de mapa de bits. Esto no es un error en mi código, así es como Windows maneja los mapas de bits. Stride puede incluir datos adicionales al final de cada línea (ancho) para que cada línea finalice y comience en un límite de 32 bits para la alineación y el rendimiento de la memoria.
deegee
3
No es un error msdn.microsoft.com/en-us/library/…
deegee
16

Guarde la imagen en un MemoryStream y luego tome la matriz de bytes.

http://msdn.microsoft.com/en-us/library/ms142148.aspx

  Byte[] data;

  using (var memoryStream = new MemoryStream())
  {
    image.Save(memoryStream, ImageFormat.Bmp);

    data = memoryStream.ToArray();
  }
Chris Baxter
fuente
No hay método de guardar en la imagen.
Prescott Chartier
@PrescottChartier El ejemplo anterior supone que está trabajando a partir de un tipo derivado de System.Drawing.Image (ver: docs.microsoft.com/en-us/dotnet/api/… )
Chris Baxter
Sí y yo entiendo System.Drawing.Image does not exist. Entonces ... no, no funciona :(
Prescott Chartier
Puedes ver lo que estoy tratando de hacer aquí: stackoverflow.com/questions/55084620/…
Prescott Chartier
10

Use un en MemoryStreamlugar de un FileStream, así:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
Jeff Reddy
fuente
Probablemente quieras ToArray, no GetBuffer.
vcsjones
GetBuffer devuelve una matriz de bytes sin firmar (matriz de bytes)
Jeff Reddy
44
Podría tener datos de relleno que no sean parte de la imagen. Desde documentos: Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method.ahora la matriz de bytes de GetBufferdevolverá la imagen más los bytes no utilizados, lo que probablemente dará como resultado una imagen corrupta.
vcsjones
1
Bueno saber. Gracias por el comentario vcs!
Jeff Reddy
Este enfoque guarda la imagen como un archivo JPEG con la configuración de compresión predeterminada, que introducirá artefactos de compresión que podrían degradar visiblemente su imagen.
Richard Ev
8

Intenta lo siguiente:

MemoryStream stream = new MemoryStream();
Bitmap bitmap = new Bitmap();
bitmap.Save(stream, ImageFormat.Jpeg);

byte[] byteArray = stream.GetBuffer();

Asegúrate de estar usando:

System.Drawing & using System.Drawing.Imaging;
Francis Gilbert
fuente
1
Este enfoque guarda la imagen como un archivo JPEG con la configuración de compresión predeterminada, que introducirá artefactos de compresión que podrían degradar visiblemente su imagen.
Richard Ev
Una vez más, no hay método Guardar en mapa de bits.
Prescott Chartier
7

Creo que simplemente puedes hacer:

ImageConverter converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(img, typeof(byte[]));
Kevek
fuente
6
MemoryStream ms = new MemoryStream();
yourBitmap.Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();
62071072SP
fuente
5

Más simple:

return (byte[])System.ComponentModel.TypeDescriptor.GetConverter(pImagen).ConvertTo(pImagen, typeof(byte[]))
Moisés Conejo
fuente
-4

Muy simple usar esto solo en una línea:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");
KARAN
fuente
op dijo que no está interesado en esta opción
Mauro Sampietro