Comment écrire une image PNG en chaîne avec le PIL?

100

J'ai généré une image en utilisant PIL . Comment puis-je l'enregistrer dans une chaîne en mémoire? La Image.save()méthode nécessite un fichier.

J'aimerais avoir plusieurs de ces images stockées dans le dictionnaire.

maxp
la source

Réponses:

194

Vous pouvez utiliser la BytesIOclasse pour obtenir un wrapper autour des chaînes qui se comportent comme un fichier. L' BytesIOobjet fournit la même interface qu'un fichier, mais enregistre le contenu uniquement en mémoire:

import io

with io.BytesIO() as output:
    image.save(output, format="GIF")
    contents = output.getvalue()

Vous devez spécifier explicitement le format de sortie avec le formatparamètre, sinon PIL générera une erreur lors de la tentative de détection automatique.

Si vous avez chargé l'image à partir d'un fichier, il a un formatparamètre qui contient le format de fichier d'origine, donc dans ce cas, vous pouvez utiliser format=image.format.

Dans les anciennes versions de Python 2 avant l'introduction du iomodule, vous auriez utilisé le StringIOmodule à la place.

qc
la source
1
Oui! Ceci est exactement ce que je cherchais. J'ai recherché sur Google toutes les combinaisons de python, de chaîne, de lecteur, d'écrivain, de tampon et je n'ai rien trouvé. Merci!
rik.the.vik
2
Ne fonctionne pas pour moi :( J'ai eu cette KeyError dans Imaging / PIL / Image.pyc ligne 1423 -> augmenter KeyError (ext) # unknown extension
Radian
6
@Radian, La documentation PIL sur la méthode save () dit: You can use a file object instead of a filename. In this case, you must always specify the format.Donc, si le premier argument est un objet fichier, vous devez passer le deuxième argument, qui est le format (par exemple 'PNG').
Su Zhang
1
Dans la version actuelle de Python, StringIO est dans le package io, vous devez écrire:from io import StringIO
Spoutnik16
4
vous pouvez mieux utiliserimage.save(output, format=image.format)
reetesh11
29

Pour Python3, il est nécessaire d'utiliser BytesIO:

from io import BytesIO
from PIL import Image, ImageDraw

image = Image.new("RGB", (300, 50))
draw = ImageDraw.Draw(image)
draw.text((0, 0), "This text is drawn on image")

byte_io = BytesIO()

image.save(byte_io, 'PNG')

En savoir plus: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image

ozooner
la source
24

La solution de qc n'a pas fonctionné pour moi
parce que dans ...

Imaging / PIL / Image.pyc ligne 1423 -> augmenter KeyError (ext) # extension inconnue

Il essayait de détecter le format à partir de l'extension dans le nom de fichier, qui n'existe pas dans le cas de StringIO

Vous pouvez contourner la détection de format en définissant vous-même le format dans un paramètre

import StringIO
output = StringIO.StringIO()
format = 'PNG' # or 'JPEG' or whatever you want
image.save(output, format)
contents = output.getvalue()
output.close()
Radian
la source
14

save()peut prendre un objet semblable à un fichier ainsi qu'un chemin, vous pouvez donc utiliser un tampon en mémoire comme un StringIO:

buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
bobince
la source
Je vous remercie. StringIO - c'est ce dont j'ai besoin.
maxp
L'interface save () est beaucoup plus propre que l'interface tostring () lorsqu'il s'agit d'obtenir un format de fichier PNG, JPEG ou réel.
Kekoa
13

Avec modern (à partir de mi-2017 Python 3.5 et Pillow 4.0):

StringIO ne semble plus fonctionner comme avant. La classe BytesIO est le moyen approprié de gérer cela. La fonction de sauvegarde de Pillow attend une chaîne comme premier argument, et étonnamment ne voit pas StringIO en tant que tel. Ce qui suit est similaire aux anciennes solutions StringIO, mais avec BytesIO à sa place.

from io import BytesIO
from PIL import Image

image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
puissant
la source
2
quel est le but de, image_data = faux_file.getvalue ()?
Ryan le
@Ryan Bonne prise, pas du tout. Il a été laissé des tests et finalement supprimé.
mightypile
9

Lorsque vous dites "J'aimerais avoir un certain nombre de ces images stockées dans le dictionnaire", il n'est pas clair s'il s'agit d'une structure en mémoire ou non.

Vous n'avez pas besoin de faire quoi que ce soit pour adoucir une image en mémoire. Gardez simplement l' imageobjet dans votre dictionnaire.

Si vous allez écrire votre dictionnaire dans un fichier, vous voudrez peut-être regarder la im.tostring()méthode et la Image.fromstring()fonction

http://effbot.org/imagingbook/image.htm

im.tostring () => chaîne

Renvoie une chaîne contenant des données de pixels, en utilisant l'encodeur "brut" standard.

Image.fromstring (mode, taille, données) => image

Crée une mémoire d'image à partir de données de pixels dans une chaîne, en utilisant le décodeur "brut" standard.

Le "format" (.jpeg, .png, etc.) n'a d'importance sur le disque que lorsque vous échangez les fichiers. Si vous n'échangez pas de fichiers, le format n'a pas d'importance.

S.Lott
la source
2
On dirait qu'il veut conserver le format PNG, pas le réduire en données de pixels brutes.
Ben Blank