前言

使用FFmpeg能够很方便的给视频片段或GIF加水印,同时还能对选取的片段生成GIF图,但是在使用默认FFmpeg设置情况下,生成的GIF画质很差,有很明显的栅格化现象。如何生成高质量的GIF是一个需要探索的问题。

问题复现

对以下这个GIF(input.gif)

添加如下水印(logo.jpg):

使用FFmpeg命令如下:

ffmpeg -i input.gif  -vf "movie=logo.jpg,scale= 30: 30[watermark];[in][watermark] overlay=x=10:y=10[out] " -y out1.gif

生成下面这个GIF(out1.gif),可以看到画质很差,有很明显的栅格化现象。

这个现象不仅在加水印的过程中出现,只要是通过FFmpeg初级命令(如ffmpeg -i in.mp4 out.gif)对视频或者GIF生成GIF图时,均会出现这种栅格化现象。

问题思考

通过搜索看到两篇比较有启示的文章(见参考资料[4]和[6]),参考资料[4]主要描述的是GIF的存储和压缩原理,文章中关于调色盘的解释和gifsicle命令工具查看GIF元信息的部分对自己的启发比较多。



使用gifsicle工具对原GIF和生成的GIF进行分析:

原GIF:

* input.gif 26 images
  logical screen 400x400
  global color table [256]
  background 254
  loop forever
  + image #0 400x400
    disposal asis delay 0.08s
  + image #1 400x400 transparent 255
    local color table [256]
    disposal asis delay 0.08s
  + image #2 400x400 transparent 253
    local color table [256]
    disposal asis delay 0.08s
  + image #3 400x400 transparent 255
    local color table [256]
    disposal asis delay 0.08s
  + image #4 400x400 transparent 253
    local color table [256]
    disposal asis delay 0.08s
  + image #5 400x400 transparent 255
    local color table [256]
    disposal asis delay 0.08s
  + image #6 400x400 transparent 252
    local color table [256]
    disposal asis delay 0.08s
  + image #7 400x400 transparent 254

生成的GIF:

* out1.gif 26 images
  logical screen 400x400
  global color table [256]
  background 31
  loop forever
  + image #0 400x400
    disposal asis delay 0.08s
  + image #1 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #2 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #3 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #4 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #5 391x395 at 4,1 transparent 4
    disposal asis delay 0.08s
  + image #6 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #7 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #8 391x395 at 4,1 transparent 4
    disposal asis delay 0.08s
  + image #9 391x394 at 4,2 transparent 4
    disposal asis delay 0.08s
  + image #10 391x394 at 4,2 transparent 4

发现最大的区别就是local color table [256],也就是局部调色盘。

※ 注解
GIF是支持全局调色板和局部调色板同时存在的,如果GIF的某帧有自己的局部调色板,那么则使用该局部调色板进行颜色量化。如果没有局部调色板,则使用全局调色板进行颜色量化。

结合参考资料[6]中描述的相近现象及参考文章,大致了解了调色板对GIF的重要性。


进一步找到参考资料[5],里面对GIF生成的画质做了很详尽的讨论,也很详细地阐述了调色板的作用。

文中指出FFmpeg默认的调色板是以下256颜色的:



也就是说,在使用基础FFmpeg命令生成GIF时是调用上述的默认调色板(全局调色板,global color table [256])进行编码,但是上述调色板的颜色对于任意帧色彩的量化有很大的局限。

弄清楚造成栅格化现象的原因,思考出两种解决方案:

(1)全局调色板角度,根据每一个GIF的所有帧单独计算一个全局调色板。

(2)局部调色板角度,对于GIF的每一帧单独计算一个局部调色板。

在参考资料[3]中,有一个回复解决了开头的问题:


(1)使用新的全局调色板:

ffmpeg -i input.gif -vf "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" out.gif

(2)使用局部调色板:

ffmpeg -i input.gif -vf "split[s0][s1];[s0]palettegen=stats_mode=single[p];[s1][p]paletteuse=new=1" out.gif

下面是用上述命令新生成的GIF:

很好地解决了栅格化现象。

以下是对input.gif加水印的同时生成新的高质量GIF命令:

ffmpeg -i input.gif  -vf "movie=logo.jpg,scale= 30: 30[watermark];[in][watermark] overlay=x=10:y=10,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse[out] " -y out2.gif

不仅是对GIF,也可以延伸至视频加水印生成GIF的应用,大家自己多搜索多琢磨即可。

总结

GIF支持全局调色板和局部调色板同时存在的,如果GIF的某帧有自己的局部调色板,那么则使用该局部调色板进行颜色量化。如果没有局部调色板,则使用全局调色板进行颜色量化。

使用如下命令可以很好的使用FFmpeg生成高清gif图。

ffmpeg -i input.gif -vf "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" out.gif

FFmpeg是一个非常强大的音视频处理工具,如果遇到问题可以多看官方文档(参考资料[1])。使用搜索引擎辅助自己理解和解决问题。深入思考产生现象的原因,往往深入地理解对解决问题非常有益。

参考资料

[1] FFmpeg 官方文档: ffmpeg Documentation
[2] FFmpeg常用命令小结
[3] superuser问答社区:How do I convert a video to GIF using ffmpeg, with reasonable quality?
[4] medium文章:High Quality Gifs with FFMPEG
[5] blog.pkh.me博客 – High quality GIF with FFmpeg
[6] 浓缩的才是精华:浅析GIF格式图片的存储和压缩
[7] Gifsicle
tinygif

set p0=%1
#ffmpeg -i %p0%  -b:v 2048k -gifflags +transdiff -vf "split[s0][s1];[s0]palettegen=stats_mode=single[p];[s1][p]paletteuse=new=1" -y %p0%.gif -threads 8
ffmpeg -i %p0%  -b:v 2048k -gifflags +transdiff -vf "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -y %p0%.gif -threads 8

echo "转换完成"

tinymp4

set p0=%1
ffmpeg -i %p0% -b 1024k -y %p0%.mp4 -threads 8
echo "转换完成"

tinymp3

set p0=%1
ffmpeg -i %p0% -vn -y %p0%.mp3 -threads 8
echo "转换完成"

tinypng

set p0=%1
ffmpeg -i %p0% -y %p0%.png
echo "转换完成"

转换png pix8格式

set p0=%1
ffmpeg -hide_banner -i %p0% -pix_fmt pal8 -y %p0%.png
echo "转换完成"

将直播流转换为mp4

ffmpeg -i http://60.199.188.151/HLS/WG_ETTV-N/index.m3u8 -c:v copy -c:a copy -bsf:a aac_adtstoasc d:\cap.mp4

资料:https://www.freesion.com/article/39981384352/

发表评论

邮箱地址不会被公开。 必填项已用*标注