获取 PIL 图像的二进制表示,无需保存

7

我将写一个需要大量使用图片的应用程序。该应用程序由两部分组成,客户端部分由Python编写。它对图像进行一些预处理,并通过TCP发送到Node.js服务器。在预处理之后,Image对象如下所示:

window = img.crop((x,y,width+x,height+y))
window = window.resize((48,48),Image.ANTIALIAS)

为了通过套接字发送数据,我必须将其转换为二进制格式。现在我正在执行以下操作:
window.save("window.jpg")
infile = open("window.jpg","rb")
encodedWindow = base64.b64encode(infile.read())
#Then send encodedWindow 

然而,这种方法的缺点很明显:我需要先将图像保存到硬盘上,再重新加载它以获得二进制格式。这对我的应用程序来说是一个巨大的负担,导致它变得非常慢。

我阅读了PIL Image的文档,但没有找到有用的信息。


http://stackoverflow.com/questions/23225738/convert-image-to-binary-stream - Padraic Cunningham
4个回答

4
根据文档(在effbot.org上):
“您可以使用文件对象而不是文件名。在这种情况下,您必须始终指定格式。文件对象必须实现seek、tell和write方法,并以二进制模式打开。”
这意味着您可以传递一个StringIO对象。对它进行写入并获取大小,而无需访问磁盘。
像这样:
s = StringIO.StringIO()
window.save(s, "jpg")
encodedWindow = base64.b64encode(s.getvalue())

这会产生以下异常: 文件“/usr/local/lib/python2.7/dist-packages/PIL/Image.py”的第1670行,在保存时引发 raise KeyError(ext) # unknown extension - JackOrJones
@Wahbivic 使用 window.save(s, "jpg")。使用PIL/Pillow,如果无法通过文件名检测到格式(或者像这里一样没有文件名),则需要指定所需的格式。 - MattDMo
@MattDMo 非常感谢。由于某种奇怪的原因,window.save(s, "jpg") 也引发了异常。然而,window.save(s,"jpeg") 却可以正常工作。 - JackOrJones
@swstephe 非常感谢,我也编辑了你的答案,使其真正起作用。 - JackOrJones
@MattDMo 是的,那就是原因。我以为我在过去更新过它,但显然它仍然是1.1.7。 - JackOrJones
显示剩余3条评论

0

使用 BytesIO

from io import BytesIO
from PIL import Image

photo=Image.open('photo.jpg')

s=BytesIO()
photo.save(s,'jpeg')

data = s.getvalue()

with open('photo2.jpg', mode='wb') as f:
    f.write(data)

-1
这篇内容涉及程序开发,讲述了内存文件对象和BufferedReader对象之间的区别。
以下是我在Jupyter(Python 3.8.10)上进行的实验
from PIL import Image as PILImage, ImageOps as PILImageOps
from IPython.display import display, Image
from io import BytesIO
import base64

url = "https://learn.microsoft.com/en-us/archive/msdn-magazine/2018/april/images/mt846470.0418_mccaffreytrun_figure2_hires(en-us,msdn.10).png"
print("get computer-readable bytes from the url")
img_bytes = requests.get(url).content
print(type(img_bytes))
display(Image(img_bytes))
print("convert to in-memory file-like object")
in_memory_file_like_object = BytesIO(img_bytes)
print(type(in_memory_file_like_object))

print("convert to an PIL Image object for manipulating") 
pil_img = PILImage.open(in_memory_file_like_object)
print("let's rotate it, and it remains a PIL Image object") 
pil_img.show()
rotated_img = pil_img.rotate(45)
print(type(rotated_img))
print("let's create an in-memory file-like object and save the PIL Image object into it")  
in_memory_file_like_object = BytesIO()
rotated_img.save(in_memory_file_like_object, 'png')
print(type(in_memory_file_like_object))

print("get computer-readable bytes") 
img_bytes = in_memory_file_like_object.getvalue()
print(type(img_bytes))
display(Image(img_bytes))
print('convert to base64 to be transmitted over channels that do not preserve all 8-bits of data, such as email')
# https://dev59.com/-Wox5IYBdhLWcg3w44Pb#8909233
base_64 = base64.b64encode(img_bytes)
print(type(base_64))
# https://dev59.com/lmct5IYBdhLWcg3wI6J6#45928164
assert base64.b64encode(base64.b64decode(base_64)) == base_64

简而言之,您可以通过如上所示的“rotated_img.save(in_memory_file_like_object,'png')”将PIL图像对象保存到内存文件类对象中,并将内存文件类对象转换为Base64。

-2
from io import BytesIO

b = BytesIO()
img.save(b, format="png")
b.seek(0)
data = b.read()
del b

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