Conversión de matriz de bytes a imagen


101

Quiero convertir una matriz de bytes en una imagen.

Este es el código de mi base de datos de donde obtengo la matriz de bytes:

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

Mi código de conversión:

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

Cuando llego a la línea con un comentario, ocurre la siguiente excepción: Parameter is not valid.

¿Cómo puedo solucionar lo que esté causando esta excepción?


¿Ha comprobado que los bytes de la imagen de su consulta son válidos? Puede hacer un File.WriteAllBytes ("myimage.jpg", byteArrayIn) para verificar.
Holstebroe

Respuestas:


110

Está escribiendo en su flujo de memoria dos veces, y tampoco está desechando el flujo después de su uso. También le solicita al decodificador de imágenes que aplique la corrección de color incorporada.

Prueba esto en su lugar:

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

También puede hacer explícitamente que el flujo de memoria no se pueda escribir después de la inicialización: new MemoryStream (byteArrayIn, false)
Holstebroe

28
Esto viola una especificación en MSDN para Image.FromStream (), donde dice "Debe mantener la transmisión abierta durante la vida útil de la imagen". Véase también stackoverflow.com/questions/3290060/…
RenniePet

81

Tal vez me esté perdiendo algo, pero para mí este de una sola línea funciona bien con una matriz de bytes que contiene una imagen de un archivo JPEG.

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

EDITAR:

Consulte aquí para obtener una versión actualizada de esta respuesta: Cómo convertir una imagen en una matriz de bytes


AAAh gracias, Finalmente una buena respuesta. Por qué tantas respuestas con flujo de memoria, me causa tantos problemas. muchas gracias !
Julian50

¡Buena respuesta! esto se puede usar en una función separada, mientras que todas las demás propuestas que usan MemoryStream no pueden (la transmisión debe mantenerse abierta durante la vida útil de la imagen)
A_L

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

Aunque normalmente no es una buena idea, coloco un GC.Collect () antes del flujo de memoria. Me estaba quedando sin memoria cuando precargué una gran cantidad de archivos gráficos grandes como bytearrays en la memoria y luego los convertí en imágenes durante la visualización.
Kayot

9

Me gustaría señalar que hay un error en la solución proporcionada por @ isaias-b.

Esa solución supone que stridees igual a la longitud de la fila. Pero no siempre es cierto. Debido a las alineaciones de memoria realizadas por GDI, el paso puede ser mayor que la longitud de la fila. Esto debe tenerse en cuenta. De lo contrario, se generará una imagen desplazada no válida. Se ignorarán los bytes de relleno en cada fila.

La zancada es el ancho de una sola fila de píxeles (una línea de exploración), redondeada a un límite de cuatro bytes.

Código fijo:

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

Para ilustrar a qué puede conducir, generemos una PixelFormat.Format24bppRgbimagen de degradado de 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);
}

Si copiamos la matriz completa como está en la dirección señalada bmpData.Scan0, obtendremos la siguiente imagen. Cambio de imagen porque parte de la imagen se escribió en bytes de relleno, eso se ignoró. También es por eso que la última fila está incompleta:

paso ignorado

Pero si copiamos el puntero de destino de cambio fila por fila por bmpData.Stridevalor, se generarán imágenes válidas:

paso tomado en cuenta

Stride también puede ser negativo:

Si la zancada es positiva, el mapa de bits es de arriba hacia abajo. Si la zancada es negativa, el mapa de bits es de abajo hacia arriba.

Pero no trabajé con tales imágenes y esto está más allá de mi nota.

Respuesta relacionada: C #: búfer RGB de mapa de bits diferente de C ++


6

Todas las respuestas presentadas asumen que la matriz de bytes contiene datos en una representación de formato de archivo conocido, como: gif, png o jpg. Pero recientemente tuve un problema al intentar convertir byte[]correos electrónicos, que contienen información BGRA linealizada, de manera eficiente en Imageobjetos. El siguiente código lo resuelve usando un Bitmapobjeto.

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

Esta es una pequeña variación de una solución que se publicó en este sitio .


como referencia MSDN: Bitmap (Int32, Int32): "Este constructor crea un mapa de bits con un valor de enumeración PixelFormat de Format32bppArgb.", lo que significa que el byte [0] es azul, el byte [1] es verde, el byte [2] es rojo , el byte [3] es alfa, el byte [4] es azul, y así sucesivamente.
2018

1
GRACIAS por agregar todos los "usos" necesarios. La mayoría de la gente lo olvida y es un dolor encontrarlos a todos.
Casey Crookston

6

hay un enfoque simple como el siguiente, puede usar el FromStreammétodo de una imagen para hacer el truco, solo recuerde usarlo 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

probar (ACTUALIZAR)

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

2
Tiene poco sentido escribir la matriz de bytes en el flujo de memoria después de que se haya inicializado con la misma matriz de bytes. En realidad, no estoy seguro de que MemoryStream permita escribir más allá de la longitud especificada en el constructor.
Holstebroe

2

No ha declarado returnImage como ningún tipo de variable :)

Esto debería ayudar:

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
Código útil como idea, pero por supuesto no funcionará como está escrito. returnImage debe declararse fuera de la sección try / catch. También se debe crear una instancia de returnImage en 'catch' - creo un mapa de bits de un solo píxel: var image = new Bitmap (1, 1); Flujo de MemoryStream = nuevo MemoryStream (); image.Save (secuencia, ImageFormat.Jpeg); stream.Position = 0;
Reid

2

Esto está inspirado en la respuesta de Holstebroe, más los comentarios aquí: Obtener un objeto Imagen de una matriz de bytes

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

1

Un trazador de líneas:

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

0

La mayoría de las veces, cuando esto sucede, son datos incorrectos en la columna SQL. Esta es la forma correcta de insertar en una columna de imagen:

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

La mayoría de la gente lo hace incorrectamente de esta manera:

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

0

Primero instale este paquete:

Paquete de instalación SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Luego use el siguiente código para Cast Byte Array To Image:

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

Para obtener ImageFormat, utilice el siguiente código:

IImageFormat format = Image.DetectFormat(byteArray);

Para el uso de imagen mutada debajo del código:

image.Mutate(x => x.Resize(new Size(1280, 960)));
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.