Umwandlung von Byte-Array in Bild


100

Ich möchte ein Byte-Array in ein Bild konvertieren.

Dies ist mein Datenbankcode, von dem ich das Byte-Array erhalte:

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

Mein Conversion-Code:

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

Wenn ich die Zeile mit einem Kommentar erreiche, tritt die folgende Ausnahme auf: Parameter is not valid.

Wie kann ich beheben, was diese Ausnahme verursacht?


Haben Sie überprüft, ob die Bildbytes in Ihrer Abfrage gültig sind? Sie können eine File.WriteAllBytes ("myimage.jpg", byteArrayIn) durchführen, um dies zu überprüfen.
Holstebroe

Antworten:


109

Sie schreiben zweimal in Ihren Speicher-Stream und entsorgen den Stream nach der Verwendung nicht. Sie fordern den Bilddecoder außerdem auf, eine eingebettete Farbkorrektur anzuwenden.

Versuchen Sie stattdessen Folgendes:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}

Sie können den Speicherstrom auch explizit nach der Initialisierung nicht beschreibbar machen: new MemoryStream (byteArrayIn, false)
Holstebroe

28
Dies verstößt gegen eine Spezifikation in MSDN für Image.FromStream (), in der steht: "Sie müssen den Stream für die Lebensdauer des Image offen halten." Siehe auch stackoverflow.com/questions/3290060/…
RenniePet

81

Vielleicht fehlt mir etwas, aber für mich funktioniert dieser Einzeiler gut mit einem Byte-Array, das ein Bild einer JPEG-Datei enthält.

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

BEARBEITEN:

Eine aktualisierte Version dieser Antwort finden Sie hier: So konvertieren Sie ein Bild in ein Byte-Array


AAAh danke, endlich eine gute Antwort. Warum so viele Antworten mit Speicherstrom, es verursacht mir so viele Probleme. Vielen Dank !
Julian50

Gute Antwort! Dies kann in einer separaten Funktion verwendet werden, während alle anderen Vorschläge, die MemoryStream verwenden, dies nicht können (Stream muss für die Lebensdauer des Bildes geöffnet
bleiben

20
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}

Während es normalerweise keine gute Idee ist, habe ich ein GC.Collect () vor den Speicher-Stream gestellt. Mir ging der Speicher aus, als ich viele große Grafikdateien als Bytearrays in den Speicher vorinstallierte und sie dann beim Anzeigen in Bilder umwandelte.
Kayot

9

Ich möchte darauf hinweisen, dass die von @ isaias-b bereitgestellte Lösung einen Fehler enthält.

Diese Lösung setzt voraus, dass dies der strideZeilenlänge entspricht. Aber es ist nicht immer wahr. Aufgrund der von GDI durchgeführten Speicherausrichtungen kann der Schritt größer als die Zeilenlänge sein. Dies muss berücksichtigt werden. Andernfalls wird ein ungültiges verschobenes Bild generiert. Das Auffüllen von Bytes in jeder Zeile wird ignoriert.

Der Schritt ist die Breite einer einzelnen Pixelreihe (einer Scanlinie), die auf eine Vier-Byte-Grenze aufgerundet ist.

Fester Code:

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

Um zu veranschaulichen, wozu es führen kann, generieren wir ein PixelFormat.Format24bppRgbVerlaufsbild 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);
}

Wenn wir das gesamte Array so kopieren, wie es ist bmpData.Scan0, erhalten wir das folgende Bild. Bildverschiebung, da ein Teil des Bildes in Füllbytes geschrieben wurde, wurde ignoriert. Auch deshalb ist die letzte Zeile unvollständig:

Schritt ignoriert

Wenn wir jedoch den zeilenweisen Verschieben des Zielzeigers nach bmpData.StrideWert kopieren , wird ein gültiges Bild generiert:

Schritt berücksichtigt

Schritt kann auch negativ sein:

Wenn der Schritt positiv ist, ist die Bitmap von oben nach unten. Wenn der Schritt negativ ist, ist die Bitmap von unten nach oben.

Aber ich habe nicht mit solchen Bildern gearbeitet und das ist jenseits meiner Notiz.

Verwandte Antwort: C # - RGB-Puffer von Bitmap anders als C ++


6

Bei allen dargestellten Antworten wird davon ausgegangen, dass das Byte-Array Daten in einer bekannten Dateiformatdarstellung enthält, z. B.: Gif, png oder jpg. Aber ich hatte kürzlich ein Problem beim Versuch, byte[]s, das linearisierte BGRA-Informationen enthält, effizient in ImageObjekte umzuwandeln . Der folgende Code löst es mit einem BitmapObjekt.

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

Dies ist eine geringfügige Variation einer Lösung, die auf dieser Website veröffentlicht wurde .


als Referenz MSDN: Bitmap (Int32, Int32): "Dieser Konstruktor erstellt eine Bitmap mit einem PixelFormat-Aufzählungswert von Format32bppArgb." Dies bedeutet, dass Byte [0] blau, Byte [1] grün und Byte [2] rot ist , Byte [3] ist Alpha, Byte [4] ist blau und so weiter.
Brk

1
DANKE für das Hinzufügen aller benötigten "using" s. Die meisten Menschen vergessen das und es ist ein Schmerz, sie alle zu finden.
Casey Crookston

6

Es gibt einen einfachen Ansatz wie unten: Sie können die FromStreamMethode eines Bildes verwenden, um den Trick auszuführen. Denken Sie daran, ihn zu verwenden 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;
}

5

try (UPDATE)

MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
ms.Position = 0; // this is important
returnImage = Image.FromStream(ms,true);

2
Es ist wenig sinnvoll, das Byte-Array in den Speicherstrom zu schreiben, nachdem es mit demselben Byte-Array initialisiert wurde. Eigentlich bin ich mir nicht sicher, ob MemoryStream das Schreiben über die im Konstruktor angegebene Länge hinaus zulässt.
Holstebroe

2

Sie haben returnImage nicht als irgendeine Variable deklariert :)

Dies sollte helfen:

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

1
Code als Idee nützlich, funktioniert aber natürlich nicht wie geschrieben. returnImage muss außerhalb des Abschnitts try / catch deklariert werden. Auch returnImage muss in 'catch' instanziiert werden - ich erstelle eine Einzelpixel-Bitmap: var image = new Bitmap (1, 1); MemoryStream-Stream = neuer MemoryStream (); image.Save (Stream, ImageFormat.Jpeg); stream.Position = 0;
Reid

2

Dies ist inspiriert von Holstebroes Antwort und Kommentaren hier: Abrufen eines Image-Objekts aus einem Byte-Array

Bitmap newBitmap;
using (MemoryStream memoryStream = new MemoryStream(byteArrayIn))
    using (Image newImage = Image.FromStream(memoryStream))
        newBitmap = new Bitmap(newImage);
return newBitmap;

1

Einzeiler:

Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));

0

In den meisten Fällen handelt es sich dabei um fehlerhafte Daten in der SQL-Spalte. Dies ist der richtige Weg, um in eine Bildspalte einzufügen:

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

Die meisten Leute machen das falsch:

INSERT INTO [TableX] (ImgColumn) VALUES ('C:\....\Picture 010.png'))

0

Installieren Sie zuerst dieses Paket:

Installationspaket SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Verwenden Sie dann den folgenden Code für Cast Byte Array To Image:

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

Verwenden Sie den folgenden Code, um ImageFormat abzurufen:

IImageFormat format = Image.DetectFormat(byteArray);

Für mutiertes Bild Verwenden Sie den folgenden Code:

image.Mutate(x => x.Resize(new Size(1280, 960)));
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.