目录
- 光流基础理论
- 经典光流方法
- 深度学习光流
- RAFT 系列
- 视频插帧
- 运动估计与跟踪
- 场景流
- 评估指标与数据集
- 应用与前沿
- 术语速查表
1. 光流基础理论
1.1 什么是光流
光流 (Optical Flow) 的定义: 图像中像素在相邻帧之间的位移向量场 设图像 I(x, y, t) 在时间 t 到 t+1 之间: 像素 (x, y) 移动到 (x + u, y + v) 光流场: F = (u, v) u: 水平位移 v: 垂直位移 ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 帧 t 帧 t+1 │ │ ┌──────────┐ ┌──────────┐ │ │ │ ●─────│──────────►│─────● │ │ │ │ (x,y) │ │ (x+u,y+v) │ │ │ │ │ │ │ │ └──────────┘ └──────────┘ │ │ │ │ 光流: F(x,y) = (u, v) │ │ │ └─────────────────────────────────────────────────────────────────┘ 光流的含义: 描述了场景中物体的运动 是视频分析的基础
1.2 光流约束方程
光流约束方程 (Optical Flow Constraint Equation): 假设亮度恒定: I(x, y, t) = I(x + u, y + v, t + 1) 泰勒展开: I(x + u, y + v, t + 1) ≈ I(x, y, t) + I_x·u + I_y·v + I_t 代入恒定假设: I_x·u + I_y·v + I_t = 0 或写成: ∇I · F + I_t = 0 其中: I_x = ∂I/∂x: 水平梯度 I_y = ∂I/∂y: 垂直梯度 I_t = ∂I/∂t: 时间梯度 F = (u, v): 光流 问题: 一个方程,两个未知数 (u, v) 存在孔径问题 (Aperture Problem)
1.3 孔径问题
孔径问题 (Aperture Problem): 问题: 只能估计梯度方向上的运动分量 无法估计垂直于梯度方向的运动 ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 孔径 (局部窗口): │ │ ┌─────────┐ │ │ │ ───── │ 只能看到水平边缘 │ │ │ │ 只能估计垂直运动 │ │ │ │ 无法估计水平运动 │ │ └─────────┘ │ │ │ │ 解决: 引入额外约束 │ │ 1. 平滑性约束 │ │ 2. 全局约束 │ │ 3. 特征匹配 │ │ │ └─────────────────────────────────────────────────────────────────┘
1.4 光流的类型
光流的类型: 1. 稀疏光流: 只计算特征点的运动 例: Lucas-Kanade 2. 稠密光流: 计算每个像素的运动 例: Horn-Schunck, RAFT 3. 半稠密光流: 计算部分像素的运动 例: 梯度较大的区域
2. 经典光流方法
2.1 Lucas-Kanade 方法
论文: "An Iterative Image Registration Technique with an Application to Stereo Vision" (Lucas & Kanade, 1981) 核心思想: 局部窗口内的运动是恒定的 在窗口内求解超定方程组 假设: 窗口内所有像素有相同的运动 (u, v) 对于窗口内的每个像素 i: I_x_i · u + I_y_i · v + I_t_i = 0 写成矩阵形式: [I_x_1, I_y_1] [u] [-I_t_1] [I_x_2, I_y_2] [v] = [-I_t_2] [ ..., ] [ ... ] 最小二乘解: [u, v]^T = (A^T A)^{-1} A^T b
importnumpyasnpclassLucasKanade:""" Lucas-Kanade 光流 理论: 局部窗口内运动恒定 最小二乘求解 假设: 小运动 (线性化成立) 局部恒定运动 """def__init__(self,window_size=15,num_iterations=3):self.window_size=window_size self.num_iterations=num_iterationsdefcompute_flow(self,image1,image2,points):""" 计算稀疏光流 image1: [H, W] 帧1 image2: [H, W] 帧2 points: [N, 2] 特征点坐标 返回: [N, 2] 位移向量 """# 计算梯度Ix,Iy=self.compute_gradients(image1)It=image2-image1 flows=[]half_w=self.window_size//2forx,yinpoints:x,y=int(x),int(y)# 提取窗口Ix_win=Ix[y-half_w:y+half_w+1,x-half_w:x+half_w+1].flatten()Iy_win=Iy[y-half_w:y+half_w+1,x-half_w:x+half_w+1].flatten()It_win=It[y-half_w:y+half_w+1,x-half_w:x+half_w+1].flatten()# 构建方程组A=np.stack([Ix_win,Iy_win],axis=1)b=-It_win# 最小二乘解flow=np.linalg.lstsq(A,b,rcond=None)[0]flows.append(flow)returnnp.array(flows)defcompute_gradients(self,image):"""计算图像梯度"""Ix=np.gradient(image,axis=1)Iy=np.gradient(image,axis=0)returnIx,Iy""" Lucas-Kanade 的理论: 最小二乘解: [u, v]^T = (A^T A)^{-1} A^T b 矩阵 A^T A 的特征值: λ₁, λ₂: 反映梯度分布 两个特征值都大: 角点 (可以估计两个方向) 一个大一个小: 边缘 (只能估计一个方向) 都小: 纹理 (无法估计) Shi-Tomasi 角点检测: min(λ₁, λ₂) > threshold 选择好的特征点进行跟踪 """
2.2 Horn-Schunck 方法
论文: "Determining Optical Flow" (Horn & Schunck, 1981) 核心思想: 全局平滑性约束 假设光流场是平滑的 目标函数: E = ∫∫ [(I_x·u + I_y·v + I_t)² + α²(‖∇u‖² + ‖∇v‖²)] dx dy 第一项: 光流约束 (数据项) 第二项: 平滑性约束 (正则项) α: 平滑权重
classHornSchunck:""" Horn-Schunck 光流 理论: 全局平滑性约束 假设光流场是平滑的 目标函数: E = (I_x·u + I_y·v + I_t)² + α²(‖∇u‖² + ‖∇v‖²) """def__init__(self,alpha=1.0,num_iterations=100):self.alpha=alpha self.num_iterations=num_iterationsdefcompute_flow(self,image1,image2):""" 计算稠密光流 image1: [H, W] image2: [H, W] 返回: [H, W, 2] """# 计算梯度Ix,Iy=self.compute_gradients(image1)It=image2-image1 H,W=image1.shape u=np.zeros((H,W))v=np.zeros((H,W))# 迭代求解for_inrange(self.num_iterations):# 邻域平均u_avg=self.neighborhood_average(u)v_avg=self.neighborhood_average(v)# 更新numerator=Ix*u_avg+Iy*v_avg+It denominator=self.alpha**2+Ix**2+Iy**2u=u_avg-Ix*numerator/denominator v=v_avg-Iy*numerator/denominatorreturnnp.stack([u,v],axis=-1)defneighborhood_average(self,f):"""邻域平均"""fromscipy.ndimageimportuniform_filterreturnuniform_filter(f,size=3)defcompute_gradients(self,image):Ix=np.gradient(image,axis=1)Iy=np.gradient(image,axis=0)returnIx,Iy""" Horn-Schunck 的迭代公式: u = ū - Ix·(Ix·ū + Iy·v̄ + It) / (α² + Ix² + Iy²) v = v̄ - Iy·(Ix·ū + Iy·v̄ + It) / (α² + Ix² + Iy²) 其中 ū, v̄ 是邻域平均 α 的作用: α 大: 更平滑 (全局运动) α 小: 更精确 (局部运动) """
3. 深度学习光流
3.1 FlowNet
论文: "FlowNet: Learning Optical Flow with Convolutional Networks" (Fischer et al., 2015) 核心思想: 用 CNN 直接预测光流 输入: 两帧图像 输出: 光流场 架构: 1. FlowNetS: 简单拼接 2. FlowNetC: 相关层 (Correlation)
importtorchimporttorch.nnasnnclassFlowNetS(nn.Module):""" FlowNetS (Simple) 理论: 直接拼接两帧图像 CNN 预测光流 输入: [B, 6, H, W] (两帧拼接) 输出: [B, 2, H, W] (光流) """def__init__(self):super().__init__()# 编码器self.encoder=nn.Sequential(nn.Conv2d(6,64,7,stride=2,padding=3),nn.LeakyReLU(0.1),nn.Conv2d(64,128,5,stride=2,padding=2),nn.LeakyReLU(0.1),nn.Conv2d(128,256,5,stride=2,padding=2),nn.LeakyReLU(0.1),nn.Conv2d(256,256,3,stride=1,padding=1),nn.LeakyReLU(0.1),nn.Conv2d(256,512,3,stride=2,padding=1),nn.LeakyReLU(0.1),nn.Conv2d(512,512,3,stride=1,padding=1),nn.LeakyReLU(0.1),nn.Conv2d(512,1024,3,stride=2,padding=1),nn.LeakyReLU(0.1))# 解码器self.decoder=nn.Sequential(nn.ConvTranspose2d(1024,512,4,stride=2,padding=1),nn.LeakyReLU(0.1),nn.ConvTranspose2d(512,256,4,stride=2,padding=1),nn.LeakyReLU(0.1),nn.ConvTranspose2d(256,128,4,stride=2,padding=1),nn.LeakyReLU(0.1),nn.ConvTranspose2d(128,64,4,stride=2,padding=1),nn.LeakyReLU(0.1),nn.Conv2d(64,2,3,padding=1))defforward(self,image1,image2):""" image1: [B, 3, H, W] image2: [B, 3, H, W] 返回: [B, 2, H, W] """# 拼接x=torch.cat([image1,image2],dim=1)# 编码features=self.encoder(x)# 解码flow=self.decoder(features)returnflow""" FlowNet 的理论: FlowNetS: 简单拼接 CNN 自动学习如何匹配 FlowNetC: 显式计算相关 更高效的匹配 两者都使用编码器-解码器结构 多尺度预测 """
3.2 RAFT
论文: "RAFT: Recurrent All-Pairs Field Transforms for Optical Flow" (Teed & Deng, 2020) 核心创新: 1. 全对相关: 计算所有像素对的相关 2. 迭代优化: GRU 迭代更新光流 3. 高精度: 超过之前所有方法 ┌─────────────────────────────────────────────────────────────────┐ │ │ │ RAFT 架构: │ │ │ │ 图像 → 特征提取 → 全对相关 → GRU 迭代 → 光流 │ │ │ │ 1. 特征提取: 提取两帧的特征 │ │ 2. 全对相关: 计算所有像素对的相关 │ │ 3. GRU 迭代: 迭代更新光流 │ │ 4. 上采样: 恢复原始分辨率 │ │ │ └─────────────────────────────────────────────────────────────────┘
classRAFT(nn.Module):""" RAFT (Recurrent All-Pairs Field Transforms) 核心创新: 1. 全对相关: 计算所有像素对的相关 2. GRU 迭代: 迭代更新光流 3. 高精度 理论: 全对相关: 捕获所有可能的匹配 GRU 迭代: 逐步优化光流 """def__init__(self,hidden_dim=128,num_iterations=12):super().__init__()self.hidden_dim=hidden_dim self.num_iterations=num_iterations# 特征提取器self.feature_encoder=FeatureEncoder(output_dim=256)self.context_encoder=ContextEncoder(output_dim=hidden_dim)# 相关金字塔self.corr_pyramid=CorrPyramid(num_levels=4)# GRU 更新self.update_block=UpdateBlock(hidden_dim=hidden_dim)defforward(self,image1,image2,iters=None):""" image1: [B, 3, H, W] image2: [B, 3, H, W] 返回: 光流列表 (每次迭代的光流) """ifitersisNone:iters=self.num_iterations# 特征提取fmap1=self.feature_encoder(image1)fmap2=self.feature_encoder(image2)# 上下文特征context=self.context_encoder(image1)# 全对相关corr=self.compute_correlation(fmap1,fmap2)corr_pyramid=self.corr_pyramid(corr)# 初始化光流B,_,H,W=fmap1.shape flow=torch.zeros(B,2,H,W,device=image1.device)# GRU 隐藏状态h=torch.tanh(context[:,:self.hidden_dim])c=context[:,self.hidden_dim:]# 迭代更新flow_list=[]for_inrange(iters):# 查找相关corr_features=self.lookup_correlation(corr_pyramid,flow)# GRU 更新h,delta_flow=self.update_block(h,flow,corr_features)# 更新光流flow=flow+delta_flow flow_list.append(flow)returnflow_listdefcompute_correlation(self,fmap1,fmap2):""" 计算全对相关 理论: 对于 fmap1 中的每个位置 计算与 fmap2 中所有位置的相关 C(i,j) = fmap1[i] · fmap2[j] """B,C,H,W=fmap1.shape fmap1=fmap1.view(B,C,H*W)fmap2=fmap2.view(B,C,H*W)corr=torch.bmm(fmap1.transpose(1,2),fmap2)corr=corr.view(B,H,W,H,W)returncorr""" RAFT 的理论优势: 1. 全对相关: 捕获所有可能的匹配 不受局部窗口限制 2. 迭代优化: 逐步优化光流 每次迭代可以修正错误 3. 高精度: 在多个基准上达到 SOTA """
4. 视频插帧
4.1 基于光流的插帧
classFlowBasedInterpolation(nn.Module):""" 基于光流的视频插帧 理论: 1. 估计光流 2. 变形两帧到中间时刻 3. 融合得到中间帧 """def__init__(self,flow_model):super().__init__()self.flow_model=flow_modeldefforward(self,frame1,frame2,t=0.5):""" frame1: [B, 3, H, W] frame2: [B, 3, H, W] t: 插值时刻 (0-1) """# 估计光流flow_forward=self.flow_model(frame1,frame2)flow_backward=self.flow_model(frame2,frame1)# 变形warped1=self.warp(frame1,flow_forward*t)warped2=self.warp(frame2,flow_backward*(1-t))# 融合weight=t interpolated=weight*warped1+(1-weight)*warped2returninterpolateddefwarp(self,image,flow):"""使用光流变形图像"""B,C,H,W=image.shape# 生成网格grid_y,grid_x=torch.meshgrid(torch.arange(H),torch.arange(W),indexing='ij')grid=torch.stack([grid_x,grid_y],dim=0).float()# 加上光流grid=grid+flow# 归一化到 [-1, 1]grid[:,0]=2*grid[:,0]/(W-1)-1grid[:,1]=2*grid[:,1]/(H-1)-1grid=grid.permute(0,2,3,1)# 双线性采样warped=F.grid_sample(image,grid,mode='bilinear',padding_mode='border',align_corners=True)returnwarped""" 基于光流插帧的问题: 1. 光流误差: 光流估计不准确 导致变形错误 2. 遮挡: 被遮挡的区域没有对应 需要特殊处理 3. 模糊: 变形后的融合可能模糊 """
5. 运动估计与跟踪
5.1 特征跟踪
classFeatureTracker:""" 特征跟踪器 使用光流跟踪特征点 理论: 检测特征点 使用光流跟踪 丢失的点重新检测 """def__init__(self,detector='shi_tomasi'):self.detector=detector self.points=Nonedefdetect(self,image):"""检测特征点"""importcv2 points=cv2.goodFeaturesToTrack(image,maxCorners=100,qualityLevel=0.01,minDistance=10)returnpointsdeftrack(self,image1,image2):"""跟踪特征点"""importcv2ifself.pointsisNone:self.points=self.detect(image1)# 光流跟踪new_points,status,_=cv2.calcOpticalFlowPyrLK(image1,image2,self.points,None)# 保留成功跟踪的点good_mask=status.flatten()==1self.points=new_points[good_mask].reshape(-1,1,2)# 如果点太少,重新检测iflen(self.points)<50:new_detections=self.detect(image2)ifnew_detectionsisnotNone:self.points=np.vstack([self.points,new_detections])returnself.points""" 特征跟踪的流程: 1. 检测: 在第一帧检测特征点 2. 跟踪: 使用光流跟踪到下一帧 3. 更新: 丢失的点重新检测 4. 重复: 持续跟踪 """
6. 场景流
6.1 场景流概述
场景流 (Scene Flow): 3D 空间中的运动场 与光流的区别: 光流: 2D 图像平面的运动 场景流: 3D 空间的运动 场景流 = 3D 位移向量场 F = (u, v, w) u, v: 对应光流 w: 深度变化 应用: 1. 自动驾驶: 3D 运动估计 2. 机器人: 抓取运动规划 3. 3D 重建: 动态场景重建
7. 评估指标与数据集
7.1 评估指标
┌─────────────────────────────────────────────────────────────────────┐ │ 光流评估指标 │ ├─────────────────┬───────────────────────────────────────────────────┤ │ 指标 │ 定义与说明 │ ├─────────────────┼───────────────────────────────────────────────────┤ │ EPE │ End-Point Error │ │ │ EPE = ‖F_pred - F_gt‖₂ │ │ │ 预测光流与真实光流的欧氏距离 │ ├─────────────────┼───────────────────────────────────────────────────┤ │ AEE │ Average End-Point Error │ │ │ 所有像素的平均 EPE │ ├─────────────────┼───────────────────────────────────────────────────┤ │ Fl │ Flow Accuracy │ │ │ EPE < 3 或 EPE < 5% 的像素比例 │ └─────────────────┴───────────────────────────────────────────────────┘
7.2 数据集
┌─────────────────────────────────────────────────────────────────────┐ │ 光流数据集 │ ├─────────────────┬───────────────────────────────────────────────────┤ │ 数据集 │ 说明 │ ├─────────────────┼───────────────────────────────────────────────────┤ │ FlyingChairs │ 合成数据,椅子在背景上移动 │ │ FlyingThings3D │ 合成 3D 场景 │ │ Sintel │ 合成电影场景,高质量 │ │ KITTI │ 真实驾驶场景 │ │ Middlebury │ 室内场景,高精度 │ └─────────────────┴───────────────────────────────────────────────────┘
8. 应用与前沿
8.1 应用领域
光流与运动分析的应用: 1. 视频压缩: 利用运动信息压缩视频 只传输运动和残差 2. 视频稳定: 估计相机运动 补偿抖动 3. 动作识别: 运动信息识别动作 光流作为特征 4. 目标跟踪: 跟踪物体运动 光流辅助跟踪 5. 自动驾驶: 障碍物运动估计 碰撞预警 6. 视频插帧: 生成慢动作视频 提升视频帧率
8.2 前沿方向
光流与运动分析的前沿: 1. 实时光流: 高效模型 边缘设备部署 2. 高分辨率光流: 4K/8K 视频 大位移 3. 3D 场景流: 结合深度估计 3D 运动理解 4. 事件相机光流: 新型传感器 高时间分辨率
9. 术语速查表
| 术语 | 英文 | 定义 |
|---|
| 光流 | Optical Flow | 像素在相邻帧之间的位移 |
| 稠密光流 | Dense Flow | 每个像素的运动 |
| 稀疏光流 | Sparse Flow | 特征点的运动 |
| 场景流 | Scene Flow | 3D 空间的运动场 |
| 孔径问题 | Aperture Problem | 局部无法确定完整运动 |
| 亮度恒定 | Brightness Constancy | 光流的基本假设 |
| 相关层 | Correlation Layer | 计算特征相似度 |
| 迭代优化 | Iterative Refinement | 逐步优化光流 |
| 视频插帧 | Frame Interpolation | 生成中间帧 |
| 运动估计 | Motion Estimation | 估计物体运动 |
附录
A. 发展时间线
1981 ──┬── Lucas-Kanade, Horn-Schunck │ 2003 ──┼── Farneback │ 2015 ──┼── FlowNet │ 2017 ──┼── FlowNet2 │ 2020 ──┼── RAFT │ 2023+ ──┴── 光流基础模型
B. 核心公式速查
| 公式 | 含义 |
|---|
| I_x·u + I_y·v + I_t = 0 | 光流约束方程 |
| [u,v]^T = (A^T A)^{-1} A^T b | Lucas-Kanade 解 |
| C(i,j) = f₁[i]·f₂[j] | 相关计算 |
| EPE = ‖F_pred - F_gt‖₂ | 端点误差 |