第一遍是对整个图片计算一个调色板,这就是新的滤波器参与进来的地方。这个滤波器对每一帧的所有颜色制作一个直方图,并且基于这些生成一个调色板。
在技术层面上还存在一些琐事:这个滤波器实现了Paul Heckbert的这篇Color Image Quantization for Frame Buffer
Display (1982)论文中的算法的一个变种。这里是我记得的一些不同之处(或者说是关于论文中未定义的行为的特异性):
它使用一个全解析度的色彩直方图。而不是论文中作为关键建议使用的下采样 RGB 5:5:5 直方图,这个滤波器对1600万种可能的 RGB 8:8:8 色彩使用了一个哈希表。
对方格的分割任然是在中点上进行的,对要分割的方格的选择是根据方格中的颜色方差来进行的(一个带有大的色彩方差的方格将会优先截掉)。
对方格中的颜色求平均值取决于颜色的重要性,就我而言,这在论文中并没有定义。
当沿着一个维度(红,绿或蓝)进行分割方格时,假如相等,绿色是优先于红色的,然后再是蓝色。
所以不管怎样,这个滤波器都是在做色彩量化,并且生成一个调色板(通常保存在一个PNG文件里)。
它通常看起来像这个样子(upscaled):
颜色映射与抖动第二遍(验码)是通过滤波器完成的,就跟它的名字一样,它将会使用这个调色板来生成最终的量化颜色流,它的任务是在生成的调色板中找出最合适的颜色来表示输入的颜色。这也是你可以选择使用哪种抖动方法的地方。
这里同样有一些技术侧面上的小问题:
使用这两个滤波器可以让你将GIF编码成这样(单全局调色板,无抖动):
用法使用相同参量手动运行两遍(验码)是有点讨厌,还要对每一遍的参数进行调整。所以我推荐写一个简单的脚本,如下:
#!/bin/sh palette="/tmp/palette.png" filters="fps=15,scale=320:-1:flags=lanczos" ffmpeg -v warning -i $1 -vf "$filters,palettegen" -y $palette ffmpeg -v warning -i $1 -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y $2…可以这样使用:
% ./gifenc.sh video.mkv anim.giffilters变量包括:
一个帧率的调整(减小到15会使画面看起来不平稳,但可以使最终的GIF体积更小)
一个取代默认(目前是bilinear)定标器的lanczos定标器缩放比例。推荐它的原因是你使用lanczos或bicubic来缩放画面要比bilinear优越的多。如果你不这样做的话,你的输入会模糊的多。
提取:仅作示例不见得你会编码一部完整的影片,所以你可能会对使用-ss和-t选项来选择一个片段感兴趣。如果你真是这样,那么就要确保将它作为输入选项加入(在-i选项之前),例如:
#!/bin/sh start_time=12:23 duration=35 palette="/tmp/palette.png" filters="fps=15,scale=320:-1:flags=lanczos" ffmpeg -v warning -ss $start_time -t $duration -i $1 -vf "$filters,palettegen" -y $palette ffmpeg -v warning -ss $start_time -t $duration -i $1 -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y $2如果不是,那么至少在第一遍它就会导致没有多个帧输出(调色板),所以不会做你想要的。
一个可供选择的是在流复制中预提取你想要编码的片段,看起来是这样:
% ffmpeg -ss 12:23 -t 35 -i full.mkv -c:v copy -map 0:v -y video.mkv如果流复制不够精确,你可以添加一个滤波器。例如:
filters="trim=start_frame=12:end_frame=431,fps=15,scale=320:-1:flags=lanczos" 获得最好的调色板输出Getting the best out of the palette generation现在我们可以开始看有趣的一部分了,在palettegen滤波器中,主要的和能让你感兴趣去尝试的大概就是stats_mode选项了。
这个选项的主要作用就是允许你指明在整部视频中你需要东西,或者只是移动的物体。如果你使用stats_mode=full(默认),所有的像素将会是颜色统计的一部分。如果你使用stats_mode=diff,只有与前一帧不同的像素会被计入。
注意:向一个滤波器中添加选项,需要这样做:filter=opt1=value1:opt2=value2
下面是一个例子来说明它是怎样影响最终的输出的: