¿Cómo escribir una imagen PNG en una cadena con el PIL?

99

He generado una imagen usando PIL . ¿Cómo puedo guardarlo en una cadena en la memoria? El Image.save()método requiere un archivo.

Me gustaría tener varias imágenes de este tipo almacenadas en el diccionario.

maxp
fuente

Respuestas:

194

Puede usar la BytesIOclase para obtener un contenedor alrededor de cadenas que se comporte como un archivo. El BytesIOobjeto proporciona la misma interfaz que un archivo, pero guarda el contenido solo en la memoria:

import io

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

Debe especificar explícitamente el formato de salida con el formatparámetro, de lo contrario, PIL generará un error al intentar detectarlo automáticamente.

Si cargó la imagen desde un archivo, tiene un formatparámetro que contiene el formato de archivo original, por lo que en este caso puede usar format=image.format.

En las versiones antiguas de Python 2 antes de la introducción del iomódulo, habría utilizado el StringIOmódulo en su lugar.

algo
fuente
1
¡Si! Esto es exactamente lo que estaba buscando. Busqué en Google cada combinación de pitón, cadena, lector, escritor, búfer y no se me ocurrió nada. ¡Gracias!
rik.the.vik
2
No funciona para mí :( Obtuve este KeyError en Imaging / PIL / Image.pyc línea 1423 -> aumentar KeyError (ext) # extensión desconocida
Radian
6
@Radian, La documentación de PIL sobre el método save () dice: You can use a file object instead of a filename. In this case, you must always specify the format.Entonces, si el primer argumento es un objeto de archivo, debe pasar el segundo argumento, que es el formato (por ejemplo 'PNG').
Su Zhang
1
En la versión actual de Python, StringIO está en el paquete io, debe escribir:from io import StringIO
Spoutnik16
4
puedes usar mejorimage.save(output, format=image.format)
reetesh11
29

Para Python3, es necesario utilizar 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')

Leer más: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image

ozooner
fuente
24

La solución de sth no funcionó para mí
porque en ...

Imaging / PIL / Image.pyc línea 1423 -> subir KeyError (ext) # extensión desconocida

Intentaba detectar el formato de la extensión en el nombre del archivo, que no existe en el caso de StringIO

Puede omitir la detección de formato configurando el formato usted mismo en un parámetro

import StringIO
output = StringIO.StringIO()
format = 'PNG' # or 'JPEG' or whatever you want
image.save(output, format)
contents = output.getvalue()
output.close()
Radián
fuente
14

save()puede tomar un objeto similar a un archivo, así como una ruta, por lo que puede usar un búfer en memoria como StringIO:

buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
bobince
fuente
Gracias. StringIO: eso es lo que necesito.
maxp
La interfaz save () es mucho más limpia que la interfaz tostring () cuando se trata de obtener un formato de archivo PNG, JPEG o real.
Kekoa
13

Con moderno (desde mediados de 2017 Python 3.5 y Pillow 4.0):

StringIO ya no parece funcionar como solía hacerlo. La clase BytesIO es la forma correcta de manejar esto. La función de guardar de Pillow espera una cadena como primer argumento y, sorprendentemente, no ve a StringIO como tal. Lo siguiente es similar a las soluciones StringIO más antiguas, pero con BytesIO en su lugar.

from io import BytesIO
from PIL import Image

image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
mightypile
fuente
2
¿Cuál es el propósito de image_data = faux_file.getvalue ()?
Ryan
@ Ryan Buena captura, sin ningún propósito. Quedó de las pruebas y finalmente se eliminó.
mightypile
9

Cuando dice "Me gustaría tener un número de esas imágenes almacenadas en el diccionario", no está claro si se trata de una estructura en memoria o no.

No necesitas hacer nada de esto para guardar una imagen en la memoria. Simplemente mantenga el imageobjeto en su diccionario.

Si va a escribir su diccionario en un archivo, es posible que desee ver el im.tostring()método y la Image.fromstring()función

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

im.tostring () => cadena

Devuelve una cadena que contiene datos de píxeles, utilizando el codificador "crudo" estándar.

Image.fromstring (modo, tamaño, datos) => imagen

Crea una memoria de imagen a partir de datos de píxeles en una cadena, utilizando el decodificador "crudo" estándar.

El "formato" (.jpeg, .png, etc.) sólo importa en el disco cuando intercambia los archivos. Si no intercambia archivos, el formato no importa.

S.Lott
fuente
2
Parece que quiere conservar el formato PNG, no reducirlo a datos de píxeles sin procesar.
Ben Blank