将视频转换为小体积 APNG

时间:2024-02-29 22:44:56

APNG虽然受很多用户喜爱,但对某个组织来说却不受欢迎,所以到现在也没有一个能够将 APNG 压缩到很小的体识的流行库。

有压迫就有反抗,有开发者专门为APNG优化了压缩算法,经我粗略测试,压缩的体积能够小 20 多倍。

如果直接用ffmpeg转换视频成得到的 apng 文件,体识巨大,是远远超过很多网站的图片上传限制的,所以我们需要的是小体积 apng。

因为简书和思否会无脑压缩图片,压缩时不会识别动图,导致压缩后的 PNG 动图变成了一张静态,所以简书和思否用户请移步博客园查看未经压缩的示例动图,同时也由于简书会将外链图片上床到自己的服务器且有 10 Mb 限制,所以只能将一张高清动图压缩到了640x360分辨率,但图中色彩丰富,每帧变化较多,排除掉分辨率的原因整体图像几乎无损,同样的视频不经apngasm压缩,体积将会从现在的 7.4M 暴增到 20M:

output

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)不会过时,依然好用。

下载后,打开软件,将第一步分解得到的所有图片扔进去:

截屏2021-06-01 11.53.56

调整主要参数:

  • Loops 循环次数,0 为一直循环
  • Compression 压缩算法,默认即可,有兴趣的可以尝试不同算法压缩
  • All frames delay 每一帧的延迟,说白了就是每一帧的显示时间,或一秒多少帧。上图中为一秒 40 帧。
    • 分解图片数量固定的情况下,帧率高意味着图片加速播放,帧率低,图片减速播放
    • 帧率的高低不影响最终图片的体积大小
  • Selected frame/s delay 选中帧的延迟,解释同上

双击左上角三大金刚键右侧的方块或三角即开始压缩图片,过程很漫长,请耐心等待。

3 示例结果

上例中,使用 153 张图片合成的动图,用ffmpeg转换为apng的体积和用上述软件转换的体积对比:

image-20210601121537558

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.png0022.png0111.png1234.png
    • 分解得到的图片一般不可能超过 3 位数,不然动图体积可能超过 1 G,完全没有必要,所以格式推荐使用%03d
    • 除自增数字外,其前后还可以加一些自定义字符串,如test_%03d
  • 压缩分辨率:参考ffmpegscale参数

    • 建议保持比例缩放
    • 上例中指定高为 360 的等比例缩放,你也可以指定宽为 720 进行等比例缩放720:-2
    • 但不能宽和高都使用 -2
    • 你也可以同时指定宽和高720:480,这样的话压缩后的视频将不是等比例
  • delay:一般不用修改此参数,除非你对fps进行了非常夸张的修改,导致分解的图片非常少或非常多才需要调整。

    • 图片数量非常少时,需要调大此参数,延长每张图片的显示时长
    • 图片数量非常多时,需要调小此参数,缩短每张图片的显示时长
    • fps\([5, 10]\)范围内不需要设置delay,使用默认的 100ms 即可

delayscale都是可选参数,正常情况下一般不用这两个参数:

./convert_to_apng.sh input.mov pngs %03d 5 output

这两个参数虽然可选,但因为都是位置参数,所以在传入scale时必须先传入delay(可传入默认值 100),传入delay时可不传入scale


结束,希望各位读者能愉快使用。