Como escrever uma imagem PNG em string com o PIL?

100

Eu gerei uma imagem usando PIL . Como posso salvá-lo em uma string na memória? O Image.save()método requer um arquivo.

Eu gostaria de ter várias dessas imagens armazenadas no dicionário.

maxp
fonte

Respostas:

194

Você pode usar a BytesIOclasse para obter um invólucro em torno de strings que se comportam como um arquivo. O BytesIOobjeto fornece a mesma interface de um arquivo, mas salva o conteúdo apenas na memória:

import io

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

Você deve especificar explicitamente o formato de saída com o formatparâmetro, caso contrário, o PIL gerará um erro ao tentar detectá-lo automaticamente.

Se você carregou a imagem de um arquivo, ela possui um formatparâmetro que contém o formato do arquivo original, portanto, neste caso, você pode usar format=image.format.

Nas versões antigas do Python 2, antes da introdução do iomódulo, você usaria o StringIOmódulo.

sth
fonte
1
Sim! Isso é exatamente o que eu estava procurando. Eu pesquisei todas as combinações de python, string, reader, writer, buffer e não encontrei nada. Obrigado!
rik.the.vik
2
Não funciona para mim :( Recebi este KeyError em Imaging / PIL / Image.pyc linha 1423 -> raise KeyError (ext) # extensão desconhecida
Radian
6
@Radian, A documentação PIL sobre o método save () diz: You can use a file object instead of a filename. In this case, you must always specify the format.Portanto, se o primeiro argumento é um objeto de arquivo, você deve passar o segundo argumento, que é o formato (por exemplo 'PNG').
Su Zhang
1
Na versão atual do Python, StringIO está no pacote io, você deve escrever:from io import StringIO
Spoutnik16
4
você pode usar melhorimage.save(output, format=image.format)
reetesh11
29

Para Python3, é necessário usar 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')

Leia mais: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image

ozooner
fonte
24

A solução do sth não funcionou para mim
porque em ...

Imaging / PIL / Image.pyc linha 1423 -> aumentar KeyError (ext) # extensão desconhecida

Ele estava tentando detectar o formato da extensão no nome do arquivo, que não existe no caso StringIO

Você pode ignorar a detecção de formato definindo o formato por conta própria em um parâmetro

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

save()pode pegar um objeto semelhante a um arquivo, bem como um caminho, então você pode usar um buffer na memória como StringIO:

buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
bobince
fonte
Obrigado. StringIO - isso é o que eu preciso.
máx.
A interface save () é muito mais limpa do que a interface tostring () quando se trata de obter um formato de arquivo PNG, JPEG ou real.
Kekoa de
13

Com o moderno (em meados de 2017 Python 3.5 e Pillow 4.0):

StringIO parece não funcionar mais como antes. A classe BytesIO é a maneira adequada de lidar com isso. A função save de Pillow espera uma string como o primeiro argumento e, surpreendentemente, não vê StringIO como tal. O seguinte é semelhante a soluções StringIO mais antigas, mas com BytesIO em seu lugar.

from io import BytesIO
from PIL import Image

image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
poderosa pilha
fonte
2
qual é o propósito de image_data = faux_file.getvalue ()?
Ryan
@Ryan Boa captura, sem nenhum propósito. Sobrou do teste e finalmente foi removido.
mightypile de
9

Quando você diz "Eu gostaria de ter algumas dessas imagens armazenadas no dicionário", não fica claro se essa é uma estrutura na memória ou não.

Você não precisa fazer nada disso para suavizar uma imagem na memória. Basta manter o imageobjeto em seu dicionário.

Se você vai escrever seu dicionário em um arquivo, pode querer olhar para o im.tostring()método e a Image.fromstring()função

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

im.tostring () => string

Retorna uma string contendo dados de pixel, usando o codificador "bruto" padrão.

Image.fromstring (modo, tamanho, dados) => imagem

Cria uma memória de imagem a partir de dados de pixel em uma string, usando o decodificador "bruto" padrão.

O "formato" (.jpeg, .png, etc.) só importa no disco quando você está trocando os arquivos. Se você não estiver trocando arquivos, o formato não importa.

S.Lott
fonte
2
Parece que ele deseja manter o formato PNG, não reduzi-lo a dados de pixel brutos.
Ben Blank,