使用 python 3.7.3,我想要 base64 编码的照片并调整它的大小而不保存到文件到磁盘并使用 Image.open() 重新打开文件

Posted

技术标签:

【中文标题】使用 python 3.7.3,我想要 base64 编码的照片并调整它的大小而不保存到文件到磁盘并使用 Image.open() 重新打开文件【英文标题】:Using python 3.7.3, I want to have base64 encoded photo and resize it without saving to file to disk and reopening the file using Image.open() 【发布时间】:2019-09-11 16:06:53 【问题描述】:

抱歉,提前发了这么长的帖子。

使用 Python 3.7.3,我想要使用 base64 编码的照片并调整其大小,而不需要将文件保存到磁盘并使用 Image.open() 重新打开文件。

我从This answer 研究了 PyPng,它似乎直接使用 base64 数据,但我不知道如何将其调整为 PIL 图像。我知道如何调整它的大小,一旦它是 Pillow Image,我需要弄清楚的是如何从 base64 直接获取 Pillow Image。

# import tkinter as tk, png
# from PIL import Image, ImageTk
# ... self.PIC_LABEL is a tk.Label() object.
def ShowImage(self, PhotoData=None):
    try:
        tempImg = png.Reader(bytes=PhotoData)
        img = Image.open(tempImg)
        # ... resizing code is working ...
        self.PIC_LABEL.IMG = tk.PhotoImage(img)
        self.PIC_LABEL.configure(image=self.PIC_LABEL.IMG)
    except Exception as e:
        logger.exception(e)

错误是:

FormatError: PNG file has invalid signature. 
Traceback (most recent call last):
    File "C:\Program Files\Python37\lib\site-packages\PIL\Image.py", line 2774, in open
        fp.seek(0) 
AttributeError: 'Reader' object has no attribute 'seek'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
   File "D:/test.py", line 911, in ShowImage    
       img = Image.open(tempImg)
   File "C:\Program Files\Python37\lib\site-packages\PIL\Image.py", line 2776, in open
       fp = io.BytesIO(fp.read())   
   File "C:\Program Files\Python37\lib\site-packages\png.py", line 1813, in read
       self.preamble(lenient=lenient)
   File "C:\Program Files\Python37\lib\site-packages\png.py", line 1609, in preamble
       self.validate_signature()
   File "C:\Program Files\Python37\lib\site-packages\png.py", line 1595, in validate_signature
       raise FormatError("PNG file has invalid signature.") 
png.FormatError: FormatError: PNG file has invalid signature.

我也试过Image.open(base64.decodebytes(PhotoByteDataDict[name])),但它给出了“无效的起始字节错误”。

更新:我尝试了基于this answer 的其他方法:

rawDefect = PhotoByteData['defect']
msg = base64.b64decode(rawDefect)  # base64.decodebytes(rawDefect) doesn't work either.
buf = io.BytesIO(msg)
tempImg = Image.open(buf)
size = tempImg.size
size = (size[0]*2, size[1]*2)
tempImg = tempImg.resize(size)
print(tempImg)
newImg = tk.PhotoImage(tempImg)
lbl.configure(image=newImg)

这使我可以成功创建 Pillow Image 和 tk.PhotoImage,但是当您尝试显示它时会崩溃:

Traceback (most recent call last):
  File "D:/OneDrive/WorkSpace/PyCharm WorkSpace/TruePad/DEV/DevTest.py", line 2907, in <module>
<PIL.Image.Image image mode=RGBA size=480x270 at 0x26DBB5DCDD8>
    lbl.configure(image=newImg)
  File "C:\Program Files\Python37\lib\tkinter\__init__.py", line 1485, in configure
    return self._configure('configure', cnf, kw)
  File "C:\Program Files\Python37\lib\tkinter\__init__.py", line 1476, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
TypeError: __str__ returned non-string (type Image)

看了tk.PhotoImage的源代码也累了:

tkDefect = tk.PhotoImage(data=rawDefect)
newHeight = 480 / tkDefect.height()
newWidth = 800 / tkDefect.width()
newSize = (newWidth, newHeight)
newImg = tkDefect.zoom(int(newWidth * tkDefect.width()), int(newHeight * tkDefect.height()))
lbl.configure(image=newImg)

但在尝试显示时再次崩溃,出现不同的错误:

Traceback (most recent call last):
  File "Test.py", line 2919, in <module>
    newImg = tkDefect.zoom(int(newWidth * tkDefect.width()), int(newHeight * tkDefect.height()))
  File "C:\Program Files\Python37\lib\tkinter\__init__.py", line 3568, in zoom
    self.tk.call(destImage, 'copy', self.name, '-zoom',x,y)
_tkinter.TclError: not enough free memory for image buffer

有什么想法吗?

【问题讨论】:

【参考方案1】:

感谢Post 找到了答案,这让我找到了ImageTk.PhotoImage:

msg = base64.b64decode(rawDefect)
with io.BytesIO(msg) as buf:
    with Image.open(buf) as tempImg:
        newWidth = 400 / tempImg.width  # change this to what ever width you need.
        newHeight = 240 / tempImg.height # change this to what ever height you need.
        newSize = (int(newWidth * tempImg.width), int(newHeight * tempImg.height))
        newImg1 = tempImg.resize(newSize)
        lbl1.IMG = ImageTk.PhotoImage(image=newImg1)
        lbl1.configure(image=lbl1.IMG)

        newWidth = 400 / tempImg.width  # change this to what ever width you need.
        newHeight = 240 / tempImg.height # change this to what ever height you need.
        newSize = (int(newWidth * tempImg.width), int(newHeight * tempImg.height))
        newImg2 = tempImg.resize(newSize)
        lbl2.IMG = ImageTk.PhotoImage(image=newImg2)
        lbl2.configure(image=lbl2.IMG)

【讨论】:

以上是关于使用 python 3.7.3,我想要 base64 编码的照片并调整它的大小而不保存到文件到磁盘并使用 Image.open() 重新打开文件的主要内容,如果未能解决你的问题,请参考以下文章

python 3.7.3 - concurent.futures的等待问题

在 python 3.6 上工作但不在 3.7.3 上工作的方法的记忆

python3.7.3使用pip安装mysqldb报错,3.7.3后改为pymysql

如何在使用 cx_Freeze 6.0b1 冻结的 Linux 上修复 python 3.7.3 脚本上的 numpy 依赖项路径?

7_python之路之python计算器

卸载 python 3.7.3 再安装 遇到 error 0x80070001