最近一直很无聊的在用AI工具做 文生图生视频 动画。线上用的豆包文生图,即梦首尾帧图生视频。豆包也有用图生视频,把首帧图倒放,然后接到即梦视频的前面。
因为豆包和即梦的视频输出是诡异的 1248x704p24fps,704甚至不能被9整除,1248÷16×9=702(根据网上搜到的结果说是为了匹配patch所以要能被32整除)。所以我在线下用了 realesrgan-ncnn-vulkan 和 rife-ncnn-vulkan 把输出放大到 1408p ,把帧率补到 60fps,然后再用 FFMPEG 重新编码到 1080p60fps。
(可灵虽然直出1920x1080p24fps,但是一个月就166个点数,非会员生成视频还要等几个小时,而且不还能多个生成并发排队,有时候生成的结果还特别像幻灯片然后补帧到的24fps。屁用没有)
这样就有一个疑问了:
是先补帧?还是先放大?
交换律?
首先第一刻板印象当然是想到了交换律,即最终结果都是1408p60fps,所以顺序并不重要。
但仔细一想,插帧是一种算法实现,缩放是另一种算法实现,这两种算法除了都是从卷积派生出来的之外,基本没啥数学关系,甚至先补帧后缩放和先缩放后补帧的1408p60fps输出结果都不一样。
所以这玩意不符合交换律。
而我其实并不太关心最终结果的质量。这俩工具目前的使用场景都是大玩具,实际生产环境也是作为玩具存在的。
我更在意的是在有限性能下,哪个前哪个后的总耗时更短,速度更快。
当然这俩玩意的算法我是没研究过,即使研究了,其在实际场景下还有多核和多线程调用的差异,在不同硬件和不同驱动下也肯定没准。
还不如在自己机器上实际跑一遍测速。
测试
所有测试中用到的视频,我上传到了B站。因为B站有二压的特性,所以所有视频素材整合到了同一个60fps的视频中。同时因为B站的限制,非会员只能观看30fps的视频,补帧效果可能看不出来。
每个测试用例的首帧图我会放到文章中。
测试用例1:AI生成的简单动画
首先准备测试用例。
我是不知道输入源的哪个因素对两个工具的性能影响最大,所以准备了两个实际素材。
一个素材偏重于更静态的图像,另一个素材则更偏重于运动场景。两个素材均是分辨率 704p 的PNG图像,共 49 帧。
当然我没有用极端测试场景(比如H.264等图形算法最常用的雪花图像,这玩意怎么缩放和补帧?),真实素材也更符合实际日常使用的结果。
输出目标均为:1408p 图像(704p的2倍),121 帧(由24fps插帧到60fps)
AI引擎和模型使用:
- 放大引擎:realesrgan-ncnn-vulkan-20220424-ubuntu,模型:realesr-animevideov3
- 插帧引擎:TNTwise-rife-ncnn-vulkan-20240102-frame-count-patch,模型:rife-v4.10_ensembleTrue
补帧和放大均使用目录路径作为输入参数,以排除文件系统调用和模型重复预热引入的性能限制。两个工具也均支持GPU加速所以没有单线程限制(反过来也就是说CPU和GPU在性能统计上会变得乱七八糟而没有参考价值)
存储则使用 ramdisk ,以减少硬盘读写性能的影响。
结果:
偏重静态的图像:

$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 0m15.478s
user 0m30.060s
sys 0m0.874s
这里,如果你没有使用 Linux time 命令的经验的话,我可以简单解释一下:
- real指的是实际用时,即真实世界时间,和你用秒表测量的数值是一样的
- user指的是用户态的CPU时间
- sys指的是内核态CPU时间
- 在单核单线程硬件下,real=user+sys,但是在多核多线程场景下,每个核的CPU时间都是独立的,所以这个时间统计在现在这个场景下基本没意义。
(解释并不精确,如果你想深入学习,建议看更详细的资料)
所以在目前这个场景下,我们只关注real这个真实耗时就足够了。
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output
计算 (49 - 1) * 60 / 24 + 1
源帧数: 49
目标帧数: 121
real 0m35.632s
user 1m27.617s
sys 0m1.462s
放大约15秒,补帧约35秒,总计约50秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
计算 (49 - 1) * 60 / 24 + 1
源帧数: 49
目标帧数: 121
real 0m7.843s
user 0m15.536s
sys 0m0.707s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 0m36.348s
user 1m14.572s
sys 0m1.513s
补帧约8秒,放大约36秒,总计约44秒。
结果是 先补帧后放大 优于 先放大后补帧 。
偏重运动的图像:

$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 0m16.676s
user 0m30.713s
sys 0m1.277s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output
real 0m36.721s
user 1m33.545s
sys 0m1.513s
放大约16秒,补帧约36秒,总计约52秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m9.753s
user 0m20.995s
sys 0m0.850s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-out/ -v
real 0m37.459s
user 1m16.351s
sys 0m2.180s
补帧约10秒,放大约38秒,总计约48秒。
结论是:
- 高动态的图像的确为插帧带来了更多压力
- 先补帧后放大的总耗时 小于 先放大后补帧的总耗时
很奇妙的是不论先后顺序,第二步的耗时都差不多。
测试用例2:AI生成的长动画
实际只测试两秒钟(49帧-121帧),打算再测一个输入时长更长的,看看能不能把性能差距拉得更开。
同样是两组,一组偏静态,另一组偏动态。
这回输入均为10秒钟24fps,241帧。目标仍是 60fps,601帧。
(注:这所谓的10秒其实是两个5秒钟视频贴在一起的,第一个视频使用即梦生成,首尾帧相同。第二个视频使用豆包生成,根据关键字调整了动作幅度)
结果:
偏重静态的图像:

$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 1m9.580s
user 2m21.161s
sys 0m3.010s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output/
计算 (241 - 1) * 60 / 24 + 1
源帧数: 241
目标帧数: 601
real 2m45.014s
user 7m9.693s
sys 0m3.845s
放大约70秒,补帧约165秒,总计约235秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m33.821s
user 1m11.185s
sys 0m1.258s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 2m52.675s
user 5m57.794s
sys 0m8.370s
补帧约34秒,放大约173秒,总计约207秒。
偏重动态的图像:

$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 1m10.573s
user 2m24.758s
sys 0m2.371s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output/
real 2m49.481s
user 7m27.740s
sys 0m4.279s
放大约70秒,补帧约170秒,总计约240秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m39.217s
user 1m32.454s
sys 0m1.594s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 3m0.312s
user 6m19.679s
sys 0m5.674s
补帧约40秒,放大约180秒,总计约220秒。
结论是:
测试用例3:AI生成的现实场景视频
这里得偷懒了。缩放工具 realesrgan 本身的默认模型 realesrgan-x4plus 对现实场景的缩放效果更好,但是其仅支持4的整数倍缩放,在现在这个场景下比较浪费。
同样 rife 这边也有更适合现实场景的补帧模型,但我也打算偷懒。
所以模型将仍然使用 realesr-animevideov3 和 rife-v4.10_ensembleTrue 。
视频输入样本,偏静态样本仍为即梦5秒+豆包5秒,但偏动态样本这回使用即梦生成的10秒,因为偏动态的内容,现实场景首尾帧相同实在太诡异了,而且即梦和豆包生成奔跑内容的视频效果非常差,经常就变成单腿跳。
结果:
偏重静态的图像:

time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 1m9.756s
user 2m22.813s
sys 0m2.415s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output/
real 2m49.322s
user 7m21.308s
sys 0m3.992s
放大约70秒,补帧约170秒,总计约240秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m37.079s
user 1m27.702s
sys 0m1.498s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 2m51.899s
user 6m0.976s
sys 0m4.820s
补帧约37秒,放大约171秒,总计约208秒。
偏重动态的图像:

$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 1m10.753s
user 2m24.943s
sys 0m2.365s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output/
real 2m49.142s
user 7m16.057s
sys 0m3.969s
放大约70秒,补帧约170秒,总计约240秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m38.306s
user 1m29.791s
sys 0m1.516s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 2m49.268s
user 5m55.462s
sys 0m4.760s
补帧约39秒,放大约170秒,总计约209秒。
结论是:
测试用例4:真实现实场景视频
这个源不太好找,现在手里没有24fps的摄像机这玩意,目前常见的摄像设备都是30fps,60fps,120fps,240fps,960fps的。
所以这里将测试用例调整一下。
首先,源视频是拍摄的一段1080p60fps的视频,再缩小成704×1252,再把1252剪成1248。
然后分为两个策略:
- 去掉所有偶数帧,这样源就变成30fps了。虽然当然也可以直接拍30fps的视频,但有个补帧缩放后有个对比也算不错。
- 使用比较主流的减帧策略(丢弃每5帧中的第2、3、5帧),将60fps视频减至24fps。
(当然像OpenCamera这种App也支持拍摄24fps视频,但是场景过于小众了)
这样做的目的是:rife 的非整数倍补帧, 24补到60是2.5倍,只有奇数帧会被保留,偶数帧在算法里被用掉之后就被丢弃了。
相关日志:
/tmp/ramdisk/rife-tmp//0-000001.png /tmp/ramdisk/rife-tmp//0-000002.png 0.000000 -> /tmp/ramdisk/rife-output//00000001.png done
/tmp/ramdisk/rife-tmp//0-000001.png /tmp/ramdisk/rife-tmp//0-000002.png 0.400000 -> /tmp/ramdisk/rife-output//00000002.png done
/tmp/ramdisk/rife-tmp//0-000001.png /tmp/ramdisk/rife-tmp//0-000002.png 0.800000 -> /tmp/ramdisk/rife-output//00000003.png done
/tmp/ramdisk/rife-tmp//0-000002.png /tmp/ramdisk/rife-tmp//0-000003.png 0.200000 -> /tmp/ramdisk/rife-output//00000004.png done
/tmp/ramdisk/rife-tmp//0-000003.png /tmp/ramdisk/rife-tmp//0-000004.png 0.000000 -> /tmp/ramdisk/rife-output//00000006.png done
/tmp/ramdisk/rife-tmp//0-000002.png /tmp/ramdisk/rife-tmp//0-000003.png 0.600000 -> /tmp/ramdisk/rife-output//00000005.png done
/tmp/ramdisk/rife-tmp//0-000003.png /tmp/ramdisk/rife-tmp//0-000004.png 0.400000 -> /tmp/ramdisk/rife-output//00000007.png done
/tmp/ramdisk/rife-tmp//0-000003.png /tmp/ramdisk/rife-tmp//0-000004.png 0.800000 -> /tmp/ramdisk/rife-output//00000008.png done
/tmp/ramdisk/rife-tmp//0-000004.png /tmp/ramdisk/rife-tmp//0-000005.png 0.200000 -> /tmp/ramdisk/rife-output//00000009.png done
/tmp/ramdisk/rife-tmp//0-000005.png /tmp/ramdisk/rife-tmp//0-000006.png 0.000000 -> /tmp/ramdisk/rife-output//00000011.png done
/tmp/ramdisk/rife-tmp//0-000004.png /tmp/ramdisk/rife-tmp//0-000005.png 0.600000 -> /tmp/ramdisk/rife-output//00000010.png done
/tmp/ramdisk/rife-tmp//0-000005.png /tmp/ramdisk/rife-tmp//0-000006.png 0.400000 -> /tmp/ramdisk/rife-output//00000012.png done
所以这次测试用例是:
- (类)原生30帧补到60帧
- (由2:3策略减帧的)24帧补到60帧
模型也同样使用 realesr-animevideov3 和 rife-v4.10_ensembleTrue 。
偏静态与偏动态不做区分了,本身真实场景,除非是使用三脚架固定相机拍摄,否则也没啥偏静态的场景,大多数都是动态且镜头抖动巨大,个人拍摄的内容还有严重的低光照问题。
(说白了还不是因为根本没有可用的视频素材)
结果:

30帧补到60帧
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 1m24.551s
user 2m57.076s
sys 0m2.912s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output/
计算 (301 - 1) * 60 / 30 + 1
源帧数: 301
目标帧数: 601
real 2m43.455s
user 7m6.708s
sys 0m3.595s
放大约85秒,补帧约164秒,总计约249秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m43.132s
user 1m52.735s
sys 0m1.387s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 2m48.691s
user 5m56.668s
sys 0m5.217s
补帧约43秒,放大约169秒,总计约209秒。
24帧补到60帧
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-input/ -o /tmp/ramdisk/rife-tmp/ -v
real 1m7.531s
user 2m21.199s
sys 0m2.532s
$ time ./2_rife.sh /tmp/ramdisk/rife-tmp/ /tmp/ramdisk/rife-output/
计算 (241 - 1) * 60 / 24 + 1
源帧数: 241
目标帧数: 601
real 2m45.471s
user 7m11.694s
sys 0m4.140s
放大约68秒,补帧约165秒,总计约233秒。
$ time ./2_rife.sh /tmp/ramdisk/rife-input/ /tmp/ramdisk/rife-tmp/
real 0m42.656s
user 1m52.408s
sys 0m1.312s
$ time /mystorage2/tools/realesrgan-ncnn-vulkan-20220424-ubuntu/realesrgan-ncnn-vulkan -n realesr-animevideov3 -f png -s 2 -i /tmp/ramdisk/rife-tmp/ -o /tmp/ramdisk/rife-output/ -v
real 2m46.998s
user 5m53.404s
sys 0m5.189s
补帧约42秒,放大约167秒,总计约209秒。
结论是:
测试总结
- 所有测试用例的场景下,先补帧后放大的总耗时 总是小于 先放大后补帧的总耗时
相关视频
【AI工具研究之:先放大还是先补帧】 https://www.bilibili.com/video/BV1yoCtBFE1L/?share_source=copy_web&vd_source=bc6d7e4cd2c1f2bba38d19773d2bc1fc
结尾
这几组测试用例其实都不严谨,没有参考官方的建议在不同场景下使用不同模型,而且因为这是我的个人电脑,里面跑的乱七八糟东西特别多,CPU和GPU在跑用例的时候偶尔也会被其他应用调用,所以每次跑的时候精度都一般,误差很大。我本来也只是跑着玩的,根本没多次测试然后取平均值。好孩子不要学。
本次测试结果仅代表两个工具以及对应模型在本人主机硬件环境上的性能测试结果,不对其在其他场景下的性能负责。请勿将本文中的结论用于生产环境。
The post
先补帧还是先放大 first appeared on
石樱灯笼博客.