首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ffmpeg 生成高质量 gif

ffmpeg 生成高质量 gif

原创
作者头像
xiaoxia
发布2024-11-29 20:51:19
发布2024-11-29 20:51:19
79200
代码可运行
举报
文章被收录于专栏:ffmpegffmpeg
运行总次数:0
代码可运行

1、GIF 简单介绍

1、GIF 特点

GIF(Graphics Interchange Format)是网络中常见的一种图像交互格式,普遍用于表情包。GIF 有很多特点:

  • 是一种位图而不是矢量图,即是使用像素阵列来表示的图像,认为图片是由许多像素点组成;
  • 仅支持8bit 索引色,即在整个图片中,由于索引 8bit 的限制,只能存在 256 种不同的颜色;
  • 采用 LZW(Lempel-Zev-Welch)无损压缩算法,基本思想是:企图从输入“数据”中创建一个“短语词典”,编码过程中,当遇到已在字典中出现的“短语”时,编码器就输出对应的“索引号”,而不是短语本身,从而压缩数据。而在 GIF 中,“数据”就是原始像素点的颜色,"短语”就是颜色,“短语字典” 就是颜色列表,也称为调色板

2、GIF 文件数据格式

在色彩列表中,单个颜色采用 RGB24(色彩深度24) 存储,而调色板受到 8bit 的索引限制,最多只能存储 256 种颜色。这就决定了一个 gif 中,最多包含只能包含 256 中 RGB24 的颜色值。本文只探索画面质量,其他内容不展开。

2、ffmpeg 默认 gif 质量问题

直接截取一段视频,保存为 gif 动画:

代码语言:shell
复制
ffmpeg -i in.mp4 -y out.gif

对比原视频(见附件)可以发现,视频画面中会出现很多栅格形状的小点,画面质量严重下降。

2、ffmpeg 默认 gif 画面质量问题分析

1、色彩下采样

ffmpeg 的 gif encoder 在实现中,为了方便生成调色板和调色板查找,粗暴的限制了像素类型,默认采用的是AV_PIX_FMT_RGB8这类非真彩色。以 RGB8(色彩深度8)为例,实际每个像素点的颜色只有 8bit,即8位色,所以图像最多只能表达 256 种颜色 。

而我们最常见的图片通常使用的是“真彩色“(RGB/24 或者 RGBA/32),这样可以表示至少 16777216 种颜色。如果直接将一个真色彩图片直接转为8位色图片,这中间就涉及到一个下采样的过程,会导致信息丢失严重,画面质量下降。

使用 ffmpeg 进行直观对比,对视频截取一帧并保存不同的色位:

A、RGB8

代码语言:shell
复制
ffmpeg -t 00:00:00.001 -i in.mp4 -an -vf "format=rgb8" -y out.png

B、RGB24

代码语言:shell
复制
ffmpeg -t 00:00:00.001 -i in.mp4 -an -vf "format=rgb24" -y out.png

对比可以发现,下采样后画面质量很低,和我们直接 ffmpeg 将 mp4 转 gif 画面失真效果一致的。在画面进入 gif encoder 之前,画面质量已经降到很差了。LZW 算法本身只是一个数据压缩算法,虽然是无损的,此时也只能将低画质的图像无损的压缩,不能提高质量。

2、调色板问题

因为 ffmpeg gif encoder,默认 AV_PIX_FMT_RGB8 这类八色位,最多只能表达 256 种颜色,所以在生成调色板时,也是简单粗暴的生成 256 个颜色。

libavutil\imgutils.c

代码语言:c
代码运行次数:0
运行
复制
int avpriv_set_systematic_pal2(uint32_t pal[256], enum AVPixelFormat pix_fmt)
{
    int i;
    for (i = 0; i < 256; i++) {
        int r, g, b;
        switch (pix_fmt) {
        case AV_PIX_FMT_RGB8:
            r = (i>>5    )*36;
            g = ((i>>2)&7)*36;
            b = (i&3     )*85;
            break;
        // ... 其他像素格式
        }
        pal[i] = b + (g << 8) + (r << 16) + (0xFFU << 24);
    }
    return 0;
}

这里要区分两个概念:

  • 8bit索引色:调色板的大小限制,整个调色板的索引只支持 8bit,也就是只能存储 256 种色彩。但色彩值是支持真色彩的 RGB24,也就是表达 16777216 种颜色的能力。gif 调色板实质是通过分析图像,从 16777216 种颜色中选取 256 种使用率最高的颜色。
  • 8色位:比如典型的 RGB8,单个像素具有表达 256 种颜色的能力。ffmpeg 采用这类像素格式,可以快速生成调色板(不用分析图像),同时提高编码速度。但实际图像中,很难有全域的颜色,所以调色板中有效的色彩数很少。

3、解决方案

为了解决图像送入 encoder 之前画质降低,这里我们就要尽可能使用高画质的像素格式。其实 ffmpeg 的 encoder 保留了一种特殊的像素格式来支持真色彩,就是 AV_PIX_FMT_PAL8 。

PAL8 在 ffmpeg 是一种比较特殊的像素格式,之前也遇到过 ffmpeg 在处理这种格式时的遇到 PAL8问题 的例子。通过 ffmpeg 源码可以简单理解 AV_PIX_FMT_PAL8 为 AV_PIX_FMT_RGB32 加一个 1024 bit pal 数据的格式。

libavutil\pixfmt.h

代码语言:c
代码运行次数:0
运行
复制
/*
 * When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized
 * image data is stored in AVFrame.data[0]. The palette is transported in
 * AVFrame.data[1], is 1024 bytes long (256 4-byte entries) and is
 * formatted the same as in AV_PIX_FMT_RGB32 described above (i.e., it is
 * also endian-specific). Note also that the individual RGB32 palette
 * components stored in AVFrame.data[1] should be in the range 0..255.
 * This is important as many custom PAL8 video codecs that were designed
 * to run on the IBM VGA graphics adapter use 6-bit palette components.
 */
enum AVPixelFormat {
    // ...
    AV_PIX_FMT_PAL8,      ///< 8 bits with AV_PIX_FMT_RGB32 palette
    // ...
}

不过在这种格式下,ffmpeg gif encoder 有些限制:需要外部先定义好调色板。这里我们就需要使用两个生成调色板的 filter 了:

  • palettegen:通过分析 n 帧画面生成调色板,并存储在 AVFrame 的 metadata["lavfi.color_quant_ratio"] 中;
  • paletteuse:根据 palettegen 生成的调色板,将调色板和原始帧合并,并以 AV_PIX_FMT_PAL8 的格式输出;

整个用法如下:

代码语言:shell
复制
ffmpeg -i in.mp4 -vf "split[s1][s2];[s1]trim=start_frame=0:end_frame=20, palettegen[p];[s2][p]paletteuse" -y out.gif 

得到效果如下,质量明显得到了改善:

4、总结

  • ffmpeg 直接转 gif 时,画面质量严重降低的根本原因是:内部 gif encoder 实现时,粗暴的限制了像素类型,默认采用的是 AV_PIX_FMT_RGB8 这类非真彩色像素格式,导致图像送入 encoder 之前就被下采样,画质失真。
  • 为了解决这个问题,可以需要在编码前,先通过 palettegen 滤镜分析一定数量的图像帧,生成一个调色板,然后通过 paletteuse 滤镜将调色板和原始真彩色图像合并,以 AV_PIX_FMT_PAL8 的形式送给 encoder,通过 encoder 对这类格式的特殊支持实现高画质的 gif 编码。
  • palettegen 分析图像帧时,会缓存对应数量的帧,会占用内存,虽然尽可能多的分析图像帧能更准确的生成调色板,但是对内存有一定压力。如果整个 gif 画面色彩变动不是很大时,一般选取前面部分帧就能生成比较准确的调色板。

5、参考

1、https://zhuanlan.zhihu.com/p/440145283

2、https://www.techug.com/post/gif-image-structure-intro/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、GIF 简单介绍
  • 2、ffmpeg 默认 gif 质量问题
  • 2、ffmpeg 默认 gif 画面质量问题分析
  • 3、解决方案
  • 4、总结
  • 5、参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档