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?

Tanzeel ur Rahman
fonte
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);
}
Holstebroe
fonte
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

RenniePet
fonte
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;
    }
}
Ali Gh
fonte
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 ++

Lorond
fonte
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 .

isaias-b
fonte
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;
}
Todos
fonte
5

tentar (ATUALIZAR)

MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
ms.Position = 0; // this is important
returnImage = Image.FromStream(ms,true);
Yahia
fonte
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;
}
LeeCambl
fonte
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
2

Isso é inspirado pela resposta de Holstebroe, além de comentários aqui: Obtendo um objeto Image a partir de uma matriz de bytes

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

Um liner:

Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));
Arvin Amir
fonte
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'))
GPGVM
fonte
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)));
AminGolmahalle
fonte