Tablica bajtowa do konwersji obrazu

101

Chcę przekonwertować tablicę bajtów na obraz.

To jest mój kod bazy danych, z którego otrzymuję tablicę bajtów:

public void Get_Finger_print()
{
    try
    {
        using (SqlConnection thisConnection = new SqlConnection(@"Data Source=" + System.Environment.MachineName + "\\SQLEXPRESS;Initial Catalog=Image_Scanning;Integrated Security=SSPI "))
        {
            thisConnection.Open();
            string query = "select pic from Image_tbl";// where Name='" + name + "'";
            SqlCommand cmd = new SqlCommand(query, thisConnection);
            byte[] image =(byte[]) cmd.ExecuteScalar();
            Image newImage = byteArrayToImage(image);
            Picture.Image = newImage;
            //return image;
        }
    }
    catch (Exception) { }
    //return null;
}

Mój kod konwersji:

public Image byteArrayToImage(byte[] byteArrayIn)
{
    try
    {
        MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
        ms.Write(byteArrayIn, 0, byteArrayIn.Length);
        returnImage = Image.FromStream(ms,true);//Exception occurs here
    }
    catch { }
    return returnImage;
}

Kiedy dochodzę do linii z komentarzem, pojawia się następujący wyjątek: Parameter is not valid.

Jak mogę naprawić wszystko, co powoduje ten wyjątek?

Tanzeel ur Rahman
źródło
Czy sprawdziłeś, czy bajty obrazu w zapytaniu są prawidłowe? Możesz zrobić File.WriteAllBytes ("myimage.jpg", byteArrayIn), aby zweryfikować.
Holstebroe

Odpowiedzi:

110

Piszesz do strumienia pamięci dwa razy, a także nie wyrzucasz strumienia po użyciu. Prosi się również dekoder obrazu o zastosowanie wbudowanej korekcji kolorów.

Spróbuj tego zamiast tego:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}
Holstebroe
źródło
Możesz również jawnie uniemożliwić zapis strumienia pamięci po inicjalizacji: new MemoryStream (byteArrayIn, false)
Holstebroe,
28
To narusza specyfikację w MSDN dla Image.FromStream (), gdzie jest napisane „Musisz pozostawić strumień otwarty przez cały okres istnienia obrazu”. Zobacz także stackoverflow.com/questions/3290060/…
RenniePet
81

Może czegoś brakuje, ale dla mnie ta jednowierszowa działa dobrze z tablicą bajtów, która zawiera obraz pliku JPEG.

Image x = (Bitmap)((new ImageConverter()).ConvertFrom(jpegByteArray));

EDYTOWAĆ:

Zobacz tutaj zaktualizowaną wersję tej odpowiedzi: Jak przekonwertować obraz w tablicy bajtów

RenniePet
źródło
Aha dzięki, wreszcie dobra odpowiedź. Dlaczego tak wiele odpowiedzi ze strumieniem pamięci, sprawia mi to tyle problemów. wielkie dzięki !
Julian50
Dobra odpowiedź! może to być użyte w osobnej funkcji, podczas gdy wszystkie inne propozycje korzystające z MemoryStream nie mogą (strumień musi być otwarty przez cały okres istnienia obrazu)
A_L
20
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}
Ali Gh
źródło
Chociaż zwykle nie jest to dobry pomysł, umieszczam GC.Collect () przed strumieniem Memory. Kończyła mi się pamięć, kiedy załadowałem do pamięci całą masę dużych plików graficznych jako bajteary, a następnie zamieniłem je w obrazy podczas przeglądania.
Kayot
9

Chciałbym zauważyć, że jest błąd w rozwiązaniu dostarczonym przez @ isaias-b.

To rozwiązanie zakłada, że stridejest równa długości rzędu. Ale nie zawsze jest to prawda. Ze względu na wyrównywanie pamięci wykonywane przez GDI, krok może być większy niż długość wiersza. Należy to wziąć pod uwagę. W przeciwnym razie zostanie wygenerowany nieprawidłowy przesunięty obraz. Bajty wypełniające w każdym wierszu zostaną zignorowane.

Krok to szerokość pojedynczego rzędu pikseli (linia skanowania) zaokrąglona do czterobajtowej granicy.

Naprawiono kod:

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

public static class ImageExtensions
{
    public static Image ImageFromRawBgraArray(this byte[] arr, int width, int height, PixelFormat pixelFormat)
    {
        var output = new Bitmap(width, height, pixelFormat);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);

        // Row-by-row copy
        var arrRowLength = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
        var ptr = bmpData.Scan0;
        for (var i = 0; i < height; i++)
        {
            Marshal.Copy(arr, i * arrRowLength, ptr, arrRowLength);
            ptr += bmpData.Stride;
        }

        output.UnlockBits(bmpData);
        return output;
    }
}

Aby zilustrować, do czego może to prowadzić, wygenerujmy PixelFormat.Format24bppRgbobraz gradientu 101x101:

var width = 101;
var height = 101;
var gradient = new byte[width * height * 3 /* bytes per pixel */];
for (int i = 0, pixel = 0; i < gradient.Length; i++, pixel = i / 3)
{
    var x = pixel % height;
    var y = (pixel - x) / width;
    gradient[i] = (byte)((x / (double)(width - 1) + y / (double)(height - 1)) / 2d * 255);
}

Jeśli skopiujemy całą tablicę bez zmian na adres wskazany przez bmpData.Scan0, otrzymamy następujący obraz. Przesunięcie obrazu, ponieważ część obrazu została zapisana w bajtach wypełniających, co zostało zignorowane. Dlatego też ostatni wiersz jest niekompletny:

krok zignorowany

Ale jeśli skopiujemy wskaźnik docelowy przesuwający się wiersz po wierszu według bmpData.Stridewartości, zostanie wygenerowany prawidłowy obraz:

uwzględniony krok

Stride może być również ujemny:

Jeśli krok jest dodatni, mapa bitowa jest odgórna. Jeśli krok jest ujemny, mapa bitowa jest oddolna.

Ale nie pracowałem z takimi obrazami i to jest poza moją uwagą.

Powiązana odpowiedź: C # - Bufor RGB z mapy bitowej różni się od C ++

lorond
źródło
6

Wszystkie przedstawione odpowiedzi zakładają, że tablica bajtów zawiera dane w znanym formacie pliku, takim jak: gif, png lub jpg. Ale ostatnio miałem problem z próbą byte[]wydajnej konwersji s, zawierających zlinearyzowane informacje BGRA, na Imageobiekty. Poniższy kod rozwiązuje to za pomocą Bitmapobiektu.

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
public static class Extensions
{
    public static Image ImageFromRawBgraArray(
        this byte[] arr, int width, int height)
    {
        var output = new Bitmap(width, height);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, 
            ImageLockMode.ReadWrite, output.PixelFormat);
        var ptr = bmpData.Scan0;
        Marshal.Copy(arr, 0, ptr, arr.Length);
        output.UnlockBits(bmpData);
        return output;
    }
}

Jest to nieznaczna odmiana rozwiązania, które zostało zamieszczone na tej stronie .

isaias-b
źródło
jako odniesienie MSDN: Bitmap (Int32, Int32): „Ten konstruktor tworzy bitmapę z wartością wyliczenia PixelFormat Format32bppArgb.”, co oznacza, że ​​bajt [0] jest niebieski, bajt [1] jest zielony, bajt [2] jest czerwony , bajt [3] jest alfa, bajt [4] jest niebieski i tak dalej.
brk
1
DZIĘKUJEMY za dodanie wszystkich potrzebnych "używania". Większość ludzi o tym zapomina i znalezienie ich wszystkich byłoby bardzo trudne.
Casey Crookston
6

istnieje proste podejście, jak poniżej, możesz użyć FromStreammetody obrazu, aby załatwić sprawę, po prostu pamiętaj, aby użyć System.Drawing;

// using image object not file 
public byte[] imageToByteArray(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;
}
Ali
źródło
5

spróbuj (AKTUALIZACJA)

MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
ms.Position = 0; // this is important
returnImage = Image.FromStream(ms,true);
Yahia
źródło
2
Nie ma sensu zapisywanie tablicy bajtów w strumieniu pamięci po jej zainicjowaniu tą samą tablicą bajtów. Właściwie nie jestem pewien, czy MemoryStream umożliwia pisanie poza długością określoną w konstruktorze.
Holstebroe
2

Nie zadeklarowałeś returnImage jako żadnej zmiennej :)

To powinno pomóc:

public Image byteArrayToImage(byte[] byteArrayIn)
{
    try
    {
        MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
        ms.Write(byteArrayIn, 0, byteArrayIn.Length);
        Image returnImage = Image.FromStream(ms,true);
    }
    catch { }
    return returnImage;
}
LeeCambl
źródło
1
Kod przydatny jako pomysł, ale oczywiście nie będzie działał tak, jak napisano. returnImage musi być zadeklarowane poza sekcją try / catch. Również returnImage musi być utworzony w 'catch' - tworzę bitmapę pojedynczego piksela: var image = new Bitmap (1, 1); MemoryStream stream = new MemoryStream (); image.Save (stream, ImageFormat.Jpeg); stream.Position = 0;
Reid
2

Jest to zainspirowane odpowiedzią Holstebroe oraz komentarzami tutaj: Pobieranie obiektu Image z tablicy bajtów

Bitmap newBitmap;
using (MemoryStream memoryStream = new MemoryStream(byteArrayIn))
    using (Image newImage = Image.FromStream(memoryStream))
        newBitmap = new Bitmap(newImage);
return newBitmap;
RenniePet
źródło
1

Jedna wkładka:

Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));
Arvin Amir
źródło
0

W większości przypadków są to złe dane w kolumnie SQL. Oto właściwy sposób wstawiania do kolumny obrazu:

INSERT INTO [TableX] (ImgColumn) VALUES (
(SELECT * FROM OPENROWSET(BULK N'C:\....\Picture 010.png', SINGLE_BLOB) as tempimg))

Większość ludzi robi to niepoprawnie w ten sposób:

INSERT INTO [TableX] (ImgColumn) VALUES ('C:\....\Picture 010.png'))
GPGVM
źródło
0

Najpierw zainstaluj ten pakiet:

Zainstaluj pakiet SixLabors.ImageSharp -Version 1.0.0-beta0007

[SixLabors.ImageSharp] [1] [1]: https://www.nuget.org/packages/SixLabors.ImageSharp

Następnie użyj poniższego kodu dla rzutowania tablicy bajtów na obraz:

Image<Rgba32> image = Image.Load(byteArray); 

Aby uzyskać ImageFormat użyj poniższego kodu:

IImageFormat format = Image.DetectFormat(byteArray);

Dla zmutowanego obrazu użyj poniższego kodu:

image.Mutate(x => x.Resize(new Size(1280, 960)));
AminGolmahalle
źródło