You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pillow库将GIF转换为方形动画WebP时,裁剪效果未生效的问题

解决Pillow转换GIF为方形WebP时裁剪/缩放效果不生效的问题

我帮你揪出代码里的问题啦!你的裁剪逻辑本身是对的,但有两个关键细节没处理好,导致最终保存的WebP没应用裁剪效果:

问题1:保存时用了原始帧而非裁剪后的帧

你辛苦裁剪好的帧都存在cropped_frames列表里,但最后保存的时候却传入了list(frames)——而且ImageSequence.Iterator是一次性迭代器,遍历过一次之后就空了,等于你传了个空列表进去,自然用的还是原始的第一帧,没应用裁剪。

问题2:裁剪参数的作用域隐患

如果输入的GIF本身已经是方形(宽高相等),那left、top、right、bottom这些变量根本没被定义,运行到裁剪步骤时会直接报错,得给这些参数加个默认值。

修复后的完整代码

from pathlib import Path
from PIL import Image, ImageSequence

source = "你的源GIF路径"  # 记得替换成实际路径
destination = Path(source).with_suffix(".webp")

im = Image.open(source)
width, height = im.size

# 先给裁剪参数设置默认值(对应方形的情况)
left = 0
top = 0
right = width
bottom = height

if width != height:
    if width > height:
        left = (width - height) / 2
        right = (width + height) / 2
        top = 0
        bottom = height
    else:
        top = (height - width) / 2
        bottom = (height + width) / 2
        left = 0
        right = width

cropped_im = im
if getattr(im, "is_animated", False):
    frames = ImageSequence.Iterator(im)
    cropped_frames = []
    for frame in frames:
        # 拷贝帧避免和原始帧产生引用关联
        cropped_frame = frame.copy().crop((left, top, right, bottom))
        cropped_frames.append(cropped_frame)
    cropped_im = cropped_frames[0]  # 取第一帧作为保存的基础
else:
    # 非动画单帧直接裁剪
    cropped_im = im.crop((left, top, right, bottom))

# 整理保存参数,分动画/单帧处理
save_kwargs = {
    "format": "webp",
    "optimize": True,
    "loop": 0
}
if getattr(im, "is_animated", False):
    save_kwargs["save_all"] = True
    save_kwargs["append_images"] = cropped_frames[1:]  # 第一帧已作为基础,只传后续帧

cropped_im.save(destination, **save_kwargs)

关键修改说明

  • 提前初始化裁剪参数,避免宽高相等时变量未定义的错误
  • 处理动画帧时给每个帧做copy()再裁剪,防止和原始帧的引用冲突
  • 保存动画时,append_images传入裁剪后的帧列表(去掉第一帧,因为基础帧已经设置好了)
  • 分情况处理动画和单帧的保存参数,逻辑更严谨

内容的提问来源于stack exchange,提问作者kipteam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 15:47:31