Convertir un bitmap en un tableau d'octets

233

En utilisant C #, y a-t-il une meilleure façon de convertir un Windows Bitmapen un byte[]qu'en enregistrant dans un fichier temporaire et en lisant le résultat en utilisant un FileStream?

Jeremy McGee
la source

Réponses:

419

Il y a deux façons.

ImageConverter

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

Celui-ci est pratique car il ne nécessite pas beaucoup de code.

Flux de mémoire

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

Celui-ci est équivalent à ce que vous faites, sauf que le fichier est enregistré dans la mémoire plutôt que sur le disque. Bien que plus de code, vous avez l'option ImageFormat et il peut être facilement modifié entre l'enregistrement sur la mémoire ou le disque.

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

prestomanifesto
la source
2
Vous pouvez également utiliser bitmap.Lockbits et Marshal.Copy pour une simple copie rapide sans aucun flux de mémoire intermédiaire, etc.
deegee
4
Veuillez noter que la ImageConverterméthode enregistrera l'image au format Png, ce qui entraînera d'énormes fichiers.
skolima
8
vous n'avez pas besoin de fermer le flux lorsque vous utilisez 'using'
LastT Tribunal
2
Quelqu'un peut-il me donner un peu plus d'informations sur la structure de ce tableau? Commence-t-il par x = 0 et parcourt-il tous les y puis incrémente-t-il x? Et puis stockez-le comme ceci: [0,0,1,1,1,1,0,0,1,1]?
Raymond Timmermans
1
ImageConvertern'est pas la norme .net que vous pourriez utiliserMemoryStream
Alexandre
95

Un MemoryStream peut être utile pour cela. Vous pouvez le mettre dans une méthode d'extension:

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

Vous pouvez simplement l'utiliser comme:

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

Je suis partiellement en désaccord avec la réponse de prestomanifto concernant l'ImageConverter. N'utilisez pas ImageConverter. Il n'y a rien de mal sur le plan technique, mais simplement le fait qu'il utilise la boxe / unboxing de l'objet me dit que c'est du code provenant des anciens endroits sombres du framework .NET et que ce n'est pas idéal à utiliser avec le traitement d'image (c'est exagéré pour la conversion en octet [] au moins), surtout si l'on considère les éléments suivants.

J'ai jeté un coup d'œil au ImageConvertercode utilisé par le framework .Net, et en interne, il utilise un code presque identique à celui que j'ai fourni ci-dessus. Il en crée un nouveau MemoryStream, l'enregistre Bitmapdans le format dans lequel il se trouvait lorsque vous l'avez fourni et renvoie le tableau. Évitez les frais généraux supplémentaires liés à la création d'une ImageConverterclasse en utilisantMemoryStream

Christopher Currens
la source
Charmant. Ça fera l'affaire! Je suppose que vous voudrez disposer du MemoryStream, cependant - vous voulez mettre à jour?
Jeremy McGee
J'ai mis à jour ma réponse avec une discussion sur pourquoi ne pas utiliser ImageConverter, comme le suggère votre réponse sélectionnée, ainsi que l'ajout de l'élimination.
Christopher Currens
Belle utilisation d'une méthode d'extension, j'aime ça!
Mec Pascalou
3
+1 pour regarder ImageConverter et rapporter les résultats de votre recherche. Mais je ne pense pas que ce que vous avez découvert justifie la déclaration "Ne pas utiliser ImageConverter." Il fournit certainement des services utiles dans l'autre sens, du tableau d'octets à l'image, par exemple en définissant la résolution de l'image (dpi). Et le "surcoût supplémentaire lié à la création d'une classe ImageConverter" est probablement négligeable et ne doit être effectué qu'une seule fois, quel que soit le nombre de fois où vous l'utilisez.
RenniePet
1
J'ai également trouvé ImageConverter utile car il peut déterminer automatiquement le type de l'image - par exemple si vous avez un tableau d'octets de l'image, mais ne connaissez pas le format - vous pouvez essayer de lire les en-têtes et d'obtenir des conseils à partir de là, mais, eh bien, ... utiliser (Image img = (Image) myImgConverter.ConvertFrom (byteImage)) puis vérifier img.PixelFormat etc. est tout simplement plus facile .. - bien sûr selon ce que vous voulez faire
hello_earth
46

Vous pouvez également simplement Marshal.Copy les données bitmap. Pas de mémoire intermédiaire, etc. et une copie de mémoire rapide. Cela devrait fonctionner sur les bitmaps 24 bits et 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
la source
1
Salut, j'ai utilisé cette méthode, mais je ne peux pas reconvertir ce tableau d'octets en une image. Bitmap bitmap1 = nouveau Bitmap (nouveau MemoryStream (tableau)); il lève une exception de paramètres invalides. Y a-t-il une erreur?
Howard
1
Salut, la section des commentaires est trop petite pour que je puisse poster du code complet. Le moyen le plus simple consiste à épingler le tableau: GCHandle handle = GCHandle.Alloc (bufferarray, GCHandleType.Pinned); obtenir le pointeur sur le tableau IntPtr iptr = Marshal.UnsafeAddrOfPinnedArrayElement (bufferarray, 0); puis créez un nouveau bitmap en utilisant IntPtr: bitmap = new Bitmap (largeur, hauteur, (largeur * 4), PixelFormat.Format32bppArgb, iptr); mais vous devrez utiliser les paramètres de création de bitmap appropriés pour votre format bitmap. Assurez-vous de nettoyer: iptr = IntPtr.Zero; handle.Free ();
deegee
3
Quel est le bug? De nombreux programmeurs, dont moi-même, utilisent ce style de code et cela fonctionne très bien.
Deegee
4
@ mini-me - Je recommande de rechercher ce qu'est la foulée bitmap par rapport à la largeur bitmap. Ce n'est pas un bug dans mon code, c'est ainsi que Windows gère les bitmaps. Stride peut inclure des données supplémentaires à la fin de chaque ligne (largeur) afin d'avoir chaque fin de ligne et de commencer sur une limite de 32 bits pour l'alignement de la mémoire et les performances.
Deegee
3
Ce n'est pas un bug. msdn.microsoft.com/en-us/library/…
deegee
16

Enregistrez l'image dans un MemoryStream, puis saisissez le tableau d'octets.

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
la source
Il n'y a pas de méthode d'enregistrement sur l'image.
Prescott Chartier
@PrescottChartier L'exemple ci-dessus suppose que vous travaillez à partir d'un type dérivé de System.Drawing.Image (voir: docs.microsoft.com/en-us/dotnet/api/… )
Chris Baxter
Ouais et je reçois System.Drawing.Image does not exist. Alors .. non, ça ne marche pas :(
Prescott Chartier
Vous pouvez voir ce que j'essaie de faire ici: stackoverflow.com/questions/55084620/…
Prescott Chartier
10

Utilisez un MemoryStreamau lieu d'un FileStream, comme ceci:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
Jeff Reddy
la source
Vous voulez probablement ToArray, non GetBuffer.
vcsjones
GetBuffer renvoie un tableau d'octets non signés (tableau d'octets)
Jeff Reddy
4
Il pourrait y avoir des données de remplissage qui ne font pas partie de l'image. De la documentation: 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.Alors maintenant, le tableau d'octets de GetBufferretournera l'image plus les octets inutilisés, ce qui entraînera probablement une image corrompue.
vcsjones
1
Bon à savoir. Merci pour le commentaire vcs!
Jeff Reddy, le
Cette approche enregistre l'image au format JPEG avec des paramètres de compression par défaut, ce qui introduira des artefacts de compression qui pourraient dégrader visiblement votre image.
Richard Ev
8

Essayez ce qui suit:

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

byte[] byteArray = stream.GetBuffer();

Assurez-vous que vous utilisez:

System.Drawing & using System.Drawing.Imaging;
Francis Gilbert
la source
1
Cette approche enregistre l'image au format JPEG avec des paramètres de compression par défaut, ce qui introduira des artefacts de compression qui pourraient dégrader visiblement votre image.
Richard Ev
Encore une fois, aucune méthode Save sur bitmap.
Prescott Chartier
7

Je pense que vous pouvez simplement faire:

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

Plus simple:

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

Très simple, utilisez ceci en une seule ligne:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");
KARAN
la source
op a dit qu'il n'était pas intéressé par cette option
Mauro Sampietro