Il modo più veloce per convertire l'immagine in un array di byte


106

Sto realizzando un'applicazione di condivisione Desktop remoto in cui acquisisco un'immagine del desktop e la comprimo e la invio al destinatario. Per comprimere l'immagine devo convertirla in un byte [].

Attualmente sto usando questo:

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

Ma non mi piace perché devo salvarlo in un ImageFormat e questo potrebbe anche utilizzare risorse (Slow Down) oltre a produrre risultati di compressione diversi.Ho letto di usare Marshal.Copy e memcpy ma non sono in grado di farlo capiscili.

Quindi esiste un altro metodo per raggiungere questo obiettivo?


Sia MemoryStream che Image hanno un metodo di smaltimento, assicurati di eliminarli poiché ciò può causare MemoryLeaks.
abc123

3
@ abc123: non è necessario smaltire un MemoryStream; è una risorsa interamente gestita, a meno che non la utilizzi in remoto. In entrambi i casi sarebbe inopportuno smaltire la risorsa.
Jon Skeet

1
@ JonSkeet interessante, hai fatto un benchmark su questo? vedere la velocità alla quale .net rilascia l'oggetto? So che esiste un argomento simile per DataTable e tuttavia esiste una notevole differenza nella velocità con cui GarbageCollector raccoglie la memoria allocata quando viene utilizzato un dispose.
abc123

@ abc123: davvero non mi aspetto che ci sia - l'eliminazione del flusso non fa nulla per l'array e MemoryStream non ha un finalizzatore (a differenza di DataTable, che ne eredita uno da MarshalByValueComponent).
Jon Skeet

2
qualsiasi soluzione finale con il codice sorgente completo?
Kiquenet

Risposte:


39

Quindi esiste un altro metodo per raggiungere questo obiettivo?

No. Per convertire un'immagine in un array di byte si deve specificare un formato di immagine - proprio come è necessario specificare una codifica quando si converte il testo in un array di byte.

Se sei preoccupato per gli artefatti di compressione, scegli un formato senza perdita di dati. Se sei preoccupato per le risorse della CPU, scegli un formato che non si preoccupi della compressione, ad esempio solo pixel ARGB non elaborati. Ma ovviamente questo porterà a un array di byte più grande.

Si noti che se si sceglie un formato che fa comprendere la compressione, non c'è alcun punto in poi comprimere la matrice di byte in seguito - è quasi certo di avere alcun effetto benefico.


12
invece di 'scegliere un formato senza perdita di dati' puoi scegliere imageIn.RawFormatquale tentativo di salvare i byte dell'immagine grezza senza ulteriore ricodifica.
Chris F Carroll

52

Esiste una proprietà RawFormat del parametro Image che restituisce il formato file dell'immagine. Potresti provare quanto segue:

// extension method
public static byte[] imageToByteArray(this System.Drawing.Image image)
{
    using(var ms = new MemoryStream())
    {
        image.Save(ms, image.RawFormat);
        return ms.ToArray();
    }
}

9
Suggerirei di eliminare MemoryStream o di racchiudere il corpo di questo metodo in un'istruzione using () {}
Neil.Allen

@ Neil.Allen Sono nuovo qui, puoi dirmi perché?
Khalil Khalaf

3
@FirstStep Perché ripulisci dopo te stesso :)
Sinaesthetic

@ Sinestetico vedo. E la routine consiste nel mettere qualunque funzione voglio eseguire, in un using () {}?
Khalil Khalaf

2
@ FirstStep Non proprio. Più precisamente: se usi un oggetto che ha implementato IDisposable, dovresti assicurarti di chiamare Dispose () quando hai finito con esso in modo che ripulirà tutte le risorse che ha legato. L'istruzione using () {} la chiama per te quando l'oggetto esce dall'ambito di tale istruzione. Quindi puoi fare myObject.Dispose()o using(myObject){}- entrambi fanno la stessa cosa, ma l'istruzione using fondamentalmente crea uno scope che ripulirà per te.
Sinaesthetic

14

Non sono sicuro che otterrai enormi guadagni per i motivi sottolineati da Jon Skeet. Tuttavia, potresti provare a confrontare il metodo TypeConvert.ConvertTo e vedere come si confronta con il tuo metodo corrente.

ImageConverter converter = new ImageConverter();
byte[] imgArray = (byte[])converter.ConvertTo(imageIn, typeof(byte[]));

Impossibile eseguire il cast dell'oggetto di tipo "System.Byte []" per digitare "System.Drawing.Image".
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;
    }

5
Benvenuto su stackoverflow.com, potresti aggiungere un piccolo dettaglio che spiega perché il codice di esempio sopra aiuta. È per altri utenti SO che potrebbero non capirlo completamente ... stackoverflow.com/help/how-to-answer
Mack

Questo è per i file in byte, ma l'OP voleva un oggetto di disegno convertito in byte. Gli oggetti di disegno possono essere memorizzati nei database, non necessariamente nel file system, come un array di byte, e quindi devono essere trasformati avanti e indietro ... ma non come file in un FileStream da convertire in byte, a meno che, forse, durante il caricamento iniziale.
vapcguy

Questo mi ha aiutato mentre stavo cercando di farlo con i file. Buono da avere in giro come relativo.
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;
    }
}

2

Il modo più veloce per scoprirlo è questo:

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

Spero di essere utile


Fai attenzione con questo, specialmente se usi WPF dove avresti System.Windows.Controls.Imageoggetti. Se vuoi convertire uno di questi in byte e lo passi a questa riga come InputImg, non funzionerà. Si aspetta un System.Drawing.Imageoggetto.
vapcguy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.