Konvertieren Sie eine Bitmap in ein Byte-Array

233

Gibt es mit C # eine bessere Möglichkeit, ein Windows Bitmapin ein zu konvertieren , byte[]als in eine temporäre Datei zu speichern und das Ergebnis mit einem zu lesen FileStream?

Jeremy McGee
quelle

Antworten:

418

Es gibt verschiedene Möglichkeiten.

ImageConverter

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

Dieser ist praktisch, weil er nicht viel Code erfordert.

Speicherstrom

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

Dieser entspricht dem, was Sie tun, außer dass die Datei im Speicher statt auf der Festplatte gespeichert wird. Obwohl Sie mehr Code haben, haben Sie die Option von ImageFormat und es kann leicht zwischen dem Speichern auf Speicher oder Festplatte geändert werden.

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

Prestomanifest
quelle
2
Sie können auch bitmap.Lockbits und Marshal.Copy für eine einfache schnelle Kopie ohne Zwischenspeicherströme usw. verwenden
deegee
4
Bitte beachten Sie, dass die ImageConverterMethode das Bild als Png speichert, was zu RIESIGEN Dateien führt.
Skolima
8
Sie müssen den Stream nicht schließen, wenn Sie 'using' verwenden
LastTribunal
2
Kann mir jemand ein bisschen mehr Informationen darüber geben, wie dieses Array aufgebaut ist? Beginnt es mit x = 0 und durchläuft jedes y und erhöht dann x? Und dann speichern Sie es wie folgt: [0,0,1,1,1,1,0,0,1,1]?
Raymond Timmermans
1
ImageConverterist kein .net-Standard, den Sie verwenden könntenMemoryStream
Alexandre
95

Ein MemoryStream kann dabei hilfreich sein. Sie könnten es in eine Erweiterungsmethode setzen:

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();
        }
    }
}

Sie könnten es einfach so verwenden:

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

Ich bin teilweise anderer Meinung als die Antwort von prestomanifto in Bezug auf den ImageConverter. Verwenden Sie ImageConverter nicht. Es ist technisch nichts falsch daran, aber die Tatsache, dass es Boxing / Unboxing von Objekt verwendet, sagt mir, dass es Code aus den alten dunklen Stellen des .NET Frameworks ist und nicht ideal für die Bildverarbeitung ist (es ist übertrieben für die Konvertierung in ein Byte [] zumindest), insbesondere wenn Sie Folgendes berücksichtigen.

Ich habe mir den ImageConverterCode angesehen, der vom .Net-Framework verwendet wird, und intern wird Code verwendet, der fast identisch mit dem oben angegebenen ist. Es erstellt ein neues MemoryStream, speichert das Bitmapin dem Format, in dem es bei der Bereitstellung angegeben wurde, und gibt das Array zurück. Überspringen Sie den zusätzlichen Aufwand beim Erstellen einer ImageConverterKlasse mithilfe vonMemoryStream

Christopher Currens
quelle
Schön. Das wird gehen! Ich gehe davon aus, dass Sie den MemoryStream entsorgen möchten - möchten Sie ihn aktualisieren?
Jeremy McGee
Ich habe meine Antwort mit einigen Diskussionen darüber aktualisiert, warum ImageConverter nicht verwendet werden soll, wie in Ihrer ausgewählten Antwort vorgeschlagen, sowie durch Hinzufügen von Entsorgung.
Christopher Currens
Gute Verwendung einer Erweiterungsmethode, ich mag es!
Dude Pascalou
3
+1 für den Blick in ImageConverter und die Berichterstattung über die Ergebnisse Ihrer Forschung. Ich glaube jedoch nicht, dass das, was Sie entdeckt haben, die Aussage "Verwenden Sie ImageConverter nicht" rechtfertigt. Es bietet auf jeden Fall nützliche Dienste, die vom Byte-Array zum Bild in die andere Richtung gehen, z. B. das Einstellen der Bildauflösung (dpi). Und der "zusätzliche Aufwand beim Erstellen einer ImageConverter-Klasse" ist vermutlich vernachlässigbar und muss nur einmal durchgeführt werden, unabhängig davon, wie oft Sie sie verwenden.
RenniePet
1
Ich fand ImageConverter auch hilfreich, da es den Typ des Bildes automatisch bestimmen kann - zum Beispiel, wenn Sie ein Byte-Array des Bildes haben, aber das Format nicht kennen - Sie könnten versuchen, Header zu lesen und Hinweise von dort zu erhalten, aber, na ja, ... mit (Bild img = (Image) myImgConverter.ConvertFrom (byteImage)) und dann die Überprüfung img.PixelFormat usw. ist nur einfacher .. - natürlich je nachdem , was Sie tun wollen
hello_earth
45

Sie können auch nur Marshal.Copy die Bitmap-Daten. Kein zwischengeschalteter Memorystream usw. und eine schnelle Speicherkopie. Dies sollte sowohl auf 24-Bit- als auch auf 32-Bit-Bitmaps funktionieren.

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
quelle
1
Hallo, ich habe diese Methode verwendet, aber ich kann dieses Byte-Array nicht wieder in ein Bild konvertieren. Bitmap bitmap1 = neue Bitmap (neuer MemoryStream (Array)); Es wird eine Ausnahme von ungültigen Parametern ausgelöst. Gibt es einen Fehler?
Howard
1
Hallo, der Kommentarbereich ist zu klein, als dass ich einen vollständigen, sauberen Code veröffentlichen könnte. Am einfachsten ist es, das Array zu pinnen: GCHandle handle = GCHandle.Alloc (bufferarray, GCHandleType.Pinned); den Zeiger auf das Array abrufen IntPtr iptr = Marshal.UnsafeAddrOfPinnedArrayElement (bufferarray, 0); Erstellen Sie dann eine neue Bitmap mit IntPtr: bitmap = new Bitmap (Breite, Höhe, (Breite * 4), PixelFormat.Format32bppArgb, iptr); Sie müssen jedoch die entsprechenden Parameter für die Bitmap-Erstellung für Ihr Bitmap-Format verwenden. Stellen Sie sicher, dass Sie aufräumen: iptr = IntPtr.Zero; handle.Free ();
Deegee
3
Was ist der Fehler? Viele Programmierer, einschließlich mir, verwenden diesen Codestil und er funktioniert einwandfrei.
Deegee
4
@ mini-me - Ich empfehle nachzuschlagen, wie groß der Bitmap-Schritt im Vergleich zur Bitmap-Breite ist. Dies ist kein Fehler in meinem Code. So geht Windows mit Bitmaps um. Stride kann zusätzliche Daten am Ende jeder Zeile (Breite) enthalten, damit jedes Zeilenende endet und an einer 32-Bit-Grenze für die Speicherausrichtung und -leistung beginnt.
Deegee
3
Es ist kein Fehler. msdn.microsoft.com/en-us/library/…
deegee
16

Speichern Sie das Image in einem MemoryStream und greifen Sie dann auf das Byte-Array zu.

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
quelle
Es gibt keine Speichermethode für das Bild.
Prescott Chartier
@ PrescottChartier Das obige Beispiel setzt voraus, dass Sie von einem Typ arbeiten, der von System.Drawing.Image (siehe: docs.microsoft.com/en-us/dotnet/api/… )
Chris Baxter
Yup und ich bekommen System.Drawing.Image does not exist. Also .. nein, funktioniert nicht :(
Prescott Chartier
Sie können hier sehen, was ich versuche: stackoverflow.com/questions/55084620/…
Prescott Chartier
10

Verwenden Sie a MemoryStreamanstelle von a FileStreamwie folgt:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
Jeff Reddy
quelle
Sie wollen wahrscheinlich ToArraynicht GetBuffer.
vcsjones
GetBuffer gibt ein Array von vorzeichenlosen Bytes (Byte-Array) zurück
Jeff Reddy
4
Es könnte Fülldaten enthalten, die nicht Teil des Bildes sind. Aus Dokumenten: 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.Jetzt gibt das Byte-Array von GetBufferdas Bild plus nicht verwendete Bytes zurück, was wahrscheinlich zu einem beschädigten Bild führt.
vcsjones
1
Gut zu wissen. Danke für den Kommentar vcs!
Jeff Reddy
Bei diesem Ansatz wird das Bild als JPEG mit Standardkomprimierungseinstellungen gespeichert, wodurch Komprimierungsartefakte eingeführt werden, die Ihr Bild sichtbar beeinträchtigen können.
Richard Ev
8

Versuche Folgendes:

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

byte[] byteArray = stream.GetBuffer();

Stellen Sie sicher, dass Sie Folgendes verwenden:

System.Drawing & using System.Drawing.Imaging;
Francis Gilbert
quelle
1
Bei diesem Ansatz wird das Bild als JPEG mit Standardkomprimierungseinstellungen gespeichert, wodurch Komprimierungsartefakte eingeführt werden, die Ihr Bild sichtbar beeinträchtigen können.
Richard Ev
Wieder keine Speichermethode für Bitmap.
Prescott Chartier
7

Ich glaube, Sie können einfach tun:

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

Einfacher:

return (byte[])System.ComponentModel.TypeDescriptor.GetConverter(pImagen).ConvertTo(pImagen, typeof(byte[]))
Moises Conejo
quelle
-3

Verwenden Sie dies ganz einfach in einer Zeile:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");
KARAN
quelle
op sagte, er sei nicht an dieser Option interessiert
Mauro Sampietro