1、GIF 特点
GIF(Graphics Interchange Format)是网络中常见的一种图像交互格式,普遍用于表情包。GIF 有很多特点:
8bit 索引色
,即在整个图片中,由于索引 8bit 的限制,只能存在 256 种不同的颜色;调色板
。2、GIF 文件数据格式
在色彩列表中,单个颜色采用 RGB24(色彩深度24) 存储,而调色板受到 8bit 的索引限制,最多只能存储 256 种颜色。这就决定了一个 gif 中,最多包含只能包含 256 中 RGB24 的颜色值。本文只探索画面质量,其他内容不展开。
直接截取一段视频,保存为 gif 动画:
ffmpeg -i in.mp4 -y out.gif
对比原视频(见附件)可以发现,视频画面中会出现很多栅格形状的小点,画面质量严重下降。
1、色彩下采样
ffmpeg 的 gif encoder 在实现中,为了方便生成调色板和调色板查找,粗暴的限制了像素类型,默认采用的是AV_PIX_FMT_RGB8
这类非真彩色。以 RGB8(色彩深度8)为例,实际每个像素点的颜色只有 8bit,即8位色,所以图像最多只能表达 256 种颜色 。
而我们最常见的图片通常使用的是“真彩色“(RGB/24 或者 RGBA/32),这样可以表示至少 16777216 种颜色。如果直接将一个真色彩图片直接转为8位色图片,这中间就涉及到一个下采样的过程,会导致信息丢失严重,画面质量下降。
使用 ffmpeg 进行直观对比,对视频截取一帧并保存不同的色位:
A、RGB8
ffmpeg -t 00:00:00.001 -i in.mp4 -an -vf "format=rgb8" -y out.png
B、RGB24
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
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 采用这类像素格式,可以快速生成调色板(不用分析图像),同时提高编码速度。但实际图像中,很难有全域的颜色,所以调色板中有效的色彩数很少。为了解决图像送入 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
/*
* 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 的格式输出;整个用法如下:
ffmpeg -i in.mp4 -vf "split[s1][s2];[s1]trim=start_frame=0:end_frame=20, palettegen[p];[s2][p]paletteuse" -y out.gif
得到效果如下,质量明显得到了改善:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。