Pillow 循环粘贴图片为何只显示第一个主题?
数据阿明
结论
问题出在 y_offset += height:height 表示整张目标图片的高度,不是单张色板的高度。粘贴第一张色板后,偏移量就到了目标图片底部,后续色板会被粘贴到画布范围之外,因此保存结果中只看得到第一张。
例如,目标画布高度设为 sw_height * len(theme_list),但第一次粘贴后,y_offset 就增加了这个完整高度。第二张色板因此从画布范围外开始粘贴。Pillow 不会自动扩大目标图片。
修正偏移量
如果每张色板的实际高度都是 sw_height,偏移量可以按单张色板的高度递增:
y_offset = 0
for theme_file in theme_list:
# 生成 sw 色板的代码略
swatches.paste(sw, (0, y_offset))
y_offset += sw_height
swatches.save('theme_swatches.png')
也可以根据生成图片的实际高度递增。plt.savefig(buf) 输出的图片高度未必等于变量 sw_height,因此可以使用 sw.height:
swatches.paste(sw, (0, y_offset))
y_offset += sw.height
目标画布需要容纳所有色板。若色板尺寸固定,可以按实际高度创建画布;若尺寸可能不同,可以先收集色板,再根据图片尺寸创建画布:
swatch_images = []
for theme_file in theme_list:
# 读取主题并生成色板,得到 sw
swatch_images.append(sw.copy())
plt.close()
if swatch_images:
canvas_width = max(sw.width for sw in swatch_images)
canvas_height = sum(sw.height for sw in swatch_images)
swatches = Image.new('RGB', (canvas_width, canvas_height))
y_offset = 0
for sw in swatch_images:
swatches.paste(sw, (0, y_offset))
y_offset += sw.height
swatches.save('theme_swatches.png')
检查 Matplotlib 状态
循环绘图时,建议每轮结束后调用 plt.close(),避免图形状态累积或占用资源。保存前也可以将缓冲区位置重置,并在打开图片后显式加载:
buf = BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
sw = Image.open(buf)
sw.load()
这些处理有助于提高循环中的图片处理稳定性。从给出的代码看,后续色板不可见的直接原因是偏移量按整张画布的高度递增。
备注:内容仅供参考。
