Najszybszy sposób na konwersję obrazu do tablicy bajtów


106

Tworzę aplikację do udostępniania pulpitu zdalnego, w której przechwytuję obraz pulpitu, kompresuję go i wysyłam do odbiornika. Aby skompresować obraz, muszę go przekonwertować na bajt [].

Obecnie używam tego:

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

Ale nie podoba mi się to, ponieważ muszę zapisać go w ImageFormat, a to może również zużywać zasoby (spowolnienie), a także powodować różne wyniki kompresji. Czytałem o użyciu Marshal.Copy i memcpy, ale nie jestem w stanie tego zrobić zrozumieć ich.

Czy jest więc jakaś inna metoda osiągnięcia tego celu?


zarówno MemoryStream, jak i Image mają metodę usuwania, upewnij się, że wyrzucasz je, ponieważ może to spowodować MemoryLeaks.
abc123

3
@ abc123: Nie musisz pozbywać się MemoryStream; jest to zasób w pełni zarządzany, chyba że używasz go w zdalnej obsłudze. W obu tych przypadkach pozbycie się zasobów byłoby niewłaściwe.
Jon Skeet

1
@JonSkeet interesujące, czy wykonałeś test porównawczy? zobaczyć prędkość, z jaką .net zwalnia obiekt? Wiem, że istnieje podobny argument dla DataTable, a jednak istnieje zauważalna różnica w szybkości, z jaką GarbageCollector zbiera przydzieloną pamięć, gdy używana jest metoda dispose.
abc123

@ abc123: Naprawdę nie spodziewałbym się, że tak będzie - usunięcie strumienia nie robi nic z tablicą, a MemoryStream nie ma finalizatora (w przeciwieństwie do DataTable, która dziedziczy ją po MarshalByValueComponent).
Jon Skeet

2
jakieś ostateczne rozwiązanie z pełnym kodem źródłowym?
Kiquenet

Odpowiedzi:


39

Czy jest więc jakaś inna metoda osiągnięcia tego celu?

Nie. Aby przekonwertować obraz na tablicę bajtów, musisz określić format obrazu - tak samo jak musisz określić kodowanie podczas konwersji tekstu na tablicę bajtów.

Jeśli martwisz się artefaktami kompresji, wybierz format bezstratny. Jeśli martwisz się o zasoby procesora, wybierz format, który nie przeszkadza kompresji - na przykład tylko surowe piksele ARGB. Ale oczywiście doprowadzi to do większej tablicy bajtów.

Zauważ, że jeśli wybrać format, który robi to kompresja, nie ma sensu, a następnie kompresji tablicę bajtów potem - to prawie pewne, że nie mają korzystny wpływ.


12
zamiast „wybierać format bezstratny”, można wybrać, imageIn.RawFormatktóre z prób mają zostać zapisane w bajtach nieprzetworzonego obrazu bez dalszego ponownego kodowania.
Chris F Carroll

52

Istnieje właściwość RawFormat parametru Image, która zwraca format pliku obrazu. Możesz spróbować następujących rzeczy:

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


@ Neil.Allen Jestem tutaj nowy, czy możesz powiedzieć dlaczego?
Khalil Khalaf

3
@FirstStep Bo posprzątaj po sobie :)
Sinaesthetic

@Sinaesthetic Widzę. A procedura polega na umieszczeniu dowolnej funkcji, którą chcę wykonać, w using () {}?
Khalil Khalaf

2
@FirstStep Niezupełnie. Dokładniej: jeśli używasz obiektu, który zaimplementował IDisposable, powinieneś upewnić się, że wywołałeś Dispose (), gdy skończysz z nim, aby wyczyścił wszystkie związane zasoby. Instrukcja using () {} po prostu wywołuje ją za Ciebie, gdy obiekt wychodzi poza zakres tej instrukcji. Możesz więc zrobić myObject.Dispose()lub using(myObject){}- oba zrobić to samo, ale instrukcja using w zasadzie tworzy zakres, który wyczyści za Ciebie.
Sinaesthetic

14

Nie jestem pewien, czy osiągniesz jakieś ogromne zyski z powodów, które wskazał Jon Skeet. Możesz jednak spróbować przetestować metodę TypeConvert.ConvertTo i zobaczyć, jak wypada w porównaniu z użyciem bieżącej metody.

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

Nie można rzutować obiektu typu „System.Byte []” na typ „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
Witamy w stackoverflow.com, czy możesz dodać trochę szczegółów wyjaśniających, dlaczego powyższy przykład kodu pomaga. To jest dla innych użytkowników SO, którzy mogą nie rozumieć tego do końca ... stackoverflow.com/help/how-to-answer
Mack

Dotyczy to plików na bajty, ale OP chciał, aby obiekt rysunkowy został przekonwertowany na bajty. Obiekty rysunkowe mogą być przechowywane w bazach danych, niekoniecznie w systemie plików, jako tablica bajtów i dlatego muszą być przekształcane tam iz powrotem ... ale nie jako pliki w FileStream, które mają zostać przekonwertowane na bajty - chyba że podczas wstępne przesyłanie.
vapcguy

Pomogło mi to, ponieważ chciałem to zrobić z plikami. Dobrze mieć w pobliżu jako związane z tym.
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

Najszybszy sposób, w jaki mogłem się dowiedzieć, jest następujący:

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

Mam nadzieję, że będę przydatny


Uważaj z tym, szczególnie jeśli używasz WPF, gdzie masz System.Windows.Controls.Imageobiekty. Jeśli chcesz przekonwertować jeden z nich na bajty i przekażesz go do tej linii jako InputImg, to nie zadziała. Oczekuje System.Drawing.Imageobiektu.
vapcguy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.