openpyxl中按PIL风格等比例缩放图片遇错,求可行解决方案
解决openpyxl中图片按比例缩放并插入Excel的问题
我来帮你搞定这个问题!你遇到的两个错误其实都是因为命名冲突和没有正确区分PIL与openpyxl的Image类导致的,咱们一步步拆解解决:
问题根源分析
AttributeError: 'Image' object has no attribute 'resize':
你代码里导入的Image来自openpyxl.drawing.image,这个类仅负责将图片导入Excel,本身没有图片缩放的resize方法。而你注释掉了PIL的Image导入,导致screen_scaling里调用的是openpyxl的Image对象,自然触发报错。UnboundLocalError: local variable 'rel' referenced before assignment:
这个错误通常是因为你尝试用PIL处理图片后,没有生成openpyxl能正常识别的图片源,再加上命名冲突干扰了类的初始化流程,最终导致变量引用异常。
解决方案:明确区分两类Image类
核心思路是给PIL和openpyxl的Image类重命名导入,彻底避免混淆,然后用PIL完成缩放,再交给openpyxl插入Excel。
方案1:保存缩放后的图片到本地(简单直观)
下面是修正后的完整代码,关键部分我加了注释:
import openpyxl # 重命名PIL的Image类,避免和openpyxl的冲突 from PIL import ImageGrab, Image as PILImage # 同样重命名openpyxl的Image类 from openpyxl.drawing.image import Image as XLImage image_scale = 2 column_start = 3 row_start = 3 def screen_capture(): img = ImageGrab.grab() img.save('test.jpg', 'JPEG') def screen_scaling(): # 用PILImage加载本地图片,这才是真正能缩放的图片对象 img = PILImage.open('test.jpg') # 按比例计算新尺寸,保持宽高比 new_width = int(img.width / image_scale) new_height = int(img.height / image_scale) # 注意:PIL 9.1.0之后ANTIALIAS被重命名为LANCZOS,低版本可以用PILImage.ANTIALIAS resized_img = img.resize((new_width, new_height), PILImage.Resampling.LANCZOS) # 保存缩放后的图片(可以覆盖原文件,也可以存新文件) resized_img.save('test_resized.jpg', 'JPEG') wb = openpyxl.Workbook() ws = wb.active screen_capture() screen_scaling() # 用XLImage加载缩放后的图片,插入到Excel指定位置 img1 = XLImage('test_resized.jpg') img1.anchor = ws.cell(row=row_start, column=column_start).coordinate ws.add_image(img1) wb.save('Save_test_data.xlsx') wb.close()
方案2:用内存字节流处理(无需生成临时文件)
如果不想生成本地临时文件,可以用io.BytesIO把缩放后的图片存在内存里,直接传给openpyxl,效率更高:
import openpyxl import io from PIL import ImageGrab, Image as PILImage from openpyxl.drawing.image import Image as XLImage image_scale = 2 column_start = 3 row_start = 3 def screen_capture(): img = ImageGrab.grab() img.save('test.jpg', 'JPEG') def screen_scaling(): img = PILImage.open('test.jpg') new_width = int(img.width / image_scale) new_height = int(img.height / image_scale) resized_img = img.resize((new_width, new_height), PILImage.Resampling.LANCZOS) # 将图片保存到内存字节流 img_bytes = io.BytesIO() resized_img.save(img_bytes, 'JPEG') img_bytes.seek(0) # 重置文件指针到开头,让openpyxl能读取 return img_bytes wb = openpyxl.Workbook() ws = wb.active screen_capture() # 获取内存中的缩放图片 resized_img_stream = screen_scaling() # 直接用字节流初始化XLImage img1 = XLImage(resized_img_stream) img1.anchor = ws.cell(row=row_start, column=column_start).coordinate ws.add_image(img1) wb.save('Save_test_data.xlsx') wb.close()
关键注意事项
- 务必区分PIL和openpyxl的Image类,重命名导入是最稳妥的方式,避免变量混淆。
- 关于PIL的缩放滤镜:如果你的PIL版本低于9.1.0,继续用
PILImage.ANTIALIAS即可;高版本推荐用PILImage.Resampling.LANCZOS,两者效果一致,但后者是官方现在推荐的写法。
内容的提问来源于stack exchange,提问作者kristo
相关产品推荐
相关产品推荐

