1.介绍
- 光流的概念是指在连续的两帧图像中由于图像中的物体移动或者摄像头的移动导致的图像中目标像素的移动
- 由观察者和场景之间的相对运动引起的视觉场景中物体表面和边缘的明显运动模式
- 光流是二维矢量场,表示了一个点从第一帧到第二帧的位移
- 上面的图表示了一个球在连续的5帧图像中的运动。箭头表示了它的位移矢量
光流法的工作原理基于如下假设:
- 场景的像素强度在相邻帧之间基本不变
- 相邻像素具有相似的运动
光流问题涉及尝试找出一幅图像中的许多点在第二幅图像中移动的位置–通常是以视频序列完成的,因此可以假定第一幅图像中的大部分点框架都可以在第二幅图像中找到。
光流可以用于场景中物体的运动估计,设置用于相机相对于整个场景的自运动估计。
根据是否选取图像稀疏点进行光流估计,可以将光流估计分为稀疏光流和稠密光流,如图,左图选取了一些特征明显(梯度较大)的点进行光流估计和跟踪,右图为连续帧稠密光流示意图。
稠密光流描述图像每个像素向下一帧运动的光流,为了方便表示,使用不同的颜色和亮度表示光流的大小和方向,如图右图的不同颜色。图展示了一种光流和颜色的映射关系,使用颜色表示光流的方向,亮度表示光流的大小。
2. 传统算法
最为常用的视觉算法库OpenCV中,提供光流估计算法接口,包括稀疏光流估计算法cv2.calcOpticalFlowPyrLK(),和稠密光流估计cv2.calcOpticalFlowFarneback()。
2.1 稀疏光流-Lucas-Kanade算法
- Lucas–Kanade光流算法是一种两帧差分的光流估计算法。它由Bruce D. Lucas 和 Takeo Kanade提出
- Lucas B D, Kanade T. An iterative image registration technique with
an application to stereo vision[J]. 1981.
其中稀疏光流估计算法为Lucas-Kanade算法,该算法为1981年由Lucas和Kanade两位科学家提出的,最为经典也较容易理解的算法
LK算法是一种两帧差分的光流估计算法,其基本思想基于以下三个假设。
- 亮度恒定:就是同一点随着时间的变化,其亮度不会发生改变。这是基本光流法的假定(所有光流法变种都必须满足),用于得到光流法基本方程。对于灰度图像(对于彩色图像同适用)这意味着像素的灰度值不会随着帧的跟踪改变。
- 时间持续性(微小移动):就是时间的变化不会引起位置的剧烈变化,这样灰度才能对位置求偏导(换句话说,小运动情况下我们才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数),这也是光流法不可或缺的假定。图像上相机的移动随时间变化缓慢。实际上,这意味着时间的变化不会引起像素位置的剧烈变化,这样像素的灰度值才能对位置求对应的偏导数。
- 空间一致性:场景中相同表面的相邻点具有相似的运动,并且其投影到图像平面上的距离也比较近。一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。这是Lucas-Kanade光流法特有的假定,因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。我们假定特征点邻域内做相似运动,就可以连立n多个方程求取x,y方向的速度(n为特征点邻域总点数,包括该特征点)
三个基本假设中前两个是光流法的基本假设,第三个是LK算法特有的。
2.1.1 算法原理
待补充????
2.1.2 算法改进
- LK算法的约束条件即:小速度,亮度不变以及区域一致性都是较强的假设,并不很容易得到满足。如当物体运动速度较快时,假设不成立,那么后续的假设就会有较大的偏差,使得最终求出的光流值有较大的误差
Jean-Yves Bouguet提出一种基于金字塔分层,针对仿射变换的改进Lucas-Kanade算法构建图像金字塔可以解决大运动目标跟踪,也可以一定程度上解决孔径问题(相同大小的窗口能覆盖大尺度图片上尽量多的角点,而这些角点无法在原始图片上被覆盖)
-
主要思想
-
考虑物体的运动速度较大时,算法会出现较大的误差。那么就希望能减少图像中物体的运动速度。一个直观的方法就是,缩小图像的尺寸。假设当图像为400×400时,物体速度为[16,16],那么图像缩小为200×200时,速度变为[8,8]。缩小为100*100时,速度减少到[4,4]。所以光流可以通过生成
原图像的金字塔图像,逐层求解,不断精确来求得 -
上层金字塔(低分辨率)中的一个像素可以代表下层的两个
-
对于Lucas-Kanade改进算法来说,主要的步骤有三步:建立金字塔,基于金字塔跟踪,迭代过程
-
Bouguet J. Pyramidal Implementation of the AÆne Lucas Kanade Feature
Tracker Description of the Algorithm, Intel
Corporation–Microprocessor Research Labs, 2000[J].
除了基于亮度不变假设和邻域光流相似假设,为了解决图像偏移较大的情况,Lucas-Kanade算法还借助了图像金字塔(Pyramid)的方式,在高层低分辨率图像上,大的偏移将变为小的偏移。
- 建立金字塔
首先需要对原始图像建立金字塔,其中原始图像位于底层,其上每一层均基于上一层进行计算。
金字塔中每一层均是上一层的下采样,其计算公式为:
其中IL(x,y)为 L 层图像中 x, y 位置所在像素点的灰度值,其相当于通过[0.25 0.5 0.25]的低通滤波器进行迭代计算。
- 迭代追踪
计算光流使用顶层(Lm)层开始,通过最小化每个点领域范围内的匹配误差和,得到每个顶层图像中每个点的光流,该步骤同LK光流法。
假设图像的尺寸每次缩放为原来的一半,一共缩放了Lm层,则第0层为原始图像,设已知原图的位移为u,则每一层的位移可以表示为:
所以顶层的光流计算结果,也就是位移,反应到Lm-1层,作为该层初始时的光流值的估计g表示为:
沿着金字塔向下反馈,重复估计每一层的位移,直到最底层也就是原始图像计算像素的位移。
可以理解为 :
准确值=估计值+残差,
对于每一层L,每个点的光流的计算都是基于邻域内所有点的匹配误差和最小化。
由于顶层图像尺寸较小,其初始的光流估计量可以设置为0,即 gL = [0, 0] 。
- 迭代过程
介绍了对于整个金字塔迭代过程,还需要提供对于每一层的残余光流计算方法。
求解光流最重要的是最小化上文提到的残差方程:
最终,Lucas-Kanade方法给出了一种求解稀疏(明显特征的角点)光流的方法。
2.1.3 OpenCV 中的 LK 光流
OpenCV 库提供函数 cv.calcOpticalFlowPyrLK()-
一个简单应用示例:
我们要跟踪视频中某些点,
先使用 cv.goodFeaturesToTrack() 来获取这些点-
首先取第一帧,检测其中的 Shi-Tomasi 角点,然后使用 Lucas-Kanade 算法来迭代地跟踪这些特征点。
迭代的方式就是向 cv.calcOpticalFlowPyrLK() 函数传入上一帧图片和其中的特征点以及当前帧图片,
函数会返回当前帧的特征点,每个点都带有一个状态值(0 或 1),
如果在当前帧找到了上一帧中的点,这个点的状态值就是 1,否则就是 0。
将状态值为 1 的点作为下次特征点的输入,不停迭代
nextPts, status, err = cv.calcOpticalFlowPyrLK(
prevImg, nextImg, prevPts, nextPts[, status[, err[, winSize[, maxLevel[, criteria[, flags[, minEigThreshold]]]]]]])
- 返回值:
- nextPts:二维点的输出矢量(具有单精度浮点坐标),包含第二图像中输入特征的计算新位置; 当传递
OPTFLOW_USE_INITIAL_FLOW 标志时,向量必须与输入中的大小相同 - status:状态值(无符号字符型)
- err:向量中的每个特征对应的错误率
- 输入值:
- prevImg:上一帧图片
- nextImg:当前帧图片
- prevPts:上一帧找到的特征点向量
- winSize:在图像金字塔中计算局部连续运动的窗口的尺寸
- maxLevel:图像金字塔层数,0 代表不使用金字塔(单极)
- criteria:指定迭代搜索算法的终止条件(在指定的最大迭代次数criteria.maxCount之后或当搜索窗口移动小于criteria.epsilon时)
- flags:选择计算方法:
OPTFLOW_USE_INITIAL_FLOW:使用初始估计,存储在nextPts中; 如果未设置标志,则将prevPts复制到nextPts并将其视为初始估计
OPTFLOW_LK_GET_MIN_EIGENVALS:使用最小特征值作为误差测量; 如果未设置标志,则将原始位置和移动点之间的色块之间的 L1 距离除以窗口中的像素数,用作误差测量
minEigThreshold:该算法计算光流方程的2×2正常矩阵的最小特征值,除以窗口中的像素数,如果此值小于 minEigThreshold,则会过滤掉相应的功能并且不会处理该光流,因此它允许删除坏点并获得性能提升
实现原理: - 在第一帧图像中检测Shi-Tomasi角点, - 使用LK算法来迭代的跟踪这些特征点。迭代的方式就是不断向cv2.calcOpticalFlowPyrLK()中传入上一帧图片的特征点以及当前帧的图片。 - 函数会返回当前帧的点,这些点带有状态1或者0,如果在当前帧找到了上一帧中的点,那么这个点的状态就是1,否则就是0。
实现流程:
- 加载视频。
- 调用cv2.GoodFeaturesToTrack 函数寻找兴趣点(关键点)。
- 调用cv2.CalcOpticalFlowPyrLK 函数计算出两帧图像中兴趣点的移动情况。
- 删除未移动的兴趣点。
- 在两次移动的点之间绘制一条线段。
2.2 稠密光流——Farneback
Farneback稠密光流的主要思想是利用多项式对每个像素的邻域信息进行近似表示,例如考虑二次多项式。
A是对称矩阵,b是向量,c为标量,~表示像素邻域信息的近似
A是通过像素的邻域信息的最小二乘加权拟合得到的,权重系数与邻域的像素大小和位置有关。
如前一帧图像用
表示,两帧图像唯一用d表示,那么
等价于
因为图像场景中像素的外观信息在帧间运动不变,可以得到对应系数相同,如果A1非奇异,则
再经过对误差的优化和调整结合图像金字塔对图像中的特征点进行跟踪,稠密光流的大致流程就算完事了。
下面样例显示如何找到稠密光流,我们得到的一个两个通道的向量(u,v)。得到的该向量的大小和方向。用不同的颜色编码来使其可视化。
2.2.1 OpenCV 中的 稠密光流
Lucas-Kanade方法计算稀疏特征集的光流,OpenCV提供了另一种算法来查找密集的光流,它计算帧中所有点的光流。它基于Gunner Farneback于2003年的《Two-Frame Motion Estimation Based on Polynomial Expansion》。
我们可以通过Opencv的函数cv2.calcOpticalFlowFarneback寻找稠密光流,我们得到的一个两个通道的向量(u,v)。得到的该向量的大小和方向。用不同的颜色编码来使其可视化。
方向与Hue值相关,大小与Value值相关。
使用calcOpticalFlowFarneback函数得到:
flow=cv.calcOpticalFlowFarneback
(prev,
next,
flow,
pyr_scale,
levels,
winsize,
iterations,
poly_n,
poly_sigma,
flags)
返回值是每个像素点的位移
- prev–> 前一帧图片
- next–> 下一帧图片,格式与prev相同
- flow–> 与返回值相同,得到一个CV_32FC2格式的光流图
- pyr_scale–> 构建图像金字塔尺度
- levels–> 图像金字塔层数
- winsize–> 窗口尺寸,值越大探测高速运动的物体越容易,但是越模糊,同时对噪声的容错性越强
- iterations–> 对每层金字塔的迭代次数
- poly_n–> 每个像素中找到多项式展开的邻域像素的大小。n越大越光滑,也越稳定
- poly_sigma–> 高斯标准差,用来平滑倒数,n越大,sigma应该适当增加
- flags–> 光流的方式,有OPTFLOW_USE_INITIAL_FLOW 和OPTFLOW_FARNEBACK_GAUSSIAN 两种
3. 深度学习算法 FlowNet/FlowNet2.0
FlowNet的作者于2015年首先使用CNN解决光流估计问题,取得了较好的结果,并且在CVPR2017上发表改进版本FlowNet2.0,成为当时State-of-the-art的方法。截止到现在,FlowNet和FlowNet2.0依然和深度学习光流估计算法中引用率最高的论文,分别引用790次和552次。因此,深度学习光流估计算法将以FlowNet/FlowNet2.0为例介绍。
ICCV2015提出的FlowNet是最早使用深度学习CNN解决光流估计问题的方法,并且在CVPR2017,同一团队提出了改进版本FlowNet2.0。FlowNet2.0 是2015年以来光流估计邻域引用最高的论文。
3.1 FlowNet
待补充???
3.2 FlowNet2.0
待补充???
4. 应用场景
光流,从物理意义的角度看,描述了视频中物体、对象在时间维度上的关联性,从而建立了视频中连续图像之间的关联关系。
因此,最为直接而自然的应用就是视频中物体的跟踪,在物体跟踪领域知名的TLD算法便借助了光流估计,
在视觉里程计和SLAM同步定位与建图领域,光流可以作为图像特征点匹配的一种方式,比如知名的视觉惯性里程计开源算法VINS-Mono。英伟达也提供了基于其GPU的光流SDK,其中展示了利用光流进行视频动作识别(video action recognition)和视频插帧的应用,