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?


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

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


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

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 ++


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 .


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

5

essayez (UPDATE)

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

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

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


1

Bon mot:

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

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'))

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)));
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.