Der schnellste Weg, um ein Bild in ein Byte-Array zu konvertieren

106

Ich erstelle eine Remotedesktop-Freigabeanwendung, in der ich ein Bild des Desktops aufnehme, komprimiere und an den Empfänger sende. Um das Bild zu komprimieren, muss ich es in ein Byte [] konvertieren.

Derzeit benutze ich dies:

public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
    MemoryStream ms = new MemoryStream();
    imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
    return  ms.ToArray();
}

public Image byteArrayToImage(byte[] byteArrayIn)
{
     MemoryStream ms = new MemoryStream(byteArrayIn);
     Image returnImage = Image.FromStream(ms);
     return returnImage;
}

Aber ich mag es nicht, weil ich es in einem ImageFormat speichern muss und das möglicherweise auch Ressourcen verbraucht (Verlangsamung) sowie unterschiedliche Komprimierungsergebnisse erzeugt. Ich habe weiter mit Marshal.Copy und memcpy gelesen, kann es aber nicht verstehe sie.

Gibt es also eine andere Methode, um dieses Ziel zu erreichen?

user2529551
quelle
Sowohl MemoryStream als auch Image verfügen über eine Entsorgungsmethode. Stellen Sie sicher, dass Sie sie entsorgen, da dies zu MemoryLeaks führen kann.
abc123
3
@ abc123: Sie müssen a nicht entsorgen MemoryStream; Es handelt sich um eine vollständig verwaltete Ressource, es sei denn, Sie verwenden sie beim Remoting. In beiden Fällen wäre es unangemessen, die Ressource zu entsorgen.
Jon Skeet
1
@ JonSkeet interessant, hast du einen Benchmark dafür gemacht? um zu sehen, mit welcher Geschwindigkeit .net das Objekt freigibt? Ich weiß, dass es ein ähnliches Argument für DataTable gibt, und dennoch gibt es einen spürbaren Unterschied in der Geschwindigkeit, mit der der GarbageCollector den zugewiesenen Speicher sammelt, wenn eine Entsorgung verwendet wird.
abc123
@ abc123: Ich würde wirklich nicht erwarten, dass es das gibt - das Entsorgen des Streams hat nichts mit dem Array zu tun, und MemoryStream hat keinen Finalizer (im Gegensatz zu DataTable, das einen von MarshalByValueComponent erbt).
Jon Skeet
2
Gibt es eine endgültige Lösung mit vollständigem Quellcode?
Kiquenet

Antworten:

39

Gibt es also eine andere Methode, um dieses Ziel zu erreichen?

Nein . Um ein Bild zu einer Byte - Array zu konvertieren Sie haben ein Bildformat angeben - so wie Sie eine Codierung angeben, wenn Sie Text in einer Byte - Array zu konvertieren.

Wenn Sie sich Sorgen über Komprimierungsartefakte machen, wählen Sie ein verlustfreies Format. Wenn Sie sich Sorgen um die CPU-Ressourcen machen, wählen Sie ein Format, das die Komprimierung nicht stört - zum Beispiel nur rohe ARGB-Pixel. Aber das führt natürlich zu einem größeren Byte-Array.

Beachten Sie, dass wenn Sie ein Format auswählen , das tut Kompression ist, gibt es keinen Punkt in dann danach den Byte - Array Komprimieren - es ist fast sicher ist , keine positive Wirkung haben.

Jon Skeet
quelle
12
Anstatt ein verlustfreies Format auszuwählen, können Sie auswählen, imageIn.RawFormatwelche Versuche ausgeführt werden sollen, um die Rohbildbytes ohne weitere Neucodierung zu speichern.
Chris F Carroll
52

Es gibt eine RawFormat-Eigenschaft des Image-Parameters, die das Dateiformat des Bildes zurückgibt. Sie könnten Folgendes versuchen:

// extension method
public static byte[] imageToByteArray(this System.Drawing.Image image)
{
    using(var ms = new MemoryStream())
    {
        image.Save(ms, image.RawFormat);
        return ms.ToArray();
    }
}
Newt
quelle
9
Ich würde empfehlen, entweder den MemoryStream zu entsorgen oder den Hauptteil dieser Methode in eine using () {}
-Anweisung zu verpacken
@ Neil.Allen Ich bin neu hier Kannst du bitte sagen warum?
Khalil Khalaf
3
@FirstStep Weil aufräumen nach sich selbst :)
Sinaesthetic
@Sinaesthetic Ich sehe. Und die Routine besteht darin, jede Funktion, die ich ausführen möchte, in ein using () {} zu setzen?
Khalil Khalaf
2
@ FirstStep Nicht ganz. Genauer gesagt: Wenn Sie ein Objekt verwenden, das IDisposable implementiert hat, sollten Sie Dispose () aufrufen, wenn Sie damit fertig sind, damit alle gebundenen Ressourcen bereinigt werden. Die using () {} -Anweisung ruft sie nur für Sie auf, wenn das Objekt den Gültigkeitsbereich dieser Anweisung verlässt. Sie können also myObject.Dispose()oder using(myObject){}- beide tun dasselbe, aber die using-Anweisung erstellt im Grunde einen Bereich, der für Sie bereinigt.
Sinaesthetic
14

Ich bin mir nicht sicher, ob Sie aus Gründen, auf die Jon Skeet hingewiesen hat, große Gewinne erzielen werden. Sie können jedoch versuchen, die TypeConvert.ConvertTo- Methode zu vergleichen und zu sehen, wie sie mit Ihrer aktuellen Methode verglichen wird.

ImageConverter converter = new ImageConverter();
byte[] imgArray = (byte[])converter.ConvertTo(imageIn, typeof(byte[]));
TastaturP
quelle
Objekt vom Typ 'System.Byte []' kann nicht in 'System.Drawing.Image' umgewandelt werden.
user123
14
public static byte[] ReadImageFile(string imageLocation)
    {
        byte[] imageData = null;
        FileInfo fileInfo = new FileInfo(imageLocation);
        long imageFileLength = fileInfo.Length;
        FileStream fs = new FileStream(imageLocation, FileMode.Open, FileAccess.Read);
        BinaryReader br = new BinaryReader(fs);
        imageData = br.ReadBytes((int)imageFileLength);
        return imageData;
    }
Bhadresh
quelle
5
Willkommen bei stackoverflow.com. Können Sie ein kleines Detail hinzufügen, das erklärt, warum das obige Codebeispiel hilfreich ist? Es ist für andere SO-Benutzer, die es möglicherweise nicht ganz verstehen ... stackoverflow.com/help/how-to-answer
Mack
Dies gilt für Dateien in Bytes, aber das OP wollte ein Zeichenobjekt, das in Bytes konvertiert wird. Zeichnungsobjekte können in Datenbanken, nicht unbedingt im Dateisystem, als Array von Bytes gespeichert werden und müssen daher hin und her transformiert werden ... aber nicht als Dateien in einem FileStream, die in Bytes konvertiert werden sollen - außer vielleicht während des Erster Upload.
Vapcguy
Dies hat mir geholfen, als ich dies mit Dateien tun wollte. Gut zu haben, wie es verwandt ist.
Justin
5
public static class HelperExtensions
{
    //Convert Image to byte[] array:
    public static byte[] ToByteArray(this Image imageIn)
    {
        var ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
        return ms.ToArray();
    }

    //Convert byte[] array to Image:
    public static Image ToImage(this byte[] byteArrayIn)
    {
        var ms = new MemoryStream(byteArrayIn);
        var returnImage = Image.FromStream(ms);
        return returnImage;
    }
}
Ahmad Aghazadeh
quelle
2

Der schnellste Weg, den ich herausfinden konnte, ist folgender:

var myArray = (byte[]) new ImageConverter().ConvertTo(InputImg, typeof(byte[]));

Ich hoffe, nützlich zu sein

Alireza Amini
quelle
Seien Sie vorsichtig damit, insbesondere wenn Sie WPF verwenden, wo Sie System.Windows.Controls.ImageObjekte haben würden . Wenn Sie eines davon in Bytes konvertieren möchten und es als an diese Zeile übergeben InputImg, funktioniert dies nicht. Es erwartet ein System.Drawing.ImageObjekt.
Vapcguy