如何在PIL中缩放动画GIF并保留动画效果

7 投票
3 回答
6931 浏览
提问于 2025-04-17 12:15

我在想,能不能用PIL来调整一个动图(GIF)的大小。特别是,Plone的archetypes ImageField在用它的scale方法缩放图片时,会丢失动图的动画效果:

def scale(self, data, w, h, default_format = 'PNG'):
    """ scale image (with material from ImageTag_Hotfix)"""
    #make sure we have valid int's
    size = int(w), int(h)

    original_file=StringIO(data)
    image = PIL.Image.open(original_file)
    # consider image mode when scaling
    # source images can be mode '1','L,','P','RGB(A)'
    # convert to greyscale or RGBA before scaling
    # preserve palletted mode (but not pallette)
    # for palletted-only image formats, e.g. GIF
    # PNG compression is OK for RGBA thumbnails
    original_mode = image.mode
    img_format = image.format and image.format or default_format
    if original_mode == '1':
        image = image.convert('L')
    elif original_mode == 'P':
        image = image.convert('RGBA')
    image.thumbnail(size, self.pil_resize_algo)
    # decided to only preserve palletted mode
    # for GIF, could also use image.format in ('GIF','PNG')
    if original_mode == 'P' and img_format == 'GIF':
        image = image.convert('P')
    thumbnail_file = StringIO()
    # quality parameter doesn't affect lossless formats
    image.save(thumbnail_file, img_format, quality=self.pil_quality)
    thumbnail_file.seek(0)
    return thumbnail_file, img_format.lower()

我知道怎么判断一个动图:可以用下面的代码来检查,结果会是True image.format == 'GIF' and image.seek(image.tell()+1)。我试过不转换成RGBA模式,但这样也不行。

背景:在我们的Plone实例中,我们修改了默认的图片类型,把图片字段的original_size属性设置为强制所有图片以合适的质量进行缩放。这样对jpeg格式的图片效果很好,但目前我们无法上传动图(GIF)。

3 个回答

0

Pillow(PIL的一个分支)从版本3.0.0开始支持动画GIF,这个版本是在PR #1384合并后发布的。

8

你可以使用images2gif.py这个工具来读取gif动画,然后可以独立地调整每一帧的大小。images2gif可以让你用一系列的图片来制作一个动画gif。

我在网上找到的images2gif.py不支持透明背景,所以我对它进行了修复。你可以在这里找到我修改过的版本: https://bitbucket.org/bench/images2gif.py

5

PIL对动画GIF的支持有限,正如所说的那样,你需要在很低的层面上进行操作。

虽然在当前版本(9.2)中,你可以在PIL(Pillow)中处理和缩放GIF,但这过程相当复杂。它只提供每一帧的“渲染”版本:也就是说,如果你的GIF每一帧有不同的调色板或形状,你只能获取到在图像查看程序中显示的扁平化帧。

如果你想处理动画GIF,建议尝试其他方法来缩放图像,而不是使用PIL。最简单的方法可能是使用一个外部的ImageMagick工具,通过subprocess.Popen来调用——不过目前我只能猜测ImageMagick在处理动画GIF时“能做到正确的事情”。

还有一个选择是建立一个“图像处理服务器”,用另一个Python脚本,和你的zope安装分开,这样它就能接收缩放图像的请求——可能通过xmlrpc调用。你可以把这个做成一个GIMP插件,利用GIMP来缩放GIF。

另一种选择是保持现状,对于需要在不同尺寸下显示的动画GIF,使用“静态图像”,而在合适的地方显示原始动画图像。(或者干脆要求提交的动画GIF就已经是合适的尺寸)

撰写回答