La forma más rápida de convertir una imagen a una matriz de bytes

106

Estoy creando una aplicación para compartir escritorio remoto en la que capturo una imagen del escritorio, la comprimo y la envío al receptor. Para comprimir la imagen necesito convertirla en un byte [].

Actualmente estoy usando esto:

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

Pero no me gusta porque tengo que guardarlo en un ImageFormat y eso también puede consumir recursos (ralentizar) y producir diferentes resultados de compresión. He leído sobre el uso de Marshal.Copy y memcpy pero no puedo entiéndelos.

Entonces, ¿hay algún otro método para lograr este objetivo?

usuario2529551
fuente
tanto MemoryStream como Image tienen un método de eliminación, asegúrese de eliminarlos, ya que esto puede causar MemoryLeaks.
abc123
3
@ abc123: No necesita deshacerse de un MemoryStream; es un recurso completamente administrado, a menos que lo esté usando de forma remota. En ambos casos, sería inapropiado deshacerse del recurso.
Jon Skeet
1
@JonSkeet interesante, ¿has hecho un punto de referencia sobre eso? para ver la velocidad a la que .net libera el objeto? Sé que existe un argumento similar para DataTable y, sin embargo, hay una diferencia notable en la velocidad con la que GarbageCollector recopila la memoria asignada cuando se usa un desecho.
abc123
@ abc123: Realmente no esperaría que lo hubiera: la eliminación de la secuencia no le hace nada a la matriz, y MemoryStream no tiene un finalizador (a diferencia de DataTable, que hereda uno de MarshalByValueComponent).
Jon Skeet
2
¿Alguna solución final con código fuente completo?
Kiquenet

Respuestas:

39

Entonces, ¿hay algún otro método para lograr este objetivo?

No. Para convertir una imagen a una matriz de bytes que tiene que especificar un formato de imagen - al igual que usted tiene que especificar una codificación al convertir texto a una matriz de bytes.

Si le preocupan los artefactos de compresión, elija un formato sin pérdidas. Si le preocupan los recursos de la CPU, elija un formato que no se moleste en comprimir, solo píxeles ARGB sin procesar, por ejemplo. Pero, por supuesto, eso conducirá a una matriz de bytes más grande.

Tenga en cuenta que si tienes que elegir un formato que hace incluir compresión, no tiene sentido en que se moldea la matriz de bytes después - que es casi seguro que no tendrá ningún efecto beneficioso.

Jon Skeet
fuente
12
en lugar de 'elegir un formato sin pérdidas', puede elegir imageIn.RawFormatqué intenta guardar los bytes de la imagen sin procesar sin volver a codificar.
Chris F Carroll
52

Hay una propiedad RawFormat del parámetro Image que devuelve el formato de archivo de la imagen. Puede intentar lo siguiente:

// extension method
public static byte[] imageToByteArray(this System.Drawing.Image image)
{
    using(var ms = new MemoryStream())
    {
        image.Save(ms, image.RawFormat);
        return ms.ToArray();
    }
}
Tritón
fuente
9
Recomendaría eliminar MemoryStream o envolver el cuerpo de este método en una declaración using () {}
Neil.Allen
@ Neil.Allen Soy nuevo aquí, ¿puedes decir por qué?
Khalil Khalaf
3
@FirstStep Porque limpia después de ti mismo :)
Sinaesthetic
@ Sinaesthetic ya veo. ¿Y la rutina es poner cualquier función que quiera ejecutar, en un using () {}?
Khalil Khalaf
2
@FirstStep No del todo. Más exactamente: si usa un objeto que ha implementado IDisposable, entonces debe asegurarse de llamar a Dispose () cuando haya terminado con él para que limpie todos los recursos que haya inmovilizado. La declaración using () {} simplemente la llama cuando el objeto sale del alcance de esa declaración. Entonces puede hacer myObject.Dispose()o using(myObject){}- ambos hacen lo mismo, pero la declaración de uso básicamente crea un alcance que se limpiará por usted.
Sinaesthetic
14

No estoy seguro de si obtendrá grandes ganancias por las razones que señaló Jon Skeet. Sin embargo, puede probar y comparar el método TypeConvert.ConvertTo y ver cómo se compara con el uso de su método actual.

ImageConverter converter = new ImageConverter();
byte[] imgArray = (byte[])converter.ConvertTo(imageIn, typeof(byte[]));
keyboardP
fuente
No se puede convertir el objeto de tipo 'System.Byte []' para escribir '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
fuente
5
Bienvenido a stackoverflow.com, ¿podría agregar un pequeño detalle que explique por qué ayuda el ejemplo de código anterior? Es para otros usuarios de SO que pueden no entenderlo por completo ... stackoverflow.com/help/how-to-answer
Mack
Esto es para archivos a bytes, pero el OP quería un objeto de dibujo convertido a bytes. Los objetos de dibujo se pueden almacenar en bases de datos, no necesariamente en el sistema de archivos, como una matriz de bytes y, por lo tanto, deben transformarse de un lado a otro ... pero no como archivos en un FileStream para convertirlos en bytes, a menos que tal vez, durante el carga inicial.
vapcguy
Esto me ayudó ya que estaba buscando hacer esto con archivos. Es bueno tenerlo todo relacionado.
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
fuente
2

La forma más rápida que pude averiguar es esta:

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

Espero ser útil

alireza amini
fuente
Tenga cuidado con esto, especialmente si usa WPF donde tendría System.Windows.Controls.Imageobjetos. Si desea convertir uno de esos en bytes y lo pasa a esta línea como InputImg, esto no funcionará. Espera un System.Drawing.Imageobjeto.
vapcguy