将视频转换为小体积 APNG
APNG
虽然受很多用户喜爱,但对某个组织来说却不受欢迎,所以到现在也没有一个能够将 APNG 压缩到很小的体识的流行库。
有压迫就有反抗,有开发者专门为APNG
优化了压缩算法,经我粗略测试,压缩的体积能够小 20 多倍。
如果直接用ffmpeg
转换视频成得到的 apng 文件,体识巨大,是远远超过很多网站的图片上传限制的,所以我们需要的是小体积 apng。
因为简书和思否会无脑压缩图片,压缩时不会识别动图,导致压缩后的 PNG 动图变成了一张静态,所以简书和思否用户请移步博客园查看未经压缩的示例动图,同时也由于简书会将外链图片上床到自己的服务器且有 10 Mb 限制,所以只能将一张高清动图压缩到了640x360
分辨率,但图中色彩丰富,每帧变化较多,排除掉分辨率的原因整体图像几乎无损,同样的视频不经apngasm
压缩,体积将会从现在的 7.4M 暴增到 20M:
1 分解视频
将视频分解为多张图片,方法有很多种,这里提供一个在线分解网站:Video to PNG image sequence converter (ezgif.com)。
将分解完成的压缩包下载解压待用。
2 合成为动图
这里用到的软件是shgodoroja/APNGb: macOS app which assembles and disassembles animated png files (github.com),虽然此库数年未更新,但其基于的 APNG 压缩库apngasm/apngasm: The next generation of apngasm, the APNG Assembler. (github.com)不会过时,依然好用。
下载后,打开软件,将第一步分解得到的所有图片扔进去:
调整主要参数:
- Loops 循环次数,0 为一直循环
- Compression 压缩算法,默认即可,有兴趣的可以尝试不同算法压缩
- All frames delay 每一帧的延迟,说白了就是每一帧的显示时间,或一秒多少帧。上图中为一秒 40 帧。
- 分解图片数量固定的情况下,帧率高意味着图片加速播放,帧率低,图片减速播放
- 帧率的高低不影响最终图片的体积大小
- Selected frame/s delay 选中帧的延迟,解释同上
双击左上角三大金刚键右侧的方块或三角即开始压缩图片,过程很漫长,请耐心等待。
3 示例结果
上例中,使用 153 张图片合成的动图,用ffmpeg
转换为apng
的体积和用上述软件转换的体积对比:
ffmpeg
并不会压缩图片,而是将多张图片粗暴地合并为一张动图,因此体积巨大。
由此可见压缩效果是很明显的,而且我目测 2.4 MB 虽然体积小,但没什么损失,图片质量极高。
因为 49.3 MB 体积巨大,没办法上传到文章中,所以我将两副图片上传到网盘中,有兴趣的可以下载下来对比:https://zijieyunpan.com/UNw5G5iMX4J
4 所有系统可用
可能不是所有系统都有可用的软件,但 APNG 压缩库apngasm/apngasm: The next generation of apngasm, the APNG Assembler. (github.com)却适用于所有平台。
Mac 版软件上面的示例中已提供下载链接。
Windows 版:APNG Assembler (sourceforge.net)。
Linux 版没有找到,但可以自行编绎 APNG 压缩库并安装,安装好后直接调用apngasm
命令即可压缩多张图片:
apngasm -o output.png 图片1.png 图片2.png 图片3.png 图片4.png 图片5.png ...
如果多张 png 文件名有规律且放到同一个文件夹内,此文件夹内有且只有这些图片,则可以直接输入下面的命令:
apngasm -o output.png 文件夹路径/*
Linux 安装可能遇到的问题
因为libapngasm.so
库默认安装在/usr/local/lib
中,导致在调用apngasm
命令时报错error while loading shared libraries: libapngasm.so
,此时需要将此文件软链接到/usr/lib
中即可:
sudo ln -s /usr/local/lib/libapngasm.so /usr/lib/libapngasm.so
4.1 直接使用apngasm
库
写了一个简单的脚本:
#!/bin/bash
# convert_to_apng.sh
set -e
input=${1} # 源视频文件
pngs_folder=${2} # 分解的图片存储目录
format=${3} # 图片的文件名格式
fps=${4} # 以多少帧率分解视频,如果是 10 则以每秒 10 帧的帧率分解视频
output=${5} # 生成的 png 动图名,不含 .png 后缀,只传入文件名
delay=${6:-100} # 每帧显示的时长,默认 100 ms,在调整 fps 后需要手动调整 delay 来对动图加速或减速播放
scale=${7:-""} # 压缩分辨率,在源视频分辨率太高时使用
# 分解视频为图片
if [ ! -d ${pngs_folder} ];then
mkdir ${pngs_folder}
else
rm -f pngs/*
fi
echo "正在分解视频"
if [ ${scale} ]; then
echo "先压缩分辨率,再分解视频"
ffmpeg -i ${input} -vf "fps=${fps},scale=${scale}" ${pngs_folder}/${format}.png
else
ffmpeg -i ${input} -vf fps=${fps} ${pngs_folder}/${format}.png
fi
echo -e "视频已解压完成,所有图片保在 ${pngs_folder} 目录中"
echo "开始压缩图片"
apngasm -o ${output}.png ${pngs_folder}/* -F -d ${delay} # -F 参数会覆盖与 output 同名的文件
echo "apng 已制作完成"
将源视频文件input.mov
分解为图片,这些图片保存到pngs
目录中,图片文件名的格式是%03d
,每 5 帧保存一张图片,最后的动图文件名为output.png
,动图每帧显示时长100ms
,视频等比例压缩为高为360
的视频:
./convert_to_apng.sh input.mov pngs %03d 5 output 100 -2:360
-
文件名格式:
%04d
是四位自增的数字,不足四位时向前补0
- 如
0001.png
、0022.png
、0111.png
、1234.png
- 分解得到的图片一般不可能超过 3 位数,不然动图体积可能超过 1 G,完全没有必要,所以格式推荐使用
%03d
- 除自增数字外,其前后还可以加一些自定义字符串,如
test_%03d
- 如
-
压缩分辨率:参考
ffmpeg
的scale
参数- 建议保持比例缩放
- 上例中指定高为 360 的等比例缩放,你也可以指定宽为 720 进行等比例缩放
720:-2
- 但不能宽和高都使用 -2
- 你也可以同时指定宽和高
720:480
,这样的话压缩后的视频将不是等比例
-
delay:一般不用修改此参数,除非你对
fps
进行了非常夸张的修改,导致分解的图片非常少或非常多才需要调整。- 图片数量非常少时,需要调大此参数,延长每张图片的显示时长
- 图片数量非常多时,需要调小此参数,缩短每张图片的显示时长
fps
在\([5, 10]\)范围内不需要设置delay
,使用默认的 100ms 即可
delay
和scale
都是可选参数,正常情况下一般不用这两个参数:
./convert_to_apng.sh input.mov pngs %03d 5 output
这两个参数虽然可选,但因为都是位置参数,所以在传入
scale
时必须先传入delay
(可传入默认值 100),传入delay
时可不传入scale
。
结束,希望各位读者能愉快使用。