Conversão de matriz de bytes para imagem


101

Quero converter uma matriz de bytes em uma imagem.

Este é o código do meu banco de dados, de onde obtenho a 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;
}

Meu código de conversão:

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 chego à linha com um comentário, ocorre a seguinte exceção: Parameter is not valid.

Como posso corrigir o que está causando essa exceção?


Você verificou se os bytes da imagem em sua consulta são válidos? Você poderia fazer um File.WriteAllBytes ("myimage.jpg", byteArrayIn) para verificar.
Holstebroe

Respostas:


110

Você está gravando no fluxo de memória duas vezes e também não está descartando o fluxo após o uso. Você também está pedindo ao decodificador de imagem para aplicar a correção de cores incorporada.

Em vez disso, tente isto:

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

Você também pode tornar o fluxo de memória não gravável explicitamente após a inicialização: new MemoryStream (byteArrayIn, false)
Holstebroe

28
Isso viola uma especificação no MSDN para Image.FromStream (), onde diz "Você deve manter o fluxo aberto durante o tempo de vida da imagem." Consulte também stackoverflow.com/questions/3290060/…
RenniePet

81

Talvez esteja faltando alguma coisa, mas para mim este one-liner funciona bem com uma matriz de bytes que contém uma imagem de um arquivo JPEG.

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

EDITAR:

Veja aqui uma versão atualizada desta resposta: Como converter imagem em matriz de bytes


AAAh obrigado, Finalmente uma boa resposta. Por que tantas respostas com fluxo de memória, isso me causa tanto problema. Muito obrigado !
Julian50

Boa resposta! isso pode ser usado em uma função separada, enquanto todas as outras propostas que usam MemoryStream não podem (o fluxo deve ser mantido aberto durante toda a vida da imagem)
A_L

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

Embora normalmente não seja uma boa ideia, coloco um GC.Collect () antes do fluxo de memória. Eu estava ficando sem memória quando pré-carreguei uma grande quantidade de arquivos gráficos grandes como bytearrays na memória e os transformei em imagens durante a visualização.
Kayot

9

Gostaria de observar que há um bug na solução fornecida por @ isaias-b.

Essa solução assume que strideé igual ao comprimento da linha. Mas nem sempre é verdade. Devido aos alinhamentos de memória realizados pelo GDI, a passada pode ser maior que o comprimento da linha. Isso deve ser levado em consideração. Caso contrário, uma imagem deslocada inválida será gerada. Os bytes de preenchimento em cada linha serão ignorados.

A distância é a largura de uma única linha de pixels (uma linha de varredura), arredondada para um limite de quatro bytes.

Código fixo:

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 que isso pode levar, vamos gerar uma PixelFormat.Format24bppRgbimagem de gradiente 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 copiarmos todo o array como está para o endereço apontado por bmpData.Scan0, obteremos a imagem a seguir. Mudança de imagem porque parte da imagem foi gravada em bytes de preenchimento, que foi ignorado. Também é por isso que a última linha está incompleta:

passo ignorado

Mas se copiarmos o ponteiro de destino de deslocamento linha por linha por bmpData.Stridevalor, imagens válidas serão geradas:

passo levado em consideração

Stride também pode ser negativo:

Se a passada for positiva, o bitmap é de cima para baixo. Se a passada for negativa, o bitmap é ascendente.

Mas não trabalhei com essas imagens e isso está além da minha nota.

Resposta relacionada: C # - Buffer RGB de Bitmap diferente de C ++


6

Todas as respostas apresentadas presumem que a matriz de bytes contém dados em uma representação de formato de arquivo conhecida, como: gif, png ou jpg. Mas recentemente tive um problema ao tentar converter byte[]s, contendo informações BGRA linearizadas, de forma eficiente em Imageobjetos. O código a seguir resolve isso usando um 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 é uma pequena variação de uma solução postada neste site .


como referência MSDN: Bitmap (Int32, Int32): "Este construtor cria um Bitmap com um valor de enumeração PixelFormat de Format32bppArgb.", o que significa que byte [0] é azul, byte [1] é verde, byte [2] é vermelho , byte [3] é alfa, byte [4] é azul e assim por diante.
brk

1
OBRIGADO por adicionar todos os "usando" s necessários. A maioria das pessoas esquece isso e é uma pena encontrar todos eles.
Casey Crookston

6

há uma abordagem simples como abaixo, você pode usar o FromStreammétodo de uma imagem para fazer o truque, apenas lembre-se de usar 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

tentar (ATUALIZAR)

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

2
Não faz muito sentido gravar a matriz de bytes no fluxo de memória depois que ela foi inicializada com a mesma matriz de bytes. Na verdade, não tenho certeza se MemoryStream permite a gravação além do comprimento especificado no construtor.
Holstebroe

2

Você não declarou returnImage como qualquer tipo de variável :)

Isso deve ajudar:

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 uma ideia, mas é claro que não funcionará como está escrito. returnImage deve ser declarado fora da seção try / catch. Além disso, returnImage deve ser instanciado em 'catch' - eu crio um bitmap de pixel único: var image = new Bitmap (1, 1); Fluxo de MemoryStream = novo MemoryStream (); image.Save (stream, ImageFormat.Jpeg); stream.Position = 0;
Reid de


1

Um liner:

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

0

Na maioria das vezes, quando isso acontece, são dados inválidos na coluna SQL. Esta é a maneira correta de inserir em uma coluna de imagem:

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

A maioria das pessoas faz isso incorretamente desta forma:

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

0

Instale primeiro este pacote:

Install-Package SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Em seguida, use o código abaixo para transmitir matriz de bytes para a imagem:

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

Para obter ImageFormat, use o código abaixo:

IImageFormat format = Image.DetectFormat(byteArray);

Para Mutate Image Use Abaixo do Código:

image.Mutate(x => x.Resize(new Size(1280, 960)));
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.