Converter um bitmap em uma matriz de bytes

233

Usando C #, existe uma maneira melhor de converter um Windows Bitmappara um do byte[]que salvar em um arquivo temporário e ler o resultado usando um FileStream?

Jeremy McGee
fonte

Respostas:

419

Existem algumas maneiras.

ImageConverter

public static byte[] ImageToByte(Image img)
{
    ImageConverter converter = new ImageConverter();
    return (byte[])converter.ConvertTo(img, typeof(byte[]));
}

Este é conveniente porque não requer muito código.

Fluxo de memória

public static byte[] ImageToByte2(Image img)
{
    using (var stream = new MemoryStream())
    {
        img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
        return stream.ToArray();
    }
}

Este é equivalente ao que você está fazendo, exceto que o arquivo é salvo na memória em vez de no disco. Embora haja mais código, você tem a opção ImageFormat e pode ser facilmente modificado entre salvar na memória ou no disco.

Fonte: http://www.vcskicks.com/image-to-byte.php

prestomanifesto
fonte
2
Você também pode usar Bitmap.LockBits e Marshal.Copy para um simples rápido copiar sem MemoryStreams intermediários etc.
deegee
4
Esteja ciente de que o ImageConvertermétodo salvará a imagem como Png, resultando em arquivos ENORMES.
Skolima 5/03
8
você não precisa fechar o fluxo ao usar 'using'
LastTribunal
2
Alguém pode me dar um pouco mais de informações sobre como essa matriz é estruturada? Começa com x = 0 e percorre todos os ye incrementando x? E então armazene-o assim: [0,0,1,1,1,1,0,0,1,1]?
Raymond Timmermans
1
ImageConverternão é padrão .net você pode usarMemoryStream
Alexandre
95

Um MemoryStream pode ser útil para isso. Você pode colocá-lo em um método de extensão:

public static class ImageExtensions
{
    public static byte[] ToByteArray(this Image image, ImageFormat format)
    {
        using(MemoryStream ms = new MemoryStream())
        {
            image.Save(ms, format);
            return ms.ToArray();
        }
    }
}

Você poderia usá-lo como:

var image = new Bitmap(10, 10);
// Draw your image
byte[] arr = image.ToByteArray(ImageFormat.Bmp);

Discordo parcialmente da resposta do prestomanifto em relação ao ImageConverter. Não use o ImageConverter. Não há nada tecnicamente errado com isso, mas simplesmente o fato de usar o boxe / unboxing do objeto me diz que é um código dos antigos locais escuros da estrutura .NET e não é ideal para o processamento de imagens (é um exagero a conversão para um byte [] pelo menos), especialmente quando você considera o seguinte.

Dei uma olhada no ImageConvertercódigo usado pela estrutura .Net e, internamente, ele usa código quase idêntico ao que forneci acima. Ele cria um novo MemoryStream, salva o Bitmapformato que estava quando você o forneceu e retorna a matriz. Ignore a sobrecarga extra de criar uma ImageConverterclasse usandoMemoryStream

Christopher Currens
fonte
Encantador. Isso serve! Suponho que você deseje descartar o MemoryStream - gostaria de atualizar?
Jeremy McGee
Atualizei minha resposta com algumas discussões sobre por que não usar o ImageConverter, como sugere a resposta selecionada, além da adição de descarte.
Christopher Currens
Bom uso de um método de extensão, eu gosto!
Cara Pascalou
3
+1 por analisar o ImageConverter e reportar os resultados de sua pesquisa. Mas não acho que o que você descobriu justifique a declaração "Não use o ImageConverter". Definitivamente, fornece serviços úteis em sentido inverso, da matriz de bytes à imagem, por exemplo, definindo a resolução da imagem (dpi). E a "sobrecarga extra de criar uma classe ImageConverter" é presumivelmente insignificante e só precisa ser feita uma vez, independentemente de quantas vezes você a utilize.
RenniePet
1
Achei o ImageConverter útil também, pois ele pode determinar o tipo da imagem automaticamente - por exemplo, se você tem uma matriz de bytes da imagem, mas não conhece o formato -, você pode tentar ler os cabeçalhos e obter dicas a partir daí, mas, bem, ... usando (img imagem = (imagem) myImgConverter.ConvertFrom (byteImage)) e, em seguida, verificando img.PixelFormat etc. é apenas mais fácil .. - é claro, dependendo do que você quer fazer
hello_earth
46

Você também pode apenas Marshal. Copiar os dados de bitmap. Não há fluxo de memória intermediário, etc., e uma cópia rápida da memória. Isso deve funcionar em bitmaps de 24 e 32 bits.

public static byte[] BitmapToByteArray(Bitmap bitmap)
{

    BitmapData bmpdata = null;

    try
    {
        bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        int numbytes = bmpdata.Stride * bitmap.Height;
        byte[] bytedata = new byte[numbytes];
        IntPtr ptr = bmpdata.Scan0;

        Marshal.Copy(ptr, bytedata, 0, numbytes);

        return bytedata;
    }
    finally
    {
        if (bmpdata != null)
            bitmap.UnlockBits(bmpdata);
    }

}

.

deegee
fonte
1
Olá, usei esse método, mas não posso converter essa matriz de bytes novamente em uma imagem. Bitmap bitmap1 = novo Bitmap (novo MemoryStream (array)); lança uma exceção de parâmetros inválidos. Existe algum erro?
Howard
1
Olá, a seção de comentários é muito pequena para eu postar código limpo completo. A maneira mais simples é fixar a matriz: GCHandle handle = GCHandle.Alloc (bufferarray, GCHandleType.Pinned); obter o ponteiro para a matriz IntPtr iptr = Marshal.UnsafeAddrOfPinnedArrayElement (bufferarray, 0); em seguida, crie um novo bitmap usando o IntPtr: bitmap = new Bitmap (largura, altura, (largura * 4), PixelFormat.Format32bppArgb, iptr); mas você precisará usar os parâmetros de criação de bitmap apropriados para o seu formato de bitmap. Certifique-se de limpar: iptr = IntPtr.Zero; handle.Free ();
deegee
3
Qual é o erro? Muitos programadores, inclusive eu, estão usando esse estilo de código e funciona bem.
Deegee 06/12
4
@ mini-me - eu recomendo procurar o passo do bitmap versus a largura do bitmap. Isso não é um bug no meu código, é assim que o Windows lida com bitmaps. O passo pode incluir dados extras no final de cada linha (largura) para que cada linha termine e comece em um limite de 32 bits para alinhamento e desempenho da memória.
deegee
3
Não é um inseto. msdn.microsoft.com/en-us/library/…
deegee
16

Salve a imagem em um MemoryStream e, em seguida, pegue a matriz de bytes.

http://msdn.microsoft.com/en-us/library/ms142148.aspx

  Byte[] data;

  using (var memoryStream = new MemoryStream())
  {
    image.Save(memoryStream, ImageFormat.Bmp);

    data = memoryStream.ToArray();
  }
Chris Baxter
fonte
Não há método Salvar na imagem.
Prescott Chartier
@PrescottChartier O exemplo acima é supondo que você está trabalhando a partir de um tipo derivado de System.Drawing.Image (ver: docs.microsoft.com/en-us/dotnet/api/... )
Chris Baxter
Sim e eu entendo System.Drawing.Image does not exist. Então .. não, não funciona :(
Prescott Chartier
Você pode ver o que estou tentando fazer aqui: stackoverflow.com/questions/55084620/…
Prescott Chartier
10

Use a em MemoryStreamvez de a FileStream, assim:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
Jeff Reddy
fonte
Você provavelmente quer ToArray, não GetBuffer.
vcsjones
GetBuffer retorna uma matriz de bytes sem sinal (matriz de bytes)
Jeff Reddy
4
Pode ter dados de preenchimento que não fazem parte da imagem. Da documentação: Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method.Agora, a matriz de bytes GetBufferretornará a imagem mais bytes não utilizados, o que provavelmente resultará em uma imagem corrompida.
Vcsjones
1
Bom saber. Obrigado pelo comentário vcs!
Jeff Reddy
Essa abordagem salva a imagem como um JPEG com configurações de compactação padrão, que introduzirão artefatos de compactação que podem degradar visivelmente sua imagem.
Richard Ev
8

Tente o seguinte:

MemoryStream stream = new MemoryStream();
Bitmap bitmap = new Bitmap();
bitmap.Save(stream, ImageFormat.Jpeg);

byte[] byteArray = stream.GetBuffer();

Verifique se você está usando:

System.Drawing & using System.Drawing.Imaging;
Francis Gilbert
fonte
1
Essa abordagem salva a imagem como um JPEG com configurações de compactação padrão, que introduzirão artefatos de compactação que podem degradar visivelmente sua imagem.
Richard Ev
Mais uma vez, nenhum método Save no bitmap.
Prescott Chartier
7

Eu acredito que você pode simplesmente fazer:

ImageConverter converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(img, typeof(byte[]));
Kevek
fonte
6
MemoryStream ms = new MemoryStream();
yourBitmap.Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();
62071072SP
fonte
5

Mais simples:

return (byte[])System.ComponentModel.TypeDescriptor.GetConverter(pImagen).ConvertTo(pImagen, typeof(byte[]))
Moises Conejo
fonte
-4

Muito simples, use isso apenas em uma linha:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");
KARAN
fonte
op disse que não está interessado nessa opção #
Mauro Sampietro 11/11