Konwertuj mapę bitową na tablicę bajtów

233

Używając C #, czy jest lepszy sposób na konwersję systemu Windows Bitmapna byte[]niż zapisywanie do pliku tymczasowego i odczytywanie wyniku za pomocą FileStream?

Jeremy McGee
źródło

Odpowiedzi:

418

Istnieje kilka sposobów.

ImageConverter

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

Ten jest wygodny, ponieważ nie wymaga dużo kodu.

Strumień pamięci

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

Ten jest równoważny z tym, co robisz, z wyjątkiem tego, że plik jest zapisywany w pamięci zamiast na dysku. Chociaż więcej kodu masz opcję ImageFormat i można go łatwo modyfikować między zapisywaniem w pamięci lub na dysku.

Źródło: http://www.vcskicks.com/image-to-byte.php

prestomanifesto
źródło
2
Możesz także użyć bitmapy.Lockbits i Marshal.Copy do prostego szybkiego kopiowania bez żadnych pośrednich memorystreams itp.
deegee
4
Należy pamiętać, że ImageConvertermetoda zapisze obraz jako Png, co spowoduje OGROMNE pliki.
skolima,
8
nie musisz zamykać strumienia, używając „using”
LastTribunal
2
Czy ktoś może mi podać nieco więcej informacji na temat struktury tej tablicy? Czy zaczyna się od x = 0 i zmienia się co y, a następnie zwiększa x? A potem zapisz to w ten sposób: [0,0,1,1,1,1,1,0,0,1,1]?
Raymond Timmermans
1
ImageConverternie jest standardem .net, którego możesz użyćMemoryStream
Alexandre
95

MemoryStream może być pomocne w tej sprawie. Możesz umieścić go w metodzie rozszerzenia:

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

Możesz po prostu użyć go w następujący sposób:

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

Częściowo nie zgadzam się z odpowiedzią prestomanifto dotyczącą ImageConverter. Nie używaj ImageConverter. Nie ma w tym nic złego pod względem technicznym, ale sam fakt, że używa on boksowania / rozpakowywania z obiektu, mówi mi, że to kod ze starych ciemnych miejsc .NET Framework i nie jest idealny do użycia z przetwarzaniem obrazu (to przesada w przypadku konwersji na bajt [] przynajmniej), zwłaszcza jeśli weźmiesz pod uwagę następujące kwestie.

Przyjrzałem się ImageConverterkodowi używanemu przez platformę .Net i wewnętrznie używa kodu prawie identycznego z tym, który podałem powyżej. Tworzy nowy MemoryStream, zapisuje Bitmapw jakimkolwiek formacie, w jakim był, gdy go podałeś, i zwraca tablicę. Pomiń dodatkowe koszty związane z tworzeniem ImageConverterklasy za pomocąMemoryStream

Christopher Currens
źródło
Śliczny. To wystarczy! Rozumiem, że będziesz chciał pozbyć się MemoryStream - chcesz zaktualizować?
Jeremy McGee,
Zaktualizowałem swoją odpowiedź, dodając dyskusję na temat tego, dlaczego nie używać ImageConverter, jak sugeruje wybrana odpowiedź, a także dodanie do utylizacji.
Christopher Currens,
Fajne użycie metody rozszerzenia, podoba mi się!
Koleś Pascalou,
3
+1 za spojrzenie na ImageConverter i raportowanie wyników swoich badań. Ale nie sądzę, że to, co odkryłeś, uzasadnia stwierdzenie „Nie używaj ImageConvertera”. Zapewnia to z pewnością użyteczne usługi idące w drugą stronę, od tablicy bajtów do obrazu, na przykład ustawienie rozdzielczości obrazu (dpi). „Dodatkowy narzut związany z tworzeniem klasy ImageConverter” jest prawdopodobnie nieistotny i należy go wykonać tylko raz, niezależnie od tego, ile razy go użyjesz.
RenniePet
1
Uważam też, że ImageConverter jest pomocny, ponieważ może automatycznie określać typ obrazu - na przykład jeśli masz tablicę bajtów obrazu, ale nie znasz formatu - możesz spróbować odczytać nagłówki i uzyskać od niego wskazówki, ale, cóż, ... użycie (Image img = (Image) myImgConverter.ConvertFrom (byteImage)), a następnie sprawdzenie img.PixelFormat itp. jest po prostu łatwiejsze ... - oczywiście w zależności od tego, co chcesz zrobić
hello_earth
45

Możesz także po prostu Marshal. Skopiować dane mapy bitowej. Brak pośredniego memorystream itp. I szybka kopia pamięci. Powinno to działać zarówno na bitmapach 24-bitowych, jak i 32-bitowych.

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
źródło
1
Cześć, użyłem tej metody, ale nie mogę ponownie przekonwertować tej tablicy bajtów z powrotem na obraz. Bitmap bitmap1 = nowa bitmapa (nowa MemoryStream (tablica)); zgłasza wyjątek niepoprawnych parametrów. Czy jest jakiś błąd?
Howard
1
Cześć, sekcja komentarzy jest dla mnie za mała, aby opublikować pełny, czysty kod. Najprostszym sposobem jest przypięcie tablicy: GCHandle handle = GCHandle.Alloc (bufferarray, GCHandleType.Pinned); pobierz wskaźnik do tablicy IntPtr iptr = Marshal.UnsafeAddrOfPinnedArrayElement (bufferarray, 0); następnie utwórz nową mapę bitową za pomocą IntPtr: bitmap = nowa mapa bitowa (szerokość, wysokość, (szerokość * 4), PixelFormat.Format32bppArgb, iptr); ale będziesz musiał użyć odpowiednich parametrów tworzenia bitmapy dla swojego formatu bitmapy. Pamiętaj, aby wyczyścić: iptr = IntPtr.Zero; handle.Free ();
deegee
3
Co to za błąd? Wielu programistów, w tym ja, używa tego stylu kodu i działa dobrze.
deegee
4
@ mini-me - polecam sprawdzić, jaki jest krok bitmapy w porównaniu do szerokości mapy bitowej. To nie jest błąd w moim kodzie, tak Windows obsługuje bitmapy. Krok może zawierać dodatkowe dane na końcu każdej linii (szerokości), aby każdy koniec linii i zaczynał się na 32-bitowej granicy w celu wyrównania pamięci i wydajności.
deegee
3
To nie jest błąd. msdn.microsoft.com/en-us/library/…
deegee
16

Zapisz obraz w MemoryStream, a następnie chwyć tablicę bajtów.

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
źródło
Nie ma metody Save na obrazie.
Prescott Chartier
@PrescottChartier Powyższy przykład zakłada, że ​​pracujesz na typie pochodzącym z System.Drawing.Image (patrz: docs.microsoft.com/en-us/dotnet/api/... )
Chris Baxter
Tak i ja System.Drawing.Image does not exist. Więc .. nie, nie działa :(
Prescott Chartier
Możesz zobaczyć, co próbuję zrobić tutaj: stackoverflow.com/questions/55084620/…
Prescott Chartier
10

Użyj MemoryStreamzamiast a FileStream, jak poniżej:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
Jeff Reddy
źródło
Prawdopodobnie chcesz ToArray, nie GetBuffer.
vcsjones
GetBuffer zwraca tablicę bajtów bez znaku (tablica bajtów)
Jeff Reddy,
4
Może zawierać dane wypełniające, które nie są częścią obrazu. Z dokumentów: 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.teraz tablica bajtów z GetBufferzwróci obraz plus nieużywane bajty, co prawdopodobnie spowoduje uszkodzenie obrazu.
vcsjones
1
Dobrze wiedzieć. Dzięki za komentarz vcs!
Jeff Reddy,
Takie podejście zapisuje obraz jako plik JPEG z domyślnymi ustawieniami kompresji, które wprowadzą artefakty kompresji, które mogą wyraźnie pogorszyć obraz.
Richard Ev,
8

Spróbuj wykonać następujące czynności:

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

byte[] byteArray = stream.GetBuffer();

Upewnij się, że używasz:

System.Drawing & using System.Drawing.Imaging;
Francis Gilbert
źródło
1
Takie podejście zapisuje obraz jako plik JPEG z domyślnymi ustawieniami kompresji, które wprowadzą artefakty kompresji, które mogą wyraźnie pogorszyć obraz.
Richard Ev,
Znów nie ma metody Save na bitmapie.
Prescott Chartier
7

Wierzę, że możesz po prostu zrobić:

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

Prościej:

return (byte[])System.ComponentModel.TypeDescriptor.GetConverter(pImagen).ConvertTo(pImagen, typeof(byte[]))
Moises Conejo
źródło
-3

Bardzo proste użycie tego tylko w jednym wierszu:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");
KARAN
źródło
op powiedział, że nie jest zainteresowany tą opcją
Mauro Sampietro,