Python中如何对gif图片进行压缩处理

如题,如何对gif进行压缩。

我一开始想到的办法是利用pillowgif中的每一帧的取出来,对单帧图片进行压缩后,再调用方法将压缩后的保存回去,但是最后得到的gif没有变小,反而变大了。

后来我对单帧图片保存为pnggif格式,明显gif格式比png格式的大小要大多了,对图像编码这一块不是很了解,有知道原因的吗?或者有知道别的对gif进行压缩的办法。。。


Python中如何对gif图片进行压缩处理

26 回复

GIF 压缩一般是跳过某些帧吧,比如 60 帧的你取 20 帧。
GIF 一般都已经被压缩了, 你单帧取出来再压缩也没有什么意义。


帖子标题:Python中如何对gif图片进行压缩处理

处理GIF压缩,用Pillow库最直接。核心思路是调整尺寸、减少帧数、优化调色板。下面是一个完整的示例函数,它接受输入路径、输出路径、目标尺寸和跳帧步长,并生成一个压缩后的GIF。

from PIL import Image
import os

def compress_gif(input_path, output_path, target_size=(300, 300), frame_skip=1):
    """
    压缩GIF图片
    :param input_path: 输入GIF文件路径
    :param output_path: 输出GIF文件路径
    :param target_size: 目标尺寸,格式为(宽, 高)
    :param frame_skip: 帧跳过的步长。例如,2表示每两帧取一帧,以此减少总帧数。
    """
    # 1. 打开GIF文件
    with Image.open(input_path) as img:
        frames = []
        durations = []

        # 2. 遍历每一帧
        frame_index = 0
        try:
            while True:
                # 根据frame_skip参数跳过部分帧
                if frame_index % frame_skip == 0:
                    # 复制当前帧
                    frame = img.copy()
                    # 调整尺寸
                    frame = frame.resize(target_size, Image.Resampling.LANCZOS)
                    # 转换为P模式(调色板模式)以减小文件大小
                    # 使用PIL的量化方法生成一个优化的调色板(最多256色)
                    frame = frame.convert('P', palette=Image.Palette.ADAPTIVE, colors=256)
                    frames.append(frame)
                    # 获取当前帧的持续时间(毫秒)
                    durations.append(img.info.get('duration', 100))  # 默认100毫秒
                frame_index += 1
                # 移动到下一帧
                img.seek(img.tell() + 1)
        except EOFError:
            # 已读取所有帧
            pass

        # 3. 保存压缩后的GIF
        # 注意:为了保持较好的兼容性,这里将第一帧作为调色板参考。
        # `save_all` 参数用于保存多帧,`optimize` 可以尝试进一步优化文件大小。
        if frames:
            frames[0].save(
                output_path,
                save_all=True,
                append_images=frames[1:],
                duration=durations,
                loop=0,  # 0表示无限循环
                disposal=2,  # 背景处置方法,2表示恢复为背景色
                optimize=True
            )
            print(f"GIF压缩完成。原始帧数: {frame_index}, 保留帧数: {len(frames)}")
            print(f"文件已保存至: {output_path}")
        else:
            print("未提取到任何帧。")

# 使用示例
if __name__ == "__main__":
    input_gif = "input.gif"  # 替换为你的输入文件路径
    output_gif = "output_compressed.gif"
    # 压缩到300x300尺寸,每2帧取1帧
    compress_gif(input_gif, output_gif, target_size=(300, 300), frame_skip=2)

代码解释:

  1. 打开与遍历:用Image.open打开GIF,通过while循环和img.seek遍历所有帧。
  2. 跳帧frame_skip参数控制帧采样率,直接减少总帧数是最有效的压缩手段之一。
  3. 调整尺寸:使用resize方法并指定LANCZOS重采样算法来缩小图片。
  4. 颜色优化convert('P', palette=Image.Palette.ADAPTIVE, colors=256)将每帧转换为一个自适应的256色调色板模式,大幅减少颜色存储空间。
  5. 保存:保存时使用optimize=True进行内部优化,disposal=2有助于减少帧间冗余。

总结建议: 调整尺寸和减少帧数对GIF体积影响最大,颜色优化是锦上添花。

第 N+1 帧把第 N 帧重复的像素去掉(变成透明)试试

等全球网络都是万兆连接的时候这个估计就没什么意义了。

现在问题就是,我单帧取出来之后,压缩过后单帧是变小了的,为什么最后整成gif的时候整个gif就变大了

ffpmeg 不是更好吗

GIF 单帧的存储方法都是固定的,你怎么还能把压缩过的单帧图片原封不动地扔进 GIF 容器里去?

imageio 试试

没用过这个,想先看一下能不能自己实现

GIF 单帧的存储方法是怎样的,我不是很了解这个,能不能再说详细点

试过了,我先是将每一帧取出来保存为 png 格式,然后利用 imageio 将所有的帧整合保存到一起,最后生成的 GIF 的大小还是比所有 png 格式的帧的大小要大

固定的调色板+LZ77,管你源是 PNG 还是 JPEG

根据使用工具的经验来看,压缩 GIF 主要是调整调色板、减少颜色,以及最重要的只保留变动的部分。感觉逐帧保存会不会破坏这个?

可以用 ffmpeg 或者是 gifsicle

试试 wand

以前看网络时候,书上好像说当前帧是 f1,下一帧 f2 的得到可能是 f1 加上相对于 f1 的变化,当 f2 和 f1 的差异非常小时这个变化量就非常小

https://en.wikipedia.org/wiki/Motion_JPEG

It is natively supported by the QuickTime Player, the PlayStation console, and web browsers such as Safari, Google Chrome, Mozilla Firefox and Microsoft Edge.


我想问为啥大家不用这个。。

我对 GIF 没有深入的研究过,以下内容仅为猜测;是否是类似视频的 B,I,P 帧呢?在源文件时 GIF 把前后帧相同的区块共用了,但是单独拆帧压缩让前后帧的相同区块破坏掉了。

花了几天没找到很好的解决办法,压缩必定是要牺牲一些的,要么减少帧数,要么对每帧进行一些重复的区域的处理,第二种方案还没有时间试过。在用 pillow 提取帧再组合帧的时候,发现每一帧上多出了个 local color table,感觉是这个增加了大小,但是还没找到办法去除掉。。。

文章讲得挺好的,提供了很多思路

不知道是否会挖坟,不过调用 gifsicle 来进行优化得到的结果还不错。
可以将依赖的二进制文件一并放在工程中。

回到顶部