如何使用PIL将PNG图像写入字符串?

124
我使用PIL生成了一张图片。如何将其保存到内存中的字符串?Image.save()方法需要一个文件。
我想将多张这样的图片存储在字典中。
6个回答

237
你可以使用BytesIO类来获取一个字符串的包装器,它的行为像一个文件。 BytesIO对象提供与文件相同的接口,但将内容保存在内存中:
import io

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

您需要使用format参数显式指定输出格式,否则当PIL尝试自动检测时,它会引发错误。

如果您从文件加载图像,则具有包含原始文件格式的format属性,因此在这种情况下,您可以使用format=image.format

在Python 2旧版本中,在引入io模块之前,您将使用StringIO模块。


1
是的!这正是我在寻找的。我尝试了Python、字符串、读取器、写入器、缓冲等所有组合,但没有找到任何信息。谢谢! - rik.the.vik
2
对我不起作用 :( 在Imaging/PIL/Image.pyc的1423行中出现了KeyError
-> raise KeyError(ext) # 未知扩展名
- tabdulradi
6
@Radian,PIL文档中的save()方法说明:您可以使用文件对象而不是文件名。在这种情况下,您必须始终指定格式。因此,如果第一个参数是文件对象,您必须传递第二个参数,即格式(例如'PNG')。 - Su Zhang
1
在当前的Python版本中,StringIO位于io包中,您必须编写:from io import StringIO - Spoutnik16
4
你可以更好地使用 image.save(output, format=image.format) - reetesh11
显示剩余5条评论

31

对于Python3,需要使用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')

阅读更多:http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image


26

sth的解决方案对我没有用,因为在……

Imaging/PIL/Image.pyc第1423行 -> raise KeyError(ext)#未知扩展名

它试图从文件名的扩展名检测格式,在StringIO情况下不存在。

您可以通过设置参数中的格式来绕过格式检测。

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

16

save()方法不仅可以接受文件路径作为参数,还可以接受类似于StringIO的内存缓冲区对象:

buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()

谢谢。StringIO - 这正是我所需要的。 - maxp
当涉及到获取PNG、JPEG或实际文件格式时,save()接口比tostring()接口更加清晰。 - Kekoa

15

使用现代(截至2017年中期的Python 3.5和Pillow 4.0):

StringIO不再起作用,BytesIO类是处理此问题的正确方法。 Pillow的保存函数将字符串作为第一个参数,令人惊讶的是不将StringIO视为字符串。以下内容类似于旧的StringIO解决方案,但使用BytesIO代替。

from io import BytesIO
from PIL import Image

image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')

2
“image_data = faux_file.getvalue()” 的目的是什么? - Ryan
@Ryan 很好的发现,完全没有用途。它是测试中遗留下来的,最终被删除了。 - mightypile

9
当你说“我想在字典中存储这些图像的数量”时,不清楚这是否是一个内存结构。
你不需要做任何事情来保存一张图片在内存中,只需将image对象保存在您的字典中即可。
如果您要将字典写入文件,则可以查看im.tostring()方法和Image.fromstring()函数。 http://effbot.org/imagingbook/image.htm

im.tostring() => string

返回包含像素数据的字符串,使用标准的“原始”编码器。

Image.fromstring(mode, size, data) => image

从字符串中的像素数据创建图像内存,使用标准的“原始”解码器。

“格式”(.jpeg、.png等)仅在交换文件时才在磁盘上起作用。如果您不交换文件,则格式无关紧要。

2
听起来他想保留PNG格式,而不是将其缩小为原始像素数据。 - Ben Blank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接