光流基础与视频分析 - 运动估计原理到实现
什么是光流 - 图像间的运动向量场
光流 (Optical Flow) 是描述图像序列中像素表观运动的 2D 向量场。对于每个像素,光流向量 (u, v) 表示该像素从一帧到下一帧的水平和垂直位移。
基本假设:
- 亮度恒常性:物体移动时像素亮度不变。I(x,y,t) = I(x+u, y+v, t+1)
- 小位移:帧间运动足够小,可用一阶泰勒展开近似
光流约束方程:对亮度恒常性假设进行泰勒展开得到:Ix×u + Iy×v + It = 0,其中 Ix、Iy 是空间梯度,It 是时间梯度。一个方程两个未知数 (u,v),这就是著名的孔径问题 (Aperture Problem)。
稀疏光流 vs 稠密光流:
- 稀疏光流:仅计算特征点 (角点) 的运动。速度快,适合目标跟踪
- 稠密光流:计算每个像素的运动。信息完整,适合视频分析
应用领域:视频稳定、动作识别、自动驾驶、视频插帧、运动分割、视频压缩 (MPEG 运动补偿)。
亮度恒定假设的数学表达:光流的基础假设是同一物体点在相邻帧中亮度不变,写成 I(x, y, t) = I(x+dx, y+dy, t+dt)。对该式作泰勒展开可得到光流约束方程 I_x × u + I_y × v + I_t = 0,其中 u、v 是光流的两个分量,I_x、I_y、I_t 分别是图像对 x 方向、y 方向和时间方向的偏导数。一个方程含两个未知量,因此必须追加约束才能求解,这正是后续各类方法的分歧点。
稀疏与稠密的取舍:稀疏光流 (Sparse Flow) 只追踪角点等特征点的运动,计算量小;稠密光流 (Dense Flow) 估计每一个像素的运动,信息更完整,但计算成本明显更高。
经典方法 - Lucas-Kanade 和 Horn-Schunck
两种经典光流方法通过不同的额外约束解决孔径问题,各有优缺点。
Lucas-Kanade (1981):
- 假设局部窗口 (如 15×15) 内所有像素具有相同运动
- 在窗口内建立超定方程组,最小二乘求解
- 稀疏光流方法,通常在角点处计算
- 金字塔 LK:多尺度处理大位移。从粗到细逐级精修
- OpenCV:
cv2.calcOpticalFlowPyrLK(prev, next, points, None)
Horn-Schunck (1981):
- 全局方法,假设光流场整体平滑
- 最小化:数据项 (亮度恒常性) + 平滑项 (光流梯度)
- 通过迭代求解产生稠密光流
- 平滑权重 α 控制平滑程度:α 大则光流平滑但细节丢失
Farneback 方法 (2003):使用多项式展开近似每个像素邻域,计算稠密光流。OpenCV 默认的稠密光流方法:cv2.calcOpticalFlowFarneback(prev, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)。速度和精度的良好平衡。
Lucas-Kanade 的求解方式:该方法假设一个小窗口 (例如 15x15 像素) 内的所有像素共享同一个光流向量,把欠定的约束变成超定方程组后用最小二乘法求解。实用中通常与 Shi-Tomasi 的良好特征点选择结合,只把 A^T A 矩阵的最小特征值超过阈值的点作为追踪对象,避免在纹理平坦处得到不可靠的结果。
Horn-Schunck 的能量函数:Horn-Schunck 用变分法最小化能量函数 E = ΣΣ[(I_x u + I_y v + I_t)² + α²(|∇u|² + |∇v|²)],第一项是亮度恒定的数据项,第二项是让相邻像素光流平滑的正则项,α 控制两者的权重。
深度学习光流 - 从 FlowNet 到 RAFT
深度学习方法通过端到端训练大幅超越经典方法的精度,尤其在大位移和遮挡区域。
FlowNet (2015):首个端到端光流 CNN。FlowNetS (单流) 和 FlowNetC (相关层) 两种架构。在合成数据 (Flying Chairs) 上训练。开创了学习光流的先河。
PWC-Net (2018):金字塔、变形、代价体积的组合。多尺度特征金字塔,逐级变形和精修。参数量小 (8.75M),速度快。
RAFT (2020):当前最先进的光流方法之一:
- 构建全对相关体积 (4D):计算所有像素对的相似度
- GRU 迭代更新:从初始零流开始,迭代精修光流估计
- 多尺度相关查找:在不同分辨率的相关体积中查找
- Sintel 基准 EPE: 1.43 (clean), 2.71 (final)
最新进展:
- FlowFormer (2022): Transformer 编码代价体积,捕获全局上下文
- VideoFlow (2023):利用多帧信息提升时间一致性
- UniMatch:统一光流、立体匹配和深度估计的框架
FlowNet 两个变体的差异:FlowNetS (Simple) 把两帧图像在通道方向直接拼接后输入,以编码器与解码器结构回归光流;FlowNetC (Correlation) 先分别编码两帧,再由相关层 (Correlation Layer) 显式计算两帧特征之间的对应关系。
PWC-Net 与 RAFT 的命名与结构:PWC-Net (2018) 的名称取自 Pyramid、Warping、Cost Volume 三个要素,采用由粗到细的金字塔结构逐级精化光流,参数量仅为 FlowNet 的 1/17,精度反而更高。RAFT (2020) 是 Recurrent All-Pairs Field Transforms 的缩写:先构建全部像素对的 4D 相关体积,再以 GRU (Gated Recurrent Unit) 为核心迭代 12-32 次逐步修正光流,并配合多尺度相关查表与从零光流开始的可学习初始化。它在 Sintel (clean) 上取得 EPE 1.43,在 KITTI 2015 上取得 F1-all 5.10%,大幅超越此前的方法。
光流应用 - 动作识别到视频编辑
光流作为视频理解的基础表示,在多个领域有广泛应用。
动作识别:双流网络 (Two-Stream) 将 RGB 帧和光流作为两个输入流,融合空间和时间信息。光流流捕获运动模式,对动作分类贡献显著。TVL1 光流是常用的预计算方法。
视频插帧:利用前后帧的光流,在中间时刻合成新帧。双向光流 + 变形 + 融合是标准流程。RIFE、IFRNet 等方法可实时将 30fps 视频转为 60/120fps。
视频稳定:估计帧间全局运动 (仿射变换或单应性),补偿相机抖动。区分前景运动和相机运动是关键。
运动分割:利用光流将独立运动的物体从背景中分离。光流幅度和方向的不连续性指示物体边界。
视频编辑:
- 光流引导的风格迁移:保持时间一致性
- 视频修复:利用光流从相邻帧传播信息填充缺失区域
- 慢动作生成:光流插帧实现时间超分辨率
动作识别 (Action Recognition):双流网络 (Two-Stream Network,Simonyan 与 Zisserman,2014) 用两个独立的 CNN 分别处理 RGB 帧和光流,再融合两路结果判定动作。该方法在 UCF-101 数据集上达到 88% 的准确率,实证了运动信息本身的重要性。
帧插值与视频稳定:DAIN (Depth-Aware Video Frame Interpolation) 在光流之外还利用深度信息,改善遮挡区域的插值质量。视频稳定 (Video Stabilization) 要把物体的局部运动与相机的全局运动分开,因此常配合 RANSAC 估计单应矩阵,只补偿相机抖动。光流同样用于视频目标分割 (VOS),把独立运动的区域从背景中分离。
光流可视化与评估指标
光流的可视化和定量评估对于算法开发和调试至关重要。
颜色编码可视化:标准方法使用 HSV 色轮:色相 (H) 表示运动方向,饱和度 (S) 表示运动幅度。Middlebury 色轮是最常用的编码方案。OpenCV 实现:将光流转换为极坐标 (幅度,角度),映射到 HSV,再转 BGR 显示。
箭头可视化:在图像上绘制运动向量箭头。适合稀疏光流或下采样后的稠密光流。箭头长度和方向直观表示运动。
评估指标:
- EPE (End-Point Error):预测光流与真实光流的欧氏距离平均值。主要指标
- Fl-all:误差超过 3 像素且超过 5% 的像素比例 (KITTI 指标)
- AE (Angular Error):预测和真实光流向量的角度差
基准数据集:
- Sintel:动画电影渲染的合成数据,有 clean 和 final (含运动模糊、雾) 两个版本
- KITTI:真实驾驶场景,LiDAR 提供稀疏真值
- Flying Chairs/Things:大规模合成训练数据
颜色编码的可视化:最常用的可视化方式是用色相 (Hue) 表示光流方向、用饱和度 (Saturation) 表示位移大小。在 OpenCV 中先用 cv2.cartToPolar() 把光流的 x、y 分量转成极坐标,再映射到 HSV 颜色空间,即可得到直观的运动场图。
Fl 指标与两阶段训练:KITTI 基准使用的 Fl (Flow Error Rate) 指标,统计 EPE 达到 3 像素以上且相对误差达到 5% 以上的像素占比,RAFT 在 KITTI 2015 上的 Fl-all 为 5.10%。训练方面的标准做法是两阶段:先在 Flying Chairs、Flying Things 3D 等合成数据上预训练,再到 Sintel、KITTI 等真实数据上微调。
实现指南 - 使用 OpenCV 和 RAFT 进行运动估计
从 OpenCV 经典方法到 RAFT 深度学习方法的具体实现代码和部署指南。
OpenCV 稀疏光流 (Lucas-Kanade):
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)next_gray = cv2.cvtColor(next_frame, cv2.COLOR_BGR2GRAY)points = cv2.goodFeaturesToTrack(prev_gray, 100, 0.3, 7)new_points, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, next_gray, points, None)
OpenCV 稠密光流 (Farneback):
flow = cv2.calcOpticalFlowFarneback(prev_gray, next_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)# flow.shape = (H, W, 2), flow[...,0]=水平, flow[...,1]=垂直
RAFT 部署:
- 安装:
pip install torch torchvision,克隆 RAFT 仓库 - 预训练模型:raft-things.pth (通用), raft-sintel.pth (动画风格)
- 推理:输入两帧 RGB 图像,输出稠密光流场
- GPU 推理速度:1080p 约 100-200ms (RTX 3090)
实时光流技巧:降低输入分辨率 (1/2 或 1/4) 可大幅提速;使用 RAFT-Small 变体;TensorRT 优化可提速 2-3 倍。对于实时应用,Farneback 在 CPU 上仍是实用选择。
Farneback 与 Lucas-Kanade 的常用参数:稠密光流的 Farneback 方法常用参数为 pyr_scale=0.5、levels=3、winsize=15、iterations=3、poly_n=5。稀疏追踪时要检测追踪失败,做法是再算一次反向光流做前后一致性检查 (forward-backward check),剔除往返误差超过阈值的点。
RAFT 的推理与选型:RAFT 官方实现基于 PyTorch,可用 torchvision.models.optical_flow.raft_large() 载入预训练模型,输入两张形状为 1x3xHxW 的图像张量。选型上,实时处理选 Farneback,适合视频特效与简易运动检测;离线追求精度选 RAFT-Large,适合视频编辑、研究用途与帧插值;嵌入式与移动端则选 LiteFlowNet 或 PWC-Net 的轻量版本。