Conversione da matrice di byte a immagine


101

Voglio convertire un array di byte in un'immagine.

Questo è il codice del mio database da cui ottengo l'array di byte:

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

Il mio codice di conversione:

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

Quando raggiungo la riga con un commento, si verifica la seguente eccezione: Parameter is not valid.

Come posso riparare qualunque cosa stia causando questa eccezione?


Hai verificato che i byte dell'immagine nella tua query siano validi? Potresti fare un File.WriteAllBytes ("myimage.jpg", byteArrayIn) per verificare.
Holstebroe

Risposte:


110

Stai scrivendo due volte nel tuo flusso di memoria, inoltre non stai eliminando il flusso dopo l'uso. Stai anche chiedendo al decodificatore di immagini di applicare la correzione del colore incorporata.

Prova questo invece:

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

Puoi anche rendere esplicitamente il flusso di memoria non scrivibile dopo l'inizializzazione: new MemoryStream (byteArrayIn, false)
Holstebroe

28
Ciò viola una specifica in MSDN per Image.FromStream (), dove si dice "È necessario mantenere il flusso aperto per la durata dell'immagine". Vedere anche stackoverflow.com/questions/3290060/...
RenniePet

81

Forse mi manca qualcosa, ma per me questo one-liner funziona bene con un array di byte che contiene un'immagine di un file JPEG.

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

MODIFICARE:

Vedi qui per una versione aggiornata di questa risposta: Come convertire l'immagine in array di byte


Grazie AAAh, Finalmente una buona risposta. Perché così tante risposte con il flusso di memoria, mi causano così tanti problemi. molte grazie !
Julian50

Buona risposta! questo può essere utilizzato in una funzione separata mentre tutte le altre proposte che utilizzano MemoryStream non possono (lo stream deve essere tenuto aperto per tutta la vita dell'immagine)
A_L

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

Anche se normalmente non è una buona idea, metto GC.Collect () prima del flusso di memoria. Stavo esaurendo la memoria quando ho precaricato un sacco di file grafici di grandi dimensioni come bytearrays nella memoria e poi li ho trasformati in immagini durante la visualizzazione.
Kayot

9

Vorrei notare che c'è un bug nella soluzione fornita da @ isaias-b.

Quella soluzione presume che stridesia uguale alla lunghezza della riga. Ma non è sempre vero. A causa degli allineamenti di memoria eseguiti da GDI, il passo può essere maggiore della lunghezza della riga. Questo deve essere preso in considerazione. In caso contrario, verrà generata un'immagine spostata non valida. I byte di riempimento in ogni riga verranno ignorati.

Il passo è la larghezza di una singola riga di pixel (una linea di scansione), arrotondata per eccesso a un limite di quattro byte.

Codice fisso:

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

Per illustrare a cosa può portare, generiamo PixelFormat.Format24bppRgbun'immagine sfumata 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);
}

Se copieremo l'intero array così com'è all'indirizzo indicato da bmpData.Scan0, otterremo l'immagine seguente. L'immagine si sposta perché una parte dell'immagine è stata scritta nei byte di riempimento, che è stata ignorata. Inoltre è per questo che l'ultima riga è incompleta:

passo ignorato

Ma se copieremo il puntatore di destinazione dello spostamento riga per riga per bmpData.Stridevalore, verrà generata un'immagine valida:

passo preso in considerazione

Il passo può anche essere negativo:

Se il passo è positivo, la bitmap è dall'alto verso il basso. Se il passo è negativo, la bitmap è dal basso verso l'alto.

Ma non ho lavorato con tali immagini e questo va oltre la mia nota.

Risposta correlata: C # - Buffer RGB da Bitmap diverso da C ++


6

Tutte le risposte presentate presumono che l'array di byte contenga dati in una rappresentazione in formato file noto, come: gif, png o jpg. Ma di recente ho avuto problemi a convertire byte[]i messaggi di posta elettronica contenenti informazioni BGRA linearizzate in modo efficiente in Imageoggetti. Il codice seguente lo risolve utilizzando un Bitmapoggetto.

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

Questa è una leggera variazione di una soluzione pubblicata su questo sito .


come riferimento MSDN: Bitmap (Int32, Int32): "Questo costruttore crea una bitmap con un valore di enumerazione PixelFormat di Format32bppArgb.", che significa che il byte [0] è blu, il byte [1] è verde, il byte [2] è rosso , il byte [3] è alfa, il byte [4] è blu e così via.
brk

1
GRAZIE per aver aggiunto tutti gli "utilizzi" necessari. La maggior parte delle persone lo dimentica ed è un dolore trovarli tutti.
Casey Crookston

6

c'è un approccio semplice come di seguito, puoi usare il FromStreammetodo di un'immagine per fare il trucco, ricorda solo di usare 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

prova (AGGIORNAMENTO)

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

2
Non ha molto senso scrivere l'array di byte nel flusso di memoria dopo che è stato inizializzato con lo stesso array di byte. In realtà non sono sicuro che MemoryStream consenta la scrittura oltre la lunghezza specificata nel costruttore.
Holstebroe

2

Non hai dichiarato returnImage come alcun tipo di variabile :)

Questo dovrebbe aiutare:

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
Codice utile come idea, ma ovviamente non funzionerà come scritto. returnImage deve essere dichiarato al di fuori della sezione try / catch. Anche returnImage deve essere istanziato in 'catch' - creo una bitmap a pixel singolo: var image = new Bitmap (1, 1); Flusso MemoryStream = nuovo MemoryStream (); image.Save (stream, ImageFormat.Jpeg); stream.Position = 0;
Reid

2

Questo è ispirato alla risposta di Holstebroe, oltre ai commenti qui: Ottenere un oggetto Image da un array di byte

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

1

Una fodera:

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

0

Il più delle volte quando ciò accade si tratta di dati errati nella colonna SQL. Questo è il modo corretto per inserire in una colonna immagine:

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

La maggior parte delle persone lo fa in modo errato in questo modo:

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

0

Prima installa questo pacchetto:

Pacchetto di installazione SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Quindi usa il codice sottostante per Cast Byte Array To Image:

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

Per ottenere ImageFormat utilizzare sotto il codice:

IImageFormat format = Image.DetectFormat(byteArray);

Per mutare l'immagine usa sotto il codice:

image.Mutate(x => x.Resize(new Size(1280, 960)));
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.