Conversion de tableau d'octets en image

101

Je veux convertir un tableau d'octets en image.

C'est mon code de base de données à partir duquel j'obtiens le tableau d'octets:

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

Mon code de conversion:

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

Lorsque j'atteins la ligne avec un commentaire, l'exception suivante se produit: Parameter is not valid.

Comment puis-je corriger ce qui cause cette exception?

Tanzeel ur Rahman
la source
Avez-vous vérifié que les octets d'image de votre requête sont valides? Vous pouvez faire un File.WriteAllBytes ("myimage.jpg", byteArrayIn) pour vérifier.
Holstebroe

Réponses:

110

Vous écrivez deux fois dans votre flux mémoire, et vous ne supprimez pas non plus le flux après utilisation. Vous demandez également au décodeur d'image d'appliquer une correction de couleur intégrée.

Essayez plutôt ceci:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}
Holstebroe
la source
Vous pouvez également explicitement rendre le flux mémoire non inscriptible après l'initialisation: new MemoryStream (byteArrayIn, false)
Holstebroe
28
Cela viole une spécification dans MSDN pour Image.FromStream (), où il est dit "Vous devez garder le flux ouvert pour la durée de vie de l'image." Voir aussi stackoverflow.com/questions/3290060/…
RenniePet
81

Peut-être que je manque quelque chose, mais pour moi, ce one-liner fonctionne bien avec un tableau d'octets qui contient une image d'un fichier JPEG.

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

ÉDITER:

Voir ici pour une version mise à jour de cette réponse: Comment convertir une image dans un tableau d'octets

RenniePet
la source
AAAh merci, enfin une bonne réponse. Pourquoi tant de réponses avec le flux de mémoire, cela me cause tellement de problèmes. Merci beaucoup !
Julian50
Bonne réponse! cela peut être utilisé dans une fonction distincte alors que toutes les autres propositions utilisant MemoryStream ne le peuvent pas (le flux doit rester ouvert pendant toute la durée de vie de l'image)
A_L
20
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}
Ali Gh
la source
Bien que ce ne soit normalement pas une bonne idée, j'ai mis un GC.Collect () avant le flux de mémoire. J'étais à court de mémoire lorsque j'ai préchargé un grand nombre de gros fichiers graphiques sous forme de bytearrays dans la mémoire, puis les ai transformés en images pendant la visualisation.
Kayot
9

Je voudrais noter qu'il y a un bogue dans la solution fournie par @ isaias-b.

Cette solution suppose qu'elle strideest égale à la longueur de la ligne. Mais ce n'est pas toujours vrai. En raison des alignements de mémoire effectués par GDI, la foulée peut être supérieure à la longueur de ligne. Cela doit être pris en compte. Sinon, une image décalée invalide sera générée. Les octets de remplissage dans chaque ligne seront ignorés.

La foulée correspond à la largeur d'une seule rangée de pixels (une ligne de balayage), arrondie à une limite de quatre octets.

Code fixe:

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

Pour illustrer ce à quoi cela peut conduire, générons une PixelFormat.Format24bppRgbimage dégradée 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 nous copions tout le tableau tel quel à l'adresse pointée par bmpData.Scan0, nous obtiendrons l'image suivante. Décalage d'image parce qu'une partie de l'image a été écrite dans les octets de remplissage, qui a été ignoré C'est aussi pourquoi la dernière ligne est incomplète:

pas ignoré

Mais si nous copions le pointeur de destination de décalage ligne par ligne par bmpData.Stridevaleur, une image valide sera générée:

pas pris en compte

La foulée peut également être négative:

Si la foulée est positive, le bitmap est de haut en bas. Si la foulée est négative, le bitmap est ascendant.

Mais je n'ai pas travaillé avec de telles images et c'est au-delà de ma note.

Réponse associée: C # - Tampon RVB de Bitmap différent de C ++

Lorond
la source
6

Toutes les réponses présentées supposent que le tableau d'octets contient des données dans une représentation de format de fichier connue, comme: gif, png ou jpg. Mais j'ai récemment eu un problème en essayant de convertir byte[]efficacement des s, contenant des informations BGRA linéarisées, en Imageobjets. Le code suivant le résout à l'aide d'un Bitmapobjet.

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

Ceci est une légère variation d'une solution qui a été publiée sur ce site .

isaias-b
la source
comme référence MSDN: Bitmap (Int32, Int32): "Ce constructeur crée un Bitmap avec une valeur d'énumération PixelFormat de Format32bppArgb.", ce qui signifie que l'octet [0] est bleu, l'octet [1] est vert, l'octet [2] est rouge , l'octet [3] est alpha, l'octet [4] est bleu, et ainsi de suite.
brk
1
MERCI d'avoir ajouté tous les "using" nécessaires. La plupart des gens oublient cela et ce sera pénible de tous les trouver.
Casey Crookston
6

il y a une approche simple comme ci-dessous, vous pouvez utiliser la FromStreamméthode d'une image pour faire l'affaire, n'oubliez pas d'utiliser 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
la source
5

essayez (UPDATE)

MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
ms.Position = 0; // this is important
returnImage = Image.FromStream(ms,true);
Yahia
la source
2
Il n'a pas de sens d'écrire le tableau d'octets dans le flux mémoire après qu'il a été initialisé avec le même tableau d'octets. En fait, je ne suis pas sûr que MemoryStream autorise l'écriture au-delà de la longueur spécifiée dans le constructeur.
Holstebroe
2

Vous n'avez pas déclaré returnImage comme une sorte de variable :)

Cela devrait aider:

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
la source
1
Code utile en tant qu'idée, mais bien sûr ne fonctionnera pas comme écrit. returnImage doit être déclaré en dehors de la section try / catch. ReturnImage doit également être instancié dans 'catch' - je crée un bitmap à un seul pixel: var image = new Bitmap (1, 1); MemoryStream stream = nouveau MemoryStream (); image.Save (flux, ImageFormat.Jpeg); stream.Position = 0;
Reid
2

Ceci est inspiré de la réponse de Holstebroe, ainsi que des commentaires ici: Obtenir un objet Image à partir d'un tableau d'octets

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

Bon mot:

Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));
Arvin Amir
la source
0

La plupart du temps, lorsque cela se produit, ce sont des données incorrectes dans la colonne SQL. C'est la bonne façon d'insérer dans une colonne d'image:

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

La plupart des gens ne le font pas correctement de cette façon:

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

Installez d'abord ce package:

Package d'installation SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Ensuite, utilisez le code ci-dessous pour Cast Byte Array To Image:

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

Pour obtenir ImageFormat, utilisez le code ci-dessous:

IImageFormat format = Image.DetectFormat(byteArray);

Pour Mutate Image, utilisez le code ci-dessous:

image.Mutate(x => x.Resize(new Size(1280, 960)));
AminGolmahalle
la source