资讯动态

hyperframes视频补帧实战:光流估计与深度学习插帧技术全解析

发布时间:2026/10/15 15:18:06 来源:尧图企业网站定制
hyperframes这个词我第一次注意到它是在一个朋友做的赛车视频里。当时他把一台普通行车记录仪拍的30帧视频硬是插值成120帧的慢动作画面里车轮的辐条、扬起的尘土流畅程度完全不像行车记录仪该有的水平。他管这套处理叫生成超帧也就是hyperframes。从那时候起我就开始系统研究这个技术方向。说白了hyperframes就是在视频的原始帧之间通过算法凭空无中生有地插进额外的中间帧让视频从30fps变成60fps、120fps甚至240fps。它不是一个具体的开源库名而是一类技术的统称核心包括光流估计、运动补偿、帧插值三大块。这几年随着深度学习和AI视频处理工具的发展这套东西已经从实验室走出来了剪映、Pr里的光流法补帧游戏里的DLSS 3插帧本质都在做同一件事。这篇文章我打算从原理到实践完整拆一遍hyperframes的生成链路。适合这几类人看做视频后期想把普通素材补成高帧率慢动作的剪辑师做自动驾驶或机器人视觉需要帧率统一的研究人员以及对视频处理算法感兴趣的开发者。我会先讲清楚光流底层的运动估计是怎么回事再给出一个不依赖深度学习、只用OpenCV就能跑起来的补帧方案最后对比主流深度学习方法RIFE、DAIN、FILM的实际表现和选型建议。全程附代码和参数能直接抄作业。1. 从24帧美学到高帧率刚需hyperframes出现的背景1.1 为什么我们明明有24帧电影却还需要更多帧电影行业用24帧用了快一百年24帧的轻微卡顿甚至成了电影感的一部分。但那是针对影视叙事的审美选择不是技术天花板。现实世界里很多场景确实需要比这高得多的时间分辨率慢动作拍摄还原一个水滴落下的瞬间、运动员起跳的爆发力120fps以下几乎没有观赏价值。高速运动分析机器人抓取、工业流水线检测、无人机避障都需要在高运动速度下保持足够的时间采样密度。视频会议与直播拙劣的低帧率在压缩后会产生严重的卡顿和拖影用户体验极差。游戏插帧渲染管线输出的帧率不够需要补帧让画面更顺滑同时降低硬件负担。问题在于真正的高速摄像机价格高昂——一台能拍1000fps的高速机预算轻松上六位数。消费级的相机里能稳定拍240fps的也屈指可数。所以用算法生成中间帧就成了一个极具性价比的方案用已有的低帧率视频通过插帧技术合成高帧率视频这就是hyperframes的初衷。1.2 补帧和插值的本质区别很多人看到补帧就想到视频剪辑软件里的倍速平滑效果但hyperframes和那种简单补帧完全不是一个量级的东西。简单补帧比如线性平均的原理是把前后两帧的像素直接做加权平均生成中间帧。你看重的物体在两帧之间移动了一大截直接平均的结果就是重影加模糊像没对上焦的鬼影。hyperframes强调的是运动感知先算出每个像素从前一帧到后一帧是怎么移动的光流再沿着运动轨迹把像素搬到中间位置。简单比喻一下线性补帧是两个图层直接半透明叠加光流补帧是先认识跑步的人再算出他跑到一半时该在哪一步然后把整个人的轮廓按姿态搬过去。两者的画质差距在运动越剧烈时拉得越开。1.3 hyperframes这个叫法从哪来hyperframes这个术语最早出现在一些学术论文里主要指通过深度学习方法在关键帧keyframes之间生成的高密度中间帧。后来被社区引用逐渐泛化成了基于光流和深度学习的视频帧插值的代名词。和它关联度最高的技术栈包括技术名词作用关联度Optical Flow光流像素级运动估计超帧的基础极高Frame Interpolation帧插值在相邻帧间生成中间帧核心Motion Compensation运动补偿按光流重定位像素位置核心Warping翘曲将一帧按运动矢量扭曲成目标时刻的状态核心RIFE / DAIN / FILM深度学习的插帧模型高理解了这个背景你再去看hyperframes相关的代码、论文会更容易抓住主线所有的努力都围绕如何准确估计运动和运动估计错了怎么补救这两件事展开。2. 光流估计算法拆解hyperframes的地基牢不牢全看它2.1 什么是光流在视频处理语境里光流描述的是相邻两帧之间每个像素的运动方向和速度。简单说前一帧里某个点在坐标(x, y)后一帧它移动到了(xu, yv)那(u, v)就是这个点的光流矢量。全图所有像素的光流矢量合在一起叫做光流场。计算光流要解决的核心问题是对应匹配我要找到前一帧的像素点在后一帧里的准确位置。这本质上是个病态问题——一片纯色的墙壁在画面里移动像素点长一模一样你根本分不清谁是谁。所以所有的光流算法都在做各种各样的假设来约束这个求解过程。2.2 两大经典光流方法稀疏的Lucas-Kanade与稠密的Farneback早期最有影响力的光流算法是Lucas-KanadeLK。它有一个核心假设在足够小的邻域窗口内所有像素的运动是相同的。这个假设把每个像素的求解变成一个小线性方程组通过最小二乘求解。LK法优点是快、准在纹理丰富区域缺点是稀疏——它主要跟踪特征点如角点不是逐像素输出。OpenCV里cv2.calcOpticalFlowPyrLK就是它的实现。另一个是Gunnar Farneback提出的多项式展开法同样在OpenCV里直接可用cv2.calcOpticalFlowFarneback。它的思路是用多项式对每个像素邻域的灰度分布做建模比较前后两帧多项式系数的变化来推算位移。这个算法输出的是稠密光流——每个像素点都有运动矢量。对hyperframes来说稠密光流是必须的因为你最终要逐像素生成中间帧。实际工程里我绝大多数情况是用Farneback做稠密光流。代价是计算量大得多尤其在高分辨率素材上。下面重点看它的参数含义因为很多人的补帧效果差问题出在参数没调好。2.3 Farneback光流参数逐个说cv2.calcOpticalFlowFarneback(prev, next, flow, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags)参数看着多其实核心就几个pyr_scale金字塔缩放比例。默认0.5即每层分辨率减半。levels金字塔层数。层数越多越能捕捉大尺度运动。一般设3到5。处理1080p以上素材时层数不够的话大位移目标会直接丢失匹配。winsize邻域窗口大小。值越大算法对噪声越鲁棒但运动细节会被平滑掉导致细小物体的运动不准确。值太小纹理弱的区域容易出错。通常选15到21。iterations每层金字塔上的迭代次数。一般3到5次足够多了计算量翻倍但精度提升有限。poly_n多项式展开的邻域大小通常选5或7。poly_sigma高斯分布标准差一般取1.1到1.5与poly_n配套。flags一般填0或者OPTFLOW_FARNEBACK_GAUSSIAN使用高斯窗口更平滑但更慢。我自己的经验是1080p视频、目标是从30fps补到120fps典型配置为flow cv2.calcOpticalFlowFarneback( prev_gray, next_gray, flowNone, pyr_scale0.5, levels5, winsize21, iterations5, poly_n7, poly_sigma1.5, flags0 )如果你的素材里运动幅度很大比如镜头快速甩动把levels加到6到7但代价是时间暴涨。实测8秒的1080p片段用这套配置在CPU上跑大概需要20到40秒还算能接受。2.4 光流的正确用法前向光流和后向光流为什么要同时算生成中间帧时光流得用前后各一把尺子才能量准。从t时刻的帧出发算它如何运动到t1时刻这叫前向光流。反过来从t1时刻出发算它如何回到t时刻叫后向光流。为什么不能只算一次前向光流因为存在遮挡问题前一帧里能看到的物体运动到后一帧时可能被其他物体挡住了一部分这部分像素没有对应的后一帧信息。如果我们只用前向光流去搬像素遮挡区域的中间帧就会留一个黑洞充满无意义的脏点。解决办法就是同时计算后向光流然后对两个方向的结果做交叉一致性检查——如果某个像素的前向光流和后向光流方向互相矛盾两者加起来不为零就认为这个像素处于遮挡状态改用更保守的插值策略来处理它。这部分细节会在下一节的代码里体现。3. 用OpenCV手工搭建一个轻量级hyperframes生成器3.1 整体流水线设计我没有直接用现成的深度学习模型之前第一版超帧生成器就是用OpenCV手工搭的流程是读取相邻两帧 - 转灰度 - 双向光流估计 - 光流一致性检查 - 按时间比例t扭曲两帧 - 融合遮挡区域 - 输出中间帧。这个流程的好处是每一环都可控、可调试。深度学习方法有大量黑盒成分出了问题很难定位是模型的问题还是输入数据的问题。手工管线出了问题一眼就能看出是光流算错了还是扭曲逻辑写错了。对于学习hyperframes先亲手搭一遍这个流程非常有必要。3.2 像素扭曲warping的实现细节按光流扭曲一帧本质是一个重新映射的过程。OpenCV里可以用cv2.remap实现。逻辑是对于目标时间t0到1之间0取前帧1取后帧中间帧某个像素的坐标应该等于前帧对应像素坐标加上它的光流矢量乘以时间比例t。你可能觉得只要把前帧的每个像素按x u * t搬过去就行了但这里有个细节光流矢量是前帧坐标空间里的量不是中间帧坐标空间里的量。好的做法是用前向光流把后帧倒推回中间时刻同时用后向光流把前帧推送到中间时刻然后融合。直接上代码import cv2 import numpy as np def warp_flow(img, flow, t): 根据光流场将图像扭曲到时刻 t (0~1) 使用反向映射 双线性插值 h, w img.shape[:2] flow flow.astype(np.float32) # 创建坐标网格 x, y np.meshgrid(np.arange(w), np.arange(h)) # 目标时刻的映射坐标 原始坐标 光流 * t map_x (x flow[..., 0] * t).astype(np.float32) map_y (y flow[..., 1] * t).astype(np.float32) # remap: 对img按map坐标重新采样 warped cv2.remap(img, map_x, map_y, interpolationcv2.INTER_LINEAR) return warped def compute_consistency(flow_forward, flow_backward): 前后光流一致性检查返回遮挡掩膜 遮挡区域标记为 True该区域需要保守处理 h, w flow_forward.shape[:2] x, y np.meshgrid(np.arange(w), np.arange(h)) # 用前向光流估算目标位置 map_x np.clip(x flow_forward[..., 0], 0, w - 1).astype(np.float32) map_y np.clip(y flow_forward[..., 1], 0, h - 1).astype(np.float32) # 在目标位置上采样后向光流 sampled_backward_x cv2.remap(flow_backward[..., 0], map_x, map_y, interpolationcv2.INTER_LINEAR) sampled_backward_y cv2.remap(flow_backward[..., 1], map_x, map_y, interpolationcv2.INTER_LINEAR) # 前后向量和不为0的为不一致区域 diff np.sqrt((flow_forward[..., 0] sampled_backward_x) ** 2 (flow_forward[..., 1] sampled_backward_y) ** 2) threshold 1.0 occlusion_mask (diff threshold).astype(np.float32) # 膨胀一下让边界更均匀 kernel np.ones((5, 5), np.float32) occlusion_mask cv2.dilate(occlusion_mask, kernel) return occlusion_mask def generate_hyperframe(img_prev, img_next, gray_prev, gray_next, t0.5): 生成位于两帧之间的超帧 img_prev / img_next: BGR 图像 gray_prev / gray_next: 灰度图 t: 目标时刻0~1 # 前向光流从prev - next flow_forward cv2.calcOpticalFlowFarneback( gray_prev, gray_next, None, 0.5, 5, 21, 5, 7, 1.5, 0 ) # 后向光流从next - prev flow_backward cv2.calcOpticalFlowFarneback( gray_next, gray_prev, None, 0.5, 5, 21, 5, 7, 1.5, 0 ) # 中间帧双向扭曲 warped_prev warp_flow(img_prev, flow_forward, t) warped_next warp_flow(img_next, flow_backward, -(1 - t)) # 一致性检测 occ_mask compute_consistency(flow_forward, flow_backward) # 融合遮挡区域用加权平均非遮挡区域用自适应权重 # 为了平滑融合生成一个过渡权重 alpha 1.0 - t fusion warped_prev * alpha warped_next * (1 - alpha) # 遮挡区域完全使用自适应权重或者可进一步处理 # 这里简单做混合 result np.where(occ_mask[..., None] 0, fusion, fusion) # 再做一遍轻微的高斯模糊消除扭曲产生的毛刺 result cv2.GaussianBlur(result, (3, 3), 0) return result这段代码里有两个容易出错的点。第一个是warp_flow的t参数用法不同实现里方向定义可能完全相反得先拿一张简单的移动白块图片做单元测试确认方向。第二个是后向扭曲时-(1-t)这个负号——它的含义是我们想把next帧倒推到中间时刻就需要沿运动的反方向补偿这在代码里体现为时间轴上的负位移。3.3 在两个实际视频帧上的验证结果我用一段快速摇动镜头的城市街景视频做了测试取第100帧和第102帧隔1帧生成中间的第101帧t0.5。从人眼角度看生成的中间帧里移动中的行人轮廓、车辆的边缘都保持在合理位置没有明显重影。但仔细看快速移动的车胎辐条位置会出现细节模糊这就是光流估计的误差集中区域。整体而言这套纯传统方法生成的中间帧在运动速度不快的情况下视觉质量可以接近真实拍摄的中间帧一旦运动速度过快或者出现严重的物体间遮挡就需要换深度学习方法了。3.4 传统方法的局限以及为什么要转向深度学习传统光流补帧的上限非常明确大位移失效Farneback光流对位移超过几十个像素的目标几乎没有匹配能力而这在慢动作补帧中非常常见。遮挡死区虽然双向一致性检测能识别遮挡但不能恢复遮挡区域的真实内容只能用模糊近似。运动物体后面的背景到底是什么样算法猜不出来。纹理缺失区域白墙、天空等平坦区域光流估计全靠邻域信息硬猜结果极不稳定。这些问题在深度学习时代有了根本性的改观因为神经网络学会了在大规模视频数据上隐式建模物体运动规律甚至能在一定程度上推理出遮挡物体背后的内容。4. 深度学习方法RIFE、DAIN、FILM的超帧生成对比4.1 三种主流方法的工作机制差异DAINDepth-Aware Video Frame Interpolation为每一帧估计深度图并结合深度信息分割前景和背景再配合光流做分层插值。它的优势是能较好地处理物体前后遮挡关系。缺点是早期版本速度非常慢需要GPU一个1080p中间帧要好几秒。RIFEReal-Time Intermediate Flow Estimation核心思路是设计了一个轻量的中间流估计模块在真实光流的中间时刻完成运动建模配合自定义的插值损失函数迭代训练。速度极快在消费级GPU上甚至能做到实时。缺点是快速运动场景偶尔出现纹理拉花。FILMFrame Interpolation for Large Motion谷歌出品专门针对大位移场景做了多尺度特征金字塔的渐进式插值结构设计上更适合画面里有物体快速横穿的场景。同尺寸下质量通常是三者里最稳的但速度也最慢。方法是否依赖外部光流大位移处理能力速度1080p单帧插值适用场景DAIN依赖深度光流中等约2-5秒/帧场景带有明显前后遮挡关系RIFE不依赖内嵌光流模块中等偏上实时效果可达30fps以上实时直播、游戏插帧、批量处理FILM不依赖强约1-3秒/帧高质量离线补帧、弱纹理场景这是基于我个人经验总结的参考值。不同硬件下差距会很大但相对关系不变RIFE最轻、FILM最重、DAIN居中偏慢。4.2 为什么我更推荐基于学习的方法直接替代传统光流如果你只是想做一次性离线生成高帧率视频不需要完全理解底层原理那直接上深度学习模型是效率最高的选择。原因很简单传统方法的每个缺陷恰好都是深度学习方法重点优化的方向。你花在手工搭管线上的时间很可能够你调好一个现成模型跑完整个素材了。而且现在的深度学习模型框架都提供了方便的Python接口。以RIFE为例用它的推理接口只需要加载模型、准备相邻帧、调用frame_interp接口就得到一张中间帧。自己定义每两帧之间要生成多少中间帧就能得到任意倍率的超帧序列。import torch from rife import IFNet # 伪代码实际以官方仓库为准 model IFNet() model.load_state_dict(torch.load(rife49.pth)) model.cuda().eval() def make_hyperframes(frame1, frame2, n): frames [frame1] for i in range(1, n): t i / n mid model(frame1, frame2, t) frames.append(mid) frames.append(frame2) return frames逻辑非常直接。你定义想在两帧之间生成几张中间帧它就按对应的时间比例输出。这种灵活性是你手工实现要写很多代码才能达到的。4.3 深度学习方法依然绕不开的坑虽然深度学习模型很强大但我用下来还是发现一些共性问题要提前打预防针训练集偏见模型在训练数据上见过更多的场景类型不一定适配你的素材。比如大量训练数据是人物肖像遇到快速运动的无人机航拍素材表现就会掉档。水印和字幕处理画面里固定的水印、字幕经过插值后容易产生抖动。因为光流在文字边缘会产生错误匹配。过度平滑某些模型倾向生成非常平滑、没有细节的中间帧看起来有一种AI涂抹感在皮肤纹理、草地、头发等区域特别明显。内存爆炸高分辨率素材4K及以上配合大型模型显存可能直接爆掉。解决这些问题没有一个万能方法。我自己常用的策略是分块处理tile或者先用视频超分辨率模型降噪再补帧。这些都属于工程经验需要根据具体素材反复调试。5. 端到端工程实践把30fps视频补到120fps的完整流水线5.1 整体方案选型综合上面的对比我个人的项目选了RIFE作为主力模型理由很实际一是GPU资源有限RIFE在速度和显存上压力最小二是它的插帧质量在绝大多数运动场景下已经足够好。这个项目的目标很具体把一段30fps的1080p街拍视频补到120fps并压制成120fps的MP4文件。整个流水线分四段抽帧用ffmpeg把视频按原始帧率导出为PNG序列。超帧生成对每一对相邻原始帧生成3张中间帧30fps补到120fps每对原始帧之间需要插入3张新的中间帧加上原始后帧构成4帧间隔。组装把原始帧和生成帧按顺序重新命名再交给ffmpeg编码成120fps视频。验证用ffmpeg的freezedetect和手动肉眼检查确认没有卡顿和跳变。5.2 抽帧与组装的具体命令抽帧命令输出为无损PNG保证后续质量ffmpeg -i input.mp4 -q:v 1 %04d.png组装命令注意帧率参数ffmpeg -framerate 120 -i %04d.png -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p output_120fps.mp4-framerate必须和生成后的帧序列实际帧率匹配否则时间轴会错乱。编码参数-crf 18是接近无损画质的设置适用于最终出片如果是临时预览可以放宽到23。5.3 超帧生成主循环核心思路遍历所有相邻原始帧对对每一对生成3个中间帧。注意一个细节原始帧本身也要参与输出序列否则视频的总帧数会少掉一个时长会缩短。import os import cv2 import torch from rife import IFNet # 伪代码示意实际调用以实际仓库为准 frames sorted([f for f in os.listdir(.) if f.endswith(.png)]) n_frames len(frames) interp_factor 4 # 30fps - 120fps每对帧之间插入3帧共4段 model load_model() # 加载RIFE模型 out_index 0 for idx in range(n_frames - 1): img1 cv2.imread(frames[idx]) img2 cv2.imread(frames[idx 1]) # 原样输出第一帧 cv2.imwrite(fout_{out_index:06d}.png, img1) out_index 1 # 生成3个中间帧 for k in range(1, interp_factor): t k / interp_factor mid model(img1, img2, t) cv2.imwrite(fout_{out_index:06d}.png, mid) out_index 1 # 最后输出尾帧 cv2.imwrite(fout_{out_index:06d}.png, cv2.imread(frames[-1])) out_index 1这个循环笨但实用。每处理一对帧就输出一个原始帧加三个合成帧最后把尾帧补上。整个工程不需要复杂的状态机反而最不容易出错。5.4 实测效果和资源消耗我的实测数据GPU是RTX 3060视频分辨率1920x1080共10秒素材即300帧原始帧生成中间帧总数为 299 x 3 897 帧。单帧插值时间约0.1到0.2秒。全程总耗时约3到4分钟。显存占用约3到5GB。生成的120fps视频播放慢动作流畅度比原始30fps提升非常明显。尤其是水花四溅、烟雾飘散这类运动原先每一帧之间的断裂感完全消失了。5.5 批处理中的工程细节别忘了时间码如果你要处理的是一个多段视频拼接的项目输出帧序列的时间对齐很关键。最简单的办法是在抽帧阶段就保留原始帧序号生成超帧时写在文件名里的序号严格递增让ffmpeg能按文件名排序编码。这样即使某个片段抽了几帧前后时间线也始终对齐。6. 参数调优笔记让hyperframes质量上限再拔高一截6.1 影响质量的三个最关键的工程变量从我大量测试来看影响超帧生成质量的因素按影响力排序是原始视频的噪声水平、运动幅度、模型选择的匹配度。噪声是最大的隐性杀手。压缩视频在高感光度区域有大量噪点这些噪点会被光流误判为微小运动导致插帧后噪点变成移动颗粒。解决方案是在抽帧之后、插帧之前先做轻度降噪比如用cv2.bilateralFilter或视频专用的时域降噪滤镜。注意不要降太狠否则运动边缘会糊。运动幅度方面如果你的素材是极高速运动比如赛车过弯、高速旋转建议先用抽帧间隔更小的策略比如每5帧做一次插值而不是每2帧。原因很简单相邻帧运动位移越大任何算法都越容易出错把位移降下来模型精度会指数级提高。模型匹配度方面我建议不要看了别人的评测就锁死一个模型。素材类型不同最佳模型也不同。实测下来人物走动、日常扫街、自然风景RIFE表现很好。快速体育动作、车辆飞驰FILM更稳RIFE偶尔出现局部扭曲。带深度层次的复杂场景前景背景分离明显DAIN有额外优势。6.2 遮挡区域的处理心得前面提到过双向光流一致性检查这里分享一个更实用的经验。即使使用深度学习模型你可能也没法完全消除遮挡伪影。一个有效的补救措施是时域上做多点采样除了在0.5时刻生成一个中间帧还额外采样0.25和0.75时刻然后根据输出序列需求选优或融合。因为遮挡在不同时刻表现不同多点采样能摊薄单一时刻的误差。另一个土办法是直接用ffmpeg的minterpolate做快速预览先看哪些时间段的运动最复杂再针对性做高质量补帧。盲目的全程高质量补帧很多时候是在浪费算力。6.3 最简单的质量评估方法冻结帧检查我对插帧结果质量做快速验证的方法不是去算PSNR、SSIM这种指标而是用ffmpeg的freezedetect。这个方法能检测视频中是否有大量重复的帧但更重要的是我会专门找一个运动速度中等的片段把补帧结果转成慢动作逐帧暂停看静止背景是否稳定不动如果背景在抖动说明光流把背景的运动估计错了。移动物体边缘是否有异常拉长如果有说明光流在边缘处失效。画面是不是有呼吸感整体亮度周期性变化如果有说明中间帧的融合权重设置有问题。这些检查不需要任何专业软件用播放器加肉眼就能完成。每次模型、参数改动后快速跑一遍就能有效避免大规模翻车。6.4 性能优化实战从1小时缩短到10分钟如果你手里的素材很多性能优化是绕不开的。我常用的三板斧FFmpeg多线程抽帧ffmpeg -i input.mp4 -q:v 1 -threads 8 %04d.png能显著提升抽帧速度。分块插帧遇到4K素材我会先在CPU上把帧切成左半和右半分别跑模型再拼接。拼缝处做渐变融合避免接缝明显。注意分块时要留出重叠区否则目标跨两半时会断裂。半精度推理把模型权重转为float16显存占用直接减半速度也能提升一些。代价是极少数细节可能损失但肉眼几乎看不出来。model.half() # 模型转为fp16 # 输入图像也转成fp16 img1_h img1.cuda().half() img2_h img2.cuda().half() mid model(img1_h, img2_h, t)7. 把hyperframes用到你的真实场景一些越用越顺的经验这套技术我能坚持用下来有几个场景反馈特别好。一是老素材复活家里用老DV拍的30fps视频补到60fps后画面一下子有了现代感。二是做产品演示视频把机器运转的瞬间补成慢动作客户观感提升非常明显。三是做科研数据预处理把低帧率相机拍的实验视频补帧后再做运动轨迹分析结果平滑很多。反过来也有几个场景我不建议用hyperframes。一是对画面真实度要求极高、不能有任何算法痕迹的专业影视输出这类片子宁可多花钱租高速摄像机也不要冒险让AI去补帧。二是素材本身已经严重压缩、噪声遍地、细节全无的视频补帧只会放大瑕疵。三是时间轴极其规律、所有物体匀速直线运动比如滑轨延时摄影补帧反而可能让运动变得不匀称。结合前面的内容我最后想分享三个实用技巧都是我踩过坑之后总结出来的。第一个技巧插帧前先把视频的节奏调慢。对30fps素材补帧到120fps如果你最终想要慢动作效果不要直接对30fps补帧到60fps再放慢播放。正确做法是先按原速补帧到120fps再在剪辑软件里把这120fps视频放慢4倍变成30fps输出。这样每个中间帧都包含真实的运动信息慢动作才顺滑。第二个技巧利用隔帧插值策略处理极快运动。如果原视频是30fps且运动极快相邻两帧之间位移太大补帧效果往往不佳。你可以手动抽取第1帧和第5帧作为一对虚拟前后帧在这4帧的间隔里生成15个中间帧等效于把原视频的慢动作密度提高了。虽然时间轴上的均匀性变了但视觉流畅度反而更好。第三个技巧不要迷信单一工具把传统方法和深度学习按场景混用。静态大场景、运动极少的视频用传统光流方法就够了速度快还不容易出错。运动复杂的片段单独拆出来用FILM处理再拆回原序列。混合处理虽然工程复杂一点但能同时保证速度和质量。hyperframes这个方向其实还在快速进化。早期大家纠结于光流准不准后来纠结于模型选哪个现在已经有不少把超帧生成和视频压缩、超分辨率、甚至3D渲染结合起来的玩法。我自己目前的关注重心已经从怎么把帧补出来转向了怎么判断哪些帧值得补、哪些帧必须保留原始信息——这个问题在真实项目里比想象中重要得多。希望这篇文章能让你少走一些我走过的弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑