ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

视频插帧技术实战:从25fps到100fps的慢动作平滑之道

视频插帧技术实战:从25fps到100fps的慢动作平滑之道 我最近接了个片子处理的活儿素材只有25fps导演却要做慢动作宣传片。把时间线放慢四倍之后画面里的运动在跳帧一辆车划过画面都像在放幻灯片。后来把素材送进超帧hyperframes流程里25fps插到100fps再在剪辑软件里做时间重映射那种丢帧感才彻底消失。先说明一下hyperframes这个词在不同语境里指向完全不同的东西。通信协议里它表示TDMA帧结构中的超高帧是复帧再往上的帧集合软件领域里你可能还会听到某个基于GraphQL的Web框架叫HyperFrame而在最近视频处理和AI行业的热搜词里它更常被用来指代通过运动估计与中间帧合成把一段视频的时间采样密度成倍提高的技术也就是我们常说的视频插帧、时域超分。我这一篇聊的就是后者超帧技术怎么在真实项目里落地原理是什么工具怎么选坑在哪里以及它和超分辨率、老视频修复这类空间增强任务该怎么配合。为什么这个话题值得单独写一篇因为太多人把精力全砸在空间分辨率上搬出各种超分模型把画面从720p提到4K却完全忽略时间分辨率这回事。结果就是画面确实清晰了但运动依然一顿一顿慢放起来像在看PPT。视频是三维信号空间维度和时间维度缺一不可只修空间不修时间等于只修了一半。1. 超帧到底在解决什么问题时间分辨率这个维度1.1 帧只是时间采样别把视频当成图片序列很多人下意识把视频理解成很多张图片连续播放这个理解不算错但它掩盖了一个关键事实帧率本质上是时间维度的采样率。视频可以用一个三维函数 f(x, y, t) 来表示x 和 y 是画面空间位置t 是时刻。24fps、25fps、30fps 这些参数其实就是在时间轴上每隔 1/24 秒、1/25 秒、1/30 秒采一次样。用拍照来类比会更容易理解。想象你用连拍模式拍一个跑步的人快门按得越快你得到的位置信息越密按得越慢两次记录之间人已经跑出去一大截你根本不知道中间轨迹是什么样的。视频的每一帧就是一次曝光采样帧率就是把时间轴切成多少段。帧率越低时间采样越稀疏运动的重建就越粗糙。那为什么低帧率素材放慢之后特别难受因为慢动作本质上是对时间的拉伸。你原本 25fps 的素材放慢四倍相当于每秒还是那 25 张画面但时间被拉成了四秒。时间轴上的采样点间距被撑大了四倍原本就稀疏的运动轨迹现在彻底断裂看起来就是一个姿势跳到下一个姿势没有中间过渡。这就是跳帧感的来源。理解了这一点你就会明白慢动作卡顿不是剪辑软件渲染的问题而是素材在时间维度上的信息本来就缺。暴力抽帧只会更卡重复帧只会更顿唯一的出路是补出信息也就是合成出那些物理采样时没有拍到的中间时刻。1.2 常规慢动作处理的三种局限抽帧、重复帧、动态模糊我先说说大多数人在没有超帧概念时是怎么对付慢动作的这样你才能体会到超帧的不可替代性。第一种是抽帧。把 25fps 素材直接按 1/4 速度放在 100fps 的时间线上播放器会每四帧里保留一帧其余全是空隙。结果就是画面一顿一顿运动完全不连续显得非常廉价。第二种是重复帧。把每一帧复制几份来填补空隙画面倒是不会跳了但物体运动像在蠕动有一种诡异的果冻感因为它本质上还是每秒 25 个不同画面只是每个画面多停留了几帧。第三种是给画面加动态模糊配合速度曲线让镜头看起来柔一点。这能缓解跳帧的视觉刺激但代价是牺牲清晰度而且一旦运动幅度稍大糊成一团导演那边基本过不了。这三种方法都是从怎么掩盖信息缺失出发而不是从怎么补全信息出发。超帧的思路正相反它通过分析相邻两帧里每个像素/物体从哪里运动到哪里预测出中间时刻应该长什么样然后把那一帧真正合成出来。这是采样密度层面的补全而不是显示层面的糊弄。1.3 超帧的典型需求场景我梳理一下自己实际做过的、以及行业内常见的超帧应用场景方便你对号入座慢动作视频创作。入门运动相机、无人机素材是 30fps 或 60fps想输出 120fps 甚至 240fps 的慢动作插帧是最直接的手段。老电影/老动画修复。旧片大多是 24fps 甚至更低修复到 50/60fps 在高帧率电视上回放需要补帧。体育与赛事回放。高速运动下镜头回放需要慢放而且慢放要顺滑职业转播里早已大量使用超帧设备。动画中间帧生成。部分动画项目把关键帧绘制交给人工中间帧交给插帧模型减少绘制量。高刷屏内容补足。手机和显示器普遍 120Hz但大量网络视频只有 24/25/30fps插帧后观看体验明显更顺滑。VR/AR 与监控视频。头显里的画面如果帧率不足眩晕感会非常强烈监控视频低帧率下做目标追踪超帧能降低帧间目标位移的跳跃。这些场景的共性是物理拍摄时没能获得足够的时间采样但业务需求又要求更高的时间密度。超帧就是为这类需求存在的。2. 两条插帧路线传统光流与深度学习模型的取舍2.1 传统光流插帧先猜运动再沿轨迹摆像素超帧并不是 AI 时代的发明。早在上世纪八九十年代视频编码里就有运动补偿的概念插帧的思路也一脉相承先估算运动再把像素沿着运动轨迹移动到中间位置。传统方法的具体流程大致是这样。取相邻的两帧 A 和 B先用光流算法估计出每个像素从 A 到 B 的运动向量也就是这一帧里这个点下一帧跑去了哪里。有了运动向量后在时间中点 t0.5 处把 A 的像素沿着向量方向挪一半距离把 B 的像素往回挪一半距离然后根据两者内容做加权融合得到中间帧。整个过程像你追着一个跑掉的东西在路径中间截住它。这个思路的经典实现普通用户接触最多的有两个一是 SmoothVideo ProjectSVP配合播放器做实时补帧二是 FFmpeg 自带的 minterpolate 滤镜可以离线处理视频。比如你可以在命令行这样跑ffmpeg -i input.mp4 -vf minterpolatefps60:mi_modemci:mc_modeaobmc:vsbmc1 output.mp4参数含义是目标帧率 60fps用运动补偿插值模式mci运动补偿方式用类似覆盖块的方式aobmc打开可变块运动补偿vsbmc。这条命令在快速出个效果、验证思路时非常实用。但传统光流插帧在实战里有几个很难绕开的硬伤。第一是遮挡问题。人物从一堵墙前面跑过墙在前后两帧都能看到但人物移动后挡住的那部分墙在 A 帧存在、在 B 帧被人物遮住中间帧里这部分墙到底是什么颜色没有信息全靠猜结果往往是模糊的色块或残影。第二是大位移运动物体一帧内移动了几十个像素光流估计的搜索窗口覆盖不到向量错误插帧就直接错了。第三是光照变化和低纹理区域墙壁、天空、素色衣服这种缺少纹理的地方每个点都长得差不多光流算法根本找不到可靠的对应点产生随机跳动。这些问题的本质是运动估计本身是不适定问题同一个颜色块可能有无数种运动解释传统方法缺乏足够强的先验去消歧。2.2 深度学习插帧把运动估计像素合成一起学出来深度学习插帧模型的思路是直接把相邻两帧输入中间帧输出当成一个端到端的学习任务。网络内部自己学会了什么东西需要估计光流、什么地方属于遮挡、应该怎么填补空洞而不是像传统方法那样全手工设计每个模块。RIFE 是这类方案里我主力使用的一个全称 Real-time Intermediate Flow Estimation。它用了一个叫 IFNet 的轻量级网络专做粗到细的光流估计配合知识蒸馏的训练策略把教师网络的知识逐步迁移到可以实时推理的学生网络上效果和速度都做得很均衡。相比像 DAIN 这种还额外估计深度图、显存占用夸张的老模型RIFE 甚至可以在消费级显卡上跑得飞快这正是它能从学术代码变成生产工具的关键。实际使用中深度学习模型的一个显著优势是遮挡处理。传统方法遇到遮挡区域基本放弃治疗而深度模型在训练阶段见过大量遮挡样例网络内部形成了一套隐式策略距离确定性高的像素参考光流遮挡区域的像素则更多依赖周围时空信息重建。虽然并不能做到完美无缺但比起传统方法的色块和残影已经是一个量级的提升。深度插帧当然也不是银弹。训练数据分布外的情况比如高速轴对称旋转、复杂非线性形变、某些罕见生物的运动方式网络会给出无法解释的结果。另外深度学习模型对输入素材的压缩噪声很敏感低码率的视频喂进去伪影会被明显放大这个我后面单独讲。2.3 主流方案横评RIFE、FILM、DAIN、SVP 与 FFmpeg我按实际体验给主流方案做个横向对比你选择时可以直接照着参考方案技术路线处理速度输出质量适用场景RIFE / 4KX深度学习粗到细光流很快可实时或近实时高遮挡处理较好批量处理、线上工具、中高质量项目FILMGoogle深度学习大规模光流较慢极高细节和运动边界好离线高质量出片对时间不敏感DAIN深度估计插帧慢显存高较高某些遮挡更好早期离线方案现在用得少了SVP传统光流块匹配实时中等复杂场景易糊Windows 播放器实时补帧FFmpeg minterpolate传统运动补偿快中低快速验证足够原型验证、简单自动化脚本选型建议很直白默认从 RIFE 入手因为它上手快、速度快、质量足够应付大多数项目如果你做的是单条精品视频、工艺要求极高且对渲染时间无感测试一下 FILM 值得要在播放器里实时补帧SVP 或显卡驱动自带的补帧功能更合适只是临时验个效果FFmpeg 命令一把梭。另外提醒一句作者团队后来做了 4KX 这个商业化版本集成了 RIFE 的核心能力并扩展了工程化支持处理流程更完善项目预算足够时也值得考虑。3. 用 RIFE 搭一条可上生产的插帧管线3.1 环境准备显卡、Python 与输入素材的规范我的主力环境是 Ubuntu 22.04 NVIDIA GPUPython 用 conda 管理。RIFE 依赖 PyTorch 和 CUDA强烈建议直接用官方仓库的安装说明大致步骤如下git clone https://github.com/hzwer/Practical-RIFE cd Practical-RIFE conda create -n rife python3.9 -y conda activate rife # 按你的 CUDA 版本安装对应 PyTorch这里以 cu121 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install -r requirements.txt预训练权重仓库里通常已经提供如果没有去 release 页面下载后放到 train_log 目录。硬件方面我发现 1080p 视频做 2 倍插帧8G 显存完全够用要做到 4K 分辨率的 4 倍帧率最好有 12G 以上显存。如果显存紧张可以启用 CPU offload 或分批处理片段但速度会明显下降。表格式总结如下输入分辨率目标倍数经验显存需求建议 GPU 型号1080p2 倍4GB 左右GTX 1660 Super 起步1080p4 倍8GB 左右RTX 3060/40604K2 倍8-12GBRTX 3080/4070 及以上4K4 倍16GB 左右RTX 4090 或专业卡提示以上是经验值实际占用还取决于素材纹理复杂度和运动幅度。宁可用更高一档的显存也不用反复试错换显卡。输入素材这个环节值得多说一句。RIFE 这类深度模型对压缩噪声非常敏感低码率 H.264、流媒体二次压制、带平台水印的素材插帧后压缩噪声会被放大成明显的块状或闪烁。我自己的规范是优先找原始录制文件如果只有压缩片先做轻量去噪比如 FFmpeg 自带的 hqdn3d 滤镜再跑插帧字幕、台标这类静态叠层偶尔会产生奇怪的吸附感如果影响主体考虑先裁剪再插帧。3.2 核心命令与参数--exp、--half、--scdet 的真实含义RIFE 推理的核心命令其实很简洁我用的是这样的模板python inference_video.py --video input.mp4 --output output.mp4 --exp2 --half1 --scdet1很多人第一次看到 --exp 这个参数会懵。它不是输出多少帧的意思而是帧数扩增的指数。简单理解exp1 大约是 2 倍帧率exp2 大约是 4 倍帧率exp3 大约是 8 倍帧率实际对应关系以当前版本 README 为准。RIFE 是用级联方式做高倍数插帧的不是一次把所有中间帧全算出来而是先算 2 倍再在已有帧之间继续插逐步翻倍。这样的好处是每一级插帧的难度都保持在两帧之间补一帧这个稳定水平不容易出灾难性错误。--half 参数我几乎每次都开它表示半帧模式。插帧时模型只需要生成时间中点的那一帧另一半时间点保留原始帧。开启后渲染量直接减半推理时间几乎减半而画质损失肉眼基本看不出来。做批量处理时这个参数能省下大量时间。--scdet 是场景切换检测也强烈建议开着。后面我详细讲硬切灾难这里先记住一点场景硬切前后两帧的内容完全没有运动对应关系如果不做检测模型会硬生生把两个毫无关联的画面融合成一个鬼影像。RIFE 内置了场景检测检测到体积变化超出阈值就跳过插帧保留原帧硬切避免乱补。跑完之后用 FFprobe 验证输出帧率是否达到预期ffprobe -v error -select_streams v:0 -show_entries streamr_frame_rate -of defaultnoprint_wrappers1 output.mp4我没见过哪次项目是什么参数都不调直接一把过的所以建议你先拿出 30 秒的素材跑通全流程确认参数和输出效果再铺开跑整片。3.3 批处理与项目化管理不要对着几十个视频手动跑真实项目很少只有一个视频往往是一个文件夹里几十条素材。我写过一个简单的 Python 批处理脚本遍历目录里的 mp4/mov逐个调用 RIFE 命令行输出按项目结构归档import os, subprocess, sys input_dir /data/raw output_dir /data/rife_output exp 2 for root, _, files in os.walk(input_dir): for f in files: if not f.lower().endswith((.mp4, .mov, .mxf)): continue in_path os.path.join(root, f) rel_path os.path.relpath(in_path, input_dir) out_path os.path.join(output_dir, os.path.splitext(rel_path)[0] _x4.mp4) os.makedirs(os.path.dirname(out_path), exist_okTrue) subprocess.run([ python, inference_video.py, --video, in_path, --output, out_path, --exp, str(exp), --half1, --scdet1, ], checkTrue) print(fdone: {in_path} - {out_path})跑长视频时建议先做场景分段。RIFE 本身对整段视频适配没问题但超长视频一旦中途出错就要从头再来输出目录、日志、临时文件都会变得很乱。我更习惯先用 ffmpeg 把片子切成若干片段逐段插帧后拼接这样某个片段效果不好还能单独重跑不进全片重来。拼接时要注意帧率一致性统一转成整数帧率再 concat否则可能出现音画不同步。顺带一提RIFE 的推理脚本已经能保留音频流但我在生产流程里通常先分离音轨插帧后重新合成因为音频的采样率和视频帧率变化没关系分离重装能避免各种容器兼容性小问题。4. 实测里最常翻车的几个场景与调参经验4.1 硬切画面超帧最典型的伪影灾难我第一次拿 RIFE 跑一条多机位素材时翻车翻得很彻底。镜头从室内访谈直接切到室外空镜我在输出视频里看到一段持续时间不到一秒的恐怖画面室内人物的半张脸和室外的大楼边缘叠在一起像一张被揉烂的拼贴画。原因很简单硬切前后两帧来自两个完全不同的拍摄场景不存在任何连续运动路径。模型只能强行猜测怎么从室内走到室外结果就是生成了一个既不属于 A 也不属于 B 的混合幽灵帧。解决方式就是前面提到的 --scdet1。RIFE 开启场景检测后遇到切换幅度超过阈值的帧对会自动跳过插帧硬切处保留原始帧。但这里有个细节阈值太灵敏会把同一个镜头里的剧烈运动比如镜头快速摇移误判成场景切换导致该插的帧被跳过阈值太松又会漏掉部分硬切。我的做法是先跑一遍用 ffprobe 抽取检测日志仓库里带了 scdet debug 模式对比原片检查哪些位置被跳过再调整阈值参数。另外如果素材是从多机位剪辑软件导出的最好在剪辑阶段就把硬切点标记出来渲染时直接按片段切分插帧从源头杜绝跨场景融合。注意渐变转场白场过渡、叠化是另一个特殊情况。它虽然不是硬切但两帧的叠加变化同样不遵循普通运动规律插帧结果容易出现亮度闪动。这类镜头我倾向于在后期软件里重新做转场而不是指望插帧模型去理解叠化。4.2 低纹理、夜景噪点和静态镜头插帧变成蠕动另一个高频翻车场景是低纹理区域和夜空噪点。做一条城市延时素材的插帧时我发现在大面积天空和渐变色墙面的部分插帧后的画面出现了非常微弱的蠕动感像画面在呼吸。放慢看甚至能看到像素在无规则地漂移。原因是这类区域缺乏纹理信息模型很难确定像素间的对应关系光流估计结果噪声很大。网络被迫在没有可靠对应关系的地方创造像素结果就是同一位置每一帧都给出轻微不同的猜测叠在一起产生蠕动。我的处理经验有几个层次。第一如果只是想让画面不那么跳可以降低插帧倍数从 4 倍降回 2 倍让模型每次只需要猜测一半的信息。第二开启源帧混合RIFE 较新版本提供了与源帧混合的参数本质上是让输出帧在一定程度上保留真实采样点的特征压制纯生成内容的自由度代价是运动平滑度略降。第三给素材做轻量降噪后再插帧夜空噪点如果不去除插帧模型可能把噪点也当成运动信号来处理产生更多抖动。最后如果这个大范围天空本来就是慢动作里极不重要的背景直接在后期对背景做轻量模糊或降频处理把观众的注意力集中在主体上效果反而更好。4.3 遮挡与大位移运动边缘撕裂和鬼影遮挡是插帧物理上的死穴深度学习只是缓解它并没有彻底解决。典型场景是跑步的人经过一堵墙墙在 A 帧被人物挡住一部分在 B 帧人物挪开后露出墙中间帧里墙该长什么样的信息在 A 帧里根本不存在。模型只能用附近帧和空间上下文推理推理不出来时就会出现边缘撕裂、半个物体半透明、鬼影等现象。我通常把这些问题的处理分成三层。第一尽量在拍摄端规避运动幅度别太大别让前景频繁遮挡背景只能靠多拍几条。第二插帧后针对个别关键镜头做补偿处理比如把产生撕裂的几帧在剪辑软件里做局部重建或者用关键帧把中间错误帧替换成相邻真实帧的动态模糊版本虽然不完美但考虑到画面切得够快观众基本感知不到。第三在项目里明确分级主体运动必须准确边缘遮挡的局部瑕疵可以接受只要不喧宾夺主。这点想清楚审片时你会轻松很多。大位移运动则是另一个维度的问题。物体一帧内跳跃了几十个像素模型可能抓不到正确的匹配结果把物体插到错误的位置。经验上运动速度超过画面高度三分之一每秒相对目标帧率时失败率明显上升。这时候单纯提高插帧倍数也没用反而是降低倍数、配合动态模糊更稳妥。4.4 素材的运动模糊决定了插帧质量的上限这一点很多教程都不会提但它非常关键拍摄时的快门速度直接决定了插帧的天花板。30fps 素材如果采用常见 180 度快门1/60 秒曝光每一帧里运动的物体自带明显动态模糊。插帧时模型面对的是两个带着方向性模糊的画面它很难区分这个物体本来就在这个位置和这个物体运动到一半经过这个位置输出帧往往呈现半透明的重影尤其高速运动场景。体育摄影和航拍素材之所以插帧效果好很大程度上因为快门很快运动被冻结成清晰瞬间插帧只需要把清晰的瞬间点沿运动轨迹连接起来。所以如果你计划后期做超帧前期拍摄尽量提高快门速度哪怕牺牲一点感光度都值。已经拍糊的素材不要指望超帧能救回来先判断运动模糊的方向和幅度再决定要不要走超分插帧的复合路线这个下面详细说。5. 把超帧放进更大的视频增强流水线5.1 先超分还是先插帧我建议先超分再插帧做老视频修复项目时几乎每个人都会遇到这个问题素材又模糊又卡顿是该先做空间超分还是先做时间插帧我一开始也纠结过后来用同一段素材试过两条管线最终结论是绝大多数情况下先超分、再插帧。原因有三。第一光流估计在高分辨率图上更稳定。运动估计依赖纹理和边缘特征分辨率越高特征越丰富匹配错误的概率越低。你先用 Real-ESRGAN 这类模型把画面提到 1080p 甚至 4K再去算光流运动向量的可靠性会明显上升插帧的连贯性也更好。第二先插帧会让后续超分把插帧引入的伪影成倍放大。插帧本来就可能在遮挡边缘引入轻微残影超分模型对残影的处理是强化边缘结果就是残影被超分放大成一团更明显的色块。如果先超分伪影在较高的分辨率下更容易被识别和修正。第三从算力角度如果你最终目标是 4K 60fps先对 1080p 插帧再超分等于对每个插帧结果做一次超分计算量翻倍先超分到 4K 再插帧插帧数量反而少相对 1080p 插帧后再超分整体耗时通常更可控。唯一的例外是极低分辨率素材比如 240p-360p 的老影像。这种分辨率下直接超分模型能参考的细节太少出来的画面经常是塑料感很强的假清晰。这时候可以尝试先插帧到正常帧率给超分模型提供更多时间邻近帧作为参考部分超分模型支持连续帧输入输出的时序一致性会更好。我的经验法是分辨率高于 720p 的素材先超分再插帧低于 480p 的素材试两条路线对比再决定。5.2 慢动作时间重映射与超帧的配合纯超帧是把帧率均匀翻倍但剪辑里的慢动作常常不是均匀的需要变速曲线。这个环节要特别注意先插帧到足够高的均匀帧率再做变速不要试图在变速后再依赖播放器的补帧功能。举个例子25fps 素材要做 4 倍慢动作目标输出 100fps 时间线。那么先插帧到 100fpsexp2约 4 倍剪辑软件里把这 100fps 的真实帧按 25% 速度播放每一帧都有真实的中间帧支撑顺滑。如果你只插到 50fps然后靠剪辑软件的帧混合光流补算来撑剩下的倍率等于把超帧交给剪辑软件引擎质量和可控性都会下降而且引擎对遮挡和硬切的处理通常不如专门模型。体育、舞蹈这类运动节奏变化剧烈的素材均匀插帧会让爆发动作显得太匀速少了起跑和冲刺的视觉冲击。我的做法是插帧到高帧率后在剪辑软件里配合速度曲线做二次时间重映射让起跳瞬间快一点、滞空瞬间慢一点再用插帧得到的中间帧素材兜底效果比纯变速或纯插帧都好。要注意重映射之后再做一次输出渲染时别再开插帧类效果否则叠加生成会让运动边界出现第二次伪影。5.3 个人经验项目落地前的小样本测试策略最后分享一个我踩了多次坑之后总结下来的工作习惯任何新素材拿到手先不要整段处理抽出三到五秒包含最复杂运动的片段跑一遍全流程超分→插帧→变速→色彩放大到原始分辨率慢慢看。重点检查三处人脸的边缘、字幕台标、快速移动物体与背景的交界。这三处是伪影最容易暴露的地方。小样本过关后再跑全片并且全程保留中间文件原始素材、超分后素材、插帧后素材、变速后素材分别存目录。后期发现某一步出了问题可以从对应环节重跑而不是整条管线推倒重来。这个习惯在片子较长时特别重要我曾经因为忘了存超分输出一个 40 分钟的片子从原始素材开始重跑了三个小时从那以后就再也没偷过懒。插帧模型在这类任务里已经足够成熟RIFE 完全可以处理大多数实际素材但成熟不意味着万能。素材质量差、拍摄参数激进、硬切多的内容你必须保留人工检查的环节。先拿样片测试再铺开批量始终是超帧工程里成本最低、收益最高的策略。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进