PIL循环粘贴图片异常:仅首个主题色板成功添加的问题排查
问题
我写了一段脚本,遍历一组JSON配色主题文件,用seaborn和matplotlib生成带标签的色板并保存到缓冲区,再把缓冲区内容粘贴到新的PIL.Image里,但只有首个主题的色板被添加到PIL.Image中。
代码如下:
sw_width = 1084 sw_height = 100 theme_list = listdir('themes') width = sw_width height = sw_height*len(theme_list) swatches = Image.new('RGB', (width, height)) y_offset = 0 # Loop through all themes, drawing a swatch for each theme for theme_file in theme_list: with open('themes/' + theme_file) as th: # load theme theme = json.load(th) # extract theme name and delete theme_name = theme.pop('name') theme_description = theme.pop('description') # Draw swatch sns.palplot(theme.values()) # Add title to swatch plt.title(theme_name) # Add key and hexadecimal color for i, key in enumerate(theme): plt.text( i, 0.2, key+'\n'+theme[key], ha='center', va='bottom', color=invert_hex(theme[key]), fontsize=7 ) plt.tight_layout() buf = BytesIO() buf.seek(0) plt.savefig(buf) sw = Image.open(buf) swatches.paste(sw, (0,y_offset)) y_offset += height swatches.save('theme_swatches.png')
其中invert_hex函数用于设置文本反色,每个色板在Jupyter Notebook里都能正确生成,请问我哪里操作错了?
解决方法
错误1:Matplotlib绘图未重置
每次循环调用sns.palplot()和plt.title()后,Matplotlib的当前绘图对象没有被清空,后续的绘图会直接叠加在之前的图上,导致从缓冲区读取的始终是包含所有主题的叠加图,而非单个主题的独立色板。错误2:y_offset累加逻辑错误
你写的y_offset += height,但height是最终合成图的总高度(sw_height*len(theme_list)),这会导致第一次循环后y_offset直接跳到超出图片范围的位置,后续的粘贴操作根本不会显示在画布上。应该改成每次累加单个色板的高度sw_height。
修正后的代码:
import os import json import seaborn as sns import matplotlib.pyplot as plt from PIL import Image from io import BytesIO sw_width = 1084 sw_height = 100 theme_list = os.listdir('themes') width = sw_width height = sw_height * len(theme_list) swatches = Image.new('RGB', (width, height)) y_offset = 0 # 遍历所有主题,生成单个色板并拼接 for theme_file in theme_list: with open(os.path.join('themes', theme_file)) as th: theme = json.load(th) theme_name = theme.pop('name') theme_description = theme.pop('description') # 每次循环创建新画布,避免绘图叠加 plt.figure(figsize=(sw_width/100, sw_height/100)) # 按像素设置画布尺寸(dpi默认100) sns.palplot(list(theme.values())) plt.title(theme_name) for i, key in enumerate(theme): plt.text( i, 0.2, f'{key}\n{theme[key]}', ha='center', va='bottom', color=invert_hex(theme[key]), fontsize=7 ) plt.tight_layout() buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) sw = Image.open(buf) swatches.paste(sw, (0, y_offset)) y_offset += sw_height # 累加单个色板的高度 plt.close() # 关闭当前画布,释放内存资源 swatches.save('theme_swatches.png')
补充说明:
- 用
plt.figure()每次创建独立画布,确保每个主题的色板互不干扰; - 用
plt.close()及时关闭画布,避免内存占用过高; - 改用
os.path.join()拼接文件路径,适配不同操作系统的路径规则。
内容的提问来源于stack exchange,提问作者zoof
相关产品推荐
相关产品推荐

