看视频时有两个很自然的愿望:老片源分辨率低,希望它清晰一点;24 帧的电影在 120Hz 屏幕上,希望它顺滑一点。这两个愿望各有名字:
- 超分(Super-Resolution):把低分辨率画面放大,并尽量补出原本不存在的细节;
- 补帧(插帧):在相邻两帧之间算出“中间那一帧“,24fps 变 48fps,运动画面明显更顺。
手机上有现成的开源技术能做这两件事吗?能做到“实时“(一边播一边算,不卡不掉帧)吗?我花了一天时间,在一台骁龙 8 Gen 3 手机上把三条技术路线全部真机跑了一遍,拿到了一手性能数据。先说结论:
- 超分容易,补帧难。超分用 GPU“滤镜“就能满帧率跑,用 NPU(手机里的 AI 专用芯片)更是连 1080p 都实时;而 AI 补帧非常吃算力,纯 GPU 方案只在 320p 这样的小分辨率下能实时。
- 三条路线最后可以拼成一条完整的实时管线:低分辨率补帧 + 高分辨率超分 + 120Hz 输出,每一步都有实测数字支撑。
下面是完整过程,按“从简单到硬核“的顺序讲。
测试平台
| 项目 | 详情 |
|---|---|
| 手机 | vivo V2403A(零售正式版,没有 root) |
| 芯片 | 骁龙 8 Gen 3(SM8650) |
| GPU | Adreno 750——管“画面计算“,着色器和 Vulkan 计算都跑在这 |
| NPU | Hexagon V75——管“AI 推理“,手机相机的美颜夜景就用它 |
| 屏幕 | 1260×2800 @120Hz,天生适合呈现插帧后的高帧率画面 |
| 系统 | Android 16 |
顺带发现一个有趣的细节:GPU 驱动里暴露了两个叫 GL_QCOM_motion_estimation(运动估计)和 GL_QCOM_frame_extrapolation(帧外推)的扩展——这是芯片厂商在硬件层面预留的补帧相关能力,这次没来得及深入,先记下来。
三条路线总览
| 路线 | 干什么 | 原理(一句话版) | 上手难度 |
|---|---|---|---|
| A. Anime4K 着色器 | 超分 | 给 GPU 挂一串“智能滤镜“,放大同时修边缘 | ★ 装个播放器就行 |
| B. RIFE(ncnn Vulkan) | 补帧 | AI 模型看着前后两帧,算出中间帧 | ★★ 要自己编译 |
| C. QNN / NPU | 超分(也能补帧) | 把 AI 模型塞进手机的 AI 专用芯片 | ★★★ 要转换模型 |
这三条不是互斥的选择题,而是可以组装的零件——文章最后会讲怎么拼。
路线 A:Anime4K 着色器超分 —— ✅ 最简单,效果立竿见影
Anime4K(GitHub ★21k,MIT 许可)是一组专门为动漫画面设计的 GPU 着色器。着色器可以理解成“跑在显卡上的滤镜“:每帧画面经过它时顺手做放大和边缘修复,单帧开销不到 3 毫秒,对帧率毫无影响。
最省事的用法是装一个 mpv-android 播放器(MIT 许可的开源播放器),它原生支持加载用户着色器。把 Anime4K 的着色器链挂进去(高光钳制 → 去噪 → 细节恢复 → 2 倍放大),播放动漫片源立刻能看到区别。
这条路上唯一的坑有点出乎意料:mpv-android 的正式版里,通过设置界面编辑保存的 mpv.conf 配置,播放器核心实际上不读——我用“同一静态画面开关着色器各截一帧“的方法验证过,两次截图 0 像素差异,等于白配。换成官方 debug 版安装包后,可以用 run-as 命令借用 App 自己的身份把配置文件直接写进它的私有目录:
adb shell "cat /sdcard/mpv.conf | run-as is.xyz.mpv sh -c 'cat > files/mpv.conf'"
这次立即生效。用同样的静态帧对比法验证:28% 的像素发生了变化(MSE=712,PSNR=19.60dB——这个指标在这里用来证明“画面确实被重新处理了“,而非同一帧)。肉眼对比:没挂着色器的画面,色块边缘是锯齿状的阶梯、小块发糊;挂了 Anime4K 后边缘平滑连续、细节更锐利。
小结:超分这一项,着色器方案零成本、满帧率,可以全程开着。两个局限:它只为动漫内容设计,真人实拍视频用了只是“锐化放大“,不会真的补细节;另外 mpv 播放器自带的“插值“选项只是显示层面的帧混合,不是 AI 补帧——顺滑的事得靠路线 B。
路线 B:RIFE 神经网络补帧 —— ✅ 能跑,但要先知道边界
补帧选的是 RIFE(GitHub ★11k,MIT 许可),目前公认的实时插帧算法里最好用的一个:输入前后两帧,AI 算出中间帧。手机上跑 AI 模型的主流框架是 ncnn + Vulkan(腾讯开源的移动端推理框架,走 GPU 计算)。
现成的 App 都不靠谱
先试了社区里现成的方案,两个都失败:
- 某个 RIFE 安卓 App 装上就闪退。查日志发现它打包的 native 库里少注册了一个叫
rife.Warp的图层,模型一加载就崩——是 App 本身的 bug,不是手机不行。 - 它内置的模型下载在手机上卡在 0%(模型托管在 GitHub,手机直连下不动;在电脑上下好再推进手机即可绕过)。
自己编译一个
既然现成的不争气,就直接拿官方实现 nihui/rife-ncnn-vulkan 的源码,用 Android NDK 交叉编译一个手机版:
cmake -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=<NDK路径>/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-24 # 24 以上才有 Vulkan
编出来一个 54MB 的独立可执行文件(自带全部依赖,也包含上面那个 App 缺的 Warp 层)。把它和 RIFE 模型文件(约 10MB)推进手机的 /data/local/tmp,直接在命令行里跑——不需要做成 App,也不需要界面:
./rife-ncnn-vulkan -m rife-v4.6 -0 前一帧.png -1 后一帧.png -o 中间帧.png
先验证算得对不对:合成出来的中间帧和两个源帧都不相同(对两者的 PSNR 都在 19~21dB),说明它既没偷懒复制前一帧也没复制后一帧,是真心算出来的新画面。✅
关键数据:GPU 补帧的实时边界
补帧要“实时“,每帧的处理时间必须小于播放间隔。24fps 补到 48fps,每帧预算是 20.8 毫秒。实测(Adreno 750,Vulkan,fp16 半精度):
| 源分辨率 | 算一帧耗时 | 相当于每秒处理 | 能实时补帧吗 |
|---|---|---|---|
| 320×180 | ~8.5ms | 117fps | ✅ 非常轻松 |
| 854×480 | ~80ms | 12.5fps | ❌ 差 4 倍 |
| 1280×720 | ~164ms | 6.1fps | ❌ 差 8 倍 |
| 1920×1080 | ~364ms | 2.7fps | ❌ 差 18 倍 |
| 3840×2160 (4K) | ~1.79s | 0.56fps | ❌ 差 86 倍 |
规律很干净:耗时基本和像素数量成正比(每微秒处理约 5.5 个像素)。结论也由此清晰——这颗 GPU 上 RIFE 的实时边界大约在 320p,480p 起步就超预算了。想在更高分辨率补帧,得换更轻的模型,或者请出下一条路线的 NPU。
路线 C:把 AI 模型塞进 NPU —— ✅ 打通,比 CPU 快 4 倍
手机里除了 CPU 和 GPU,还有一块专门跑 AI 的芯片(高通叫 Hexagon NPU/HTP)。它的特点是又快又省电,手机自带的相机夜景、人像算法就跑在上面。要把自己的模型放上去,需要用高通的 QAIRT SDK 做模型转换——这一步工具链比较繁琐,但一旦打通,收益巨大。
整个流程走通后回头看,其实是五步:
- 用 ONNX 搭一个小超分模型:三层卷积 + 像素重排,输入 64×64 输出 128×128(全部选用 NPU 喜欢的算子);
- 用 SDK 的
qnn-onnx-converter转换:顺便做 8-bit 量化——把模型里的数字从浮点压成整数,体积和耗时都大减; - 把转换产物编成手机能加载的模型库
.so(按官方 Android.mk 流程手工组装,NDK 交叉编译); - 把运行库和模型推进手机
/data/local/tmp; - 用 SDK 自带的
qnn-net-run跑推理——输出Finished Executing Graphs, rc=0,成功 ✅。
过程中的三个坑值得记下来:
- Python 版本:SDK 转换器的某个组件只认 Python 3.10,3.12 下直接报错——版本卡住,换版本就好。
- 缺文件:手工链接模型库时会缺两个官方工具源文件和权重符号对齐,照着官方编译脚本补齐即可。
- 一个惊喜:按文档,加载进 NPU 的模块需要厂商签名,但这台零售手机对未签名模块放行,第三方模型直接跑进了 NPU——也说明这条路的门槛纯粹在软件工具链,不需要 root、不需要破解。
NPU 有多快
| 指标 | 数值 |
|---|---|
| 64×64→128×128 超分,单次推理 | 1.28 毫秒 |
| 硬件层面的证据 | 报告显示 Number of HVX threads used: 4——NPU 的向量核 4 线程全开,是真·NPU 执行而非模拟 |
| 同一个模型跑在 CPU 上 | 6.5 毫秒 → NPU 快约 4 倍(还更省电) |
再把输入分辨率逐档加大,测出超分的完整效率表:
| 输入 → 输出 | NPU 执行耗时 | 等效帧率 | 够实时吗 |
|---|---|---|---|
| 854×480 → 1708×960 | 3.9ms | 190fps | ✅ 绰绰有余 |
| 1280×720 → 2560×1440 | 9.3ms | 71fps | ✅ 轻松 |
| 1920×1080 → 3840×2160 | 24.8ms | 31fps | ✅ 刚好覆盖 24/30fps 源 |
| 3840×2160 → 7680×4320 | 89.7ms | 11fps | ❌ 4K 超分不现实 |
耗时约每像素 10 纳秒,同样是线性规律。要说明的是这是个 4 层的演示模型;换成真正的画质级模型(参数大约多 5~10 倍),耗时乘 3~6 倍——720p 依然实时,1080p 接近实时,依然可用。
把两张表放在一起看
对着这块 1260×2800 的屏幕算总账,得到三个有点反直觉的结论:
- 4K 片源根本不需要超分。4K(830 万像素)已经比屏幕(约 350 万像素)大了,直接缩小显示就好。4K 源想补帧也不现实(0.56fps),正确吃法是先缩到 1080p 再补帧、再放大。
- 瓶颈永远是补帧,不是超分。超分这边,着色器方案恒实时、NPU 方案连 1080p 都实时;而补帧在 GPU 上必须压到 320p 的小画面上才来得及算。
- GPU 和 NPU 可以分工。补帧和超分分别放在不同的芯片单元上跑,互不抢资源,还能并行。
于是最终的推荐管线长这样(每输出一帧约花 12~20 毫秒,在 48fps 的 20.8ms 预算之内):
flowchart LR
D["解码<br/>(播放器硬解)"] --> RIFE["RIFE 补帧<br/>在 320p 小画面上 ~8.5ms<br/>(GPU)"]
RIFE --> SR["超分放大<br/>NPU 4~9ms<br/>或 Anime4K 着色器"]
SR --> OUT["放大铺满屏幕<br/>1260x2800 @120Hz"]
思路就是“让重的活在小画面上干“:补帧最吃算力,就先把画面缩到 320p 再补;超分便宜,就在接近屏幕大小的画面上做。真做成播放器时还需要两个保险:双缓冲避免解码和计算互相等待;以及降级策略——万一算不过来,就自动改成隔帧补、或干脆只留超分。另外 GPU/NPU 持续满载会发热耗电,补帧功能最好做成手动开关。
三条路线总结
| 路线 | 实测结果 | 实时性 | 适合内容 | 成本 |
|---|---|---|---|---|
| A. Anime4K 着色器 | ✅ 生效(28% 像素差异) | ✅ 满帧率 | 动漫最佳 | 最低,装播放器即用 |
| B. RIFE 补帧 | ✅ 合成帧正确 | ≤320p ✅ / 720p ❌ | 所有内容 | 中(要自己编译) |
| C. QNN NPU | ✅ 全链路跑通,4 倍于 CPU | 超分 1080p 实时 | 所有内容 | 中(要转换模型) |
给想动手的读者的建议顺序:
- 先装 mpv-android 挂 Anime4K,当天就能看到动漫超分的效果,成本几乎为零;
- 想玩补帧,用 NDK 编一个 rife-ncnn-vulkan,先在 320p 小画面上感受“24 变 48“;
- 想追求 720p 以上的全实时,再去研究 QNN 模型转换——这是三条路里文档最少、但天花板最高的一条。
开源社区已经把零件都备齐了(而且基本都是 MIT/BSD 许可,可自由使用;唯一要避开的是 AGPL 许可的 video2x,只能参考不能集成)。一台不 root 的普通手机,也能搭出一条每一环都有实测数据支撑的实时超分补帧管线——剩下的就是动手了。