A maneira mais rápida de converter imagem em matriz de bytes

106

Estou fazendo um aplicativo de compartilhamento de área de trabalho remota em que capturo uma imagem da área de trabalho e a comprimo e envio para o receptor. Para compactar a imagem, preciso convertê-la em um byte [].

Atualmente estou usando este:

public byte[] imageToByteArray(System.Drawing.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;
}

Mas eu não gosto porque tenho que salvá-lo em um ImageFormat e isso também pode usar recursos (Slow Down), bem como produzir resultados de compactação diferentes. Eu li sobre como usar Marshal.Copy e memcpy, mas não consigo compreendê-los.

Então, existe algum outro método para atingir esse objetivo?

user2529551
fonte
Tanto MemoryStream quanto Image têm um método de descarte, certifique-se de descartá-los, pois isso pode causar MemoryLeaks.
abc123
3
@ abc123: Você não precisa descartar um MemoryStream; é um recurso totalmente gerenciado, a menos que você o esteja usando em comunicação remota. Em ambos os casos, seria inapropriado dispor do recurso.
Jon Skeet
1
@JonSkeet interessante, você fez um benchmark sobre isso? para ver a velocidade com que .net libera o objeto? Eu sei que há um argumento semelhante para DataTable e, ainda assim, há uma diferença notável na velocidade com que o GarbageCollector coleta a memória alocada quando um descarte é usado.
abc123
@ abc123: Eu realmente não esperava que houvesse - descartar o stream não faz nada com o array, e MemoryStream não tem um finalizador (ao contrário de DataTable, que herda um de MarshalByValueComponent).
Jon Skeet
2
alguma solução final com código-fonte completo?
Kiquenet

Respostas:

39

Então, existe algum outro método para atingir esse objetivo?

Não. Para converter uma imagem para um array de bytes que você tem para especificar um formato de imagem - assim como você tem que especificar uma codificação quando você converter texto em um array de bytes.

Se você está preocupado com artefatos de compressão, escolha um formato sem perdas. Se você está preocupado com os recursos da CPU, escolha um formato que não se incomode em compactar - apenas pixels ARGB brutos, por exemplo. Mas é claro que isso levará a uma matriz de bytes maior.

Note que se você escolher um formato que faz incluem compressão, não há nenhum ponto em seguida, comprimindo a matriz de bytes depois - é quase certo que não têm nenhum efeito benéfico.

Jon Skeet
fonte
12
em vez de 'escolher um formato sem perdas', você pode escolher imageIn.RawFormatqual tenta salvar os bytes da imagem bruta sem mais recodificação.
Chris F Carroll
52

Existe uma propriedade RawFormat do parâmetro Image que retorna o formato de arquivo da imagem. Você pode tentar o seguinte:

// extension method
public static byte[] imageToByteArray(this System.Drawing.Image image)
{
    using(var ms = new MemoryStream())
    {
        image.Save(ms, image.RawFormat);
        return ms.ToArray();
    }
}
Newt
fonte
9
Eu recomendo descartar o MemoryStream ou envolver o corpo deste método em uma instrução using () {}
Neil.Allen
@ Neil.Allen Sou novo aqui, pode me dizer por quê?
Khalil Khalaf
3
@FirstStep Porque limpe depois de você :)
Sinestésico
@Sinaesthetic I see. E a rotina é colocar qualquer função que desejo executar, em um using () {}?
Khalil Khalaf,
2
@FirstStep Não exatamente. Mais precisamente: se você usar um objeto que implementou IDisposable, certifique-se de chamar Dispose () quando terminar de usá-lo, para que ele limpe todos os recursos que tenha ocupado. A instrução using () {} apenas chama para você quando o objeto sai do escopo dessa instrução. Portanto, você pode fazer myObject.Dispose()ou using(myObject){}- ambos fazem a mesma coisa, mas a instrução using basicamente cria um escopo que limpará para você.
Sinestésico
14

Não tenho certeza se você terá grandes ganhos pelos motivos apontados por Jon Skeet. No entanto, você pode tentar comparar o método TypeConvert.ConvertTo e ver como ele se compara ao uso do método atual.

ImageConverter converter = new ImageConverter();
byte[] imgArray = (byte[])converter.ConvertTo(imageIn, typeof(byte[]));
teclado P
fonte
Não é possível lançar o objeto do tipo 'System.Byte []' para o tipo 'System.Drawing.Image'.
user123
14
public static byte[] ReadImageFile(string imageLocation)
    {
        byte[] imageData = null;
        FileInfo fileInfo = new FileInfo(imageLocation);
        long imageFileLength = fileInfo.Length;
        FileStream fs = new FileStream(imageLocation, FileMode.Open, FileAccess.Read);
        BinaryReader br = new BinaryReader(fs);
        imageData = br.ReadBytes((int)imageFileLength);
        return imageData;
    }
bhadresh
fonte
5
Bem-vindo ao stackoverflow.com, você poderia adicionar alguns detalhes explicando por que o exemplo de código acima ajuda. É para outros usuários de SO que podem não entendê-lo inteiramente ... stackoverflow.com/help/how-to-answer
Mack
Isso é para arquivos em bytes, mas o OP queria um objeto de desenho convertido em bytes. Os objetos de desenho podem ser armazenados em bancos de dados, não necessariamente no sistema de arquivos, como uma matriz de bytes e, portanto, devem ser transformados para frente e para trás ... mas não como arquivos em um FileStream para serem convertidos em bytes - a menos que talvez, durante o upload inicial.
vapcguy
Isso me ajudou, pois eu procurava fazer isso com arquivos. É bom ter por perto.
Justin
5
public static class HelperExtensions
{
    //Convert Image to byte[] array:
    public static byte[] ToByteArray(this Image imageIn)
    {
        var ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
        return ms.ToArray();
    }

    //Convert byte[] array to Image:
    public static Image ToImage(this byte[] byteArrayIn)
    {
        var ms = new MemoryStream(byteArrayIn);
        var returnImage = Image.FromStream(ms);
        return returnImage;
    }
}
Ahmad Aghazadeh
fonte
2

A maneira mais rápida de descobrir é esta:

var myArray = (byte[]) new ImageConverter().ConvertTo(InputImg, typeof(byte[]));

Espero ser útil

Alireza Amini
fonte
Tenha cuidado com isso, especialmente se estiver usando WPF onde você teria System.Windows.Controls.Imageobjetos. Se você quiser converter um desses para bytes e passá-lo para esta linha como InputImg, isso não funcionará. Ele espera um System.Drawing.Imageobjeto.
vapcguy