在SLAM的视觉里程计中,比较常用的就是特征点法
和直接法
。而直接法中,光流则是其中的重点内容,比如LSD-SLAM中就使用到了光流的方法。本文将会就光流的理论原理、公式推导进行详细的剖析,以帮助读者深刻地理解。
光流算法
光流是关于视域中的物体运动检测中的概念,它用来描述相对于观察者的运动所造成的观测目标、表面或边缘。
简单来讲,光流描述了场景中物体运动在视觉中的变化。光流的概念,由Gibson在1950年提出,其通过相邻帧之间像素点的对应关系计算像素点的瞬时速度,从而描述物体信息。
为了应用光流计算物体运动法,相邻帧需要满足两个假设:
- 灰度不变:两个间隔帧之间描述相同目标的像素点的灰度需要保持恒定;
- 运动较小:两个间隔帧之间描述相同目标的像素点并没有发生剧烈变化;
假设图像上一个像素点 ( x , y ) (x, y) (x,y),它在时刻 t t t的亮度为 I ( x , y , t ) I(x, y, t) I(x,y,t),用 u ( x , y ) u(x, y) u(x,y)和 v ( x , y ) v(x, y) v(x,y)表示该点光流在水平和垂直方向上的速度分量:
u = d x d t u=\frac{\mathrm{d} x}{\mathrm{d} t} u=dtdx
u = d y d t u=\frac{\mathrm{d} y}{\mathrm{d} t} u=dtdy
在经过时间间隔 d t dt dt之后,该点的对应点的亮度变为 I ( x + d x , y + d y , t + d t ) I(x+dx, y+dy, t+dt) I(x+dx,y+dy,t+dt).
根据假设一:
I ( x + d x , y + d y , t + d t ) = I ( x , y , t ) I(x+dx, y+dy, t+dt)=I(x, y, t) I(x+dx,y+dy,t+dt)=I(x,y,t)
根据假设二,利用泰勒公式展开:
I ( x + d x , y + d y , t + d t ) ≈ I ( x , y , t ) + ∂ I ∂ x d x + ∂ I ∂ y d y + ∂ I ∂ t d t I(x+dx, y+dy, t+dt) \approx I(x, y, t) + \frac{\partial I}{\partial x} dx + \frac{\partial I}{\partial y} dy + \frac{\partial I}{\partial t} dt I(x+dx,y+dy,t+dt)≈I(x,y,t)+∂x∂Idx+∂y∂Idy+∂t∂Idt
根据上面两个式子,可以得到:
∂ I ∂ x d x + ∂ I ∂ y d y + ∂ I ∂ t d t = 0 \frac{\partial I}{\partial x} dx + \frac{\partial I}{\partial y} dy + \frac{\partial I}{\partial t} dt = 0 ∂x∂Idx+∂y∂Idy+∂t∂Idt=0
当 d t dt dt足够小,趋向于0时:
∂ I ∂ x d x d t + ∂ I ∂ y d y d t + ∂ I ∂ t = 0 \frac{\partial I}{\partial x} \frac{dx}{dt} + \frac{\partial I}{\partial y} \frac{dy}{dt} + \frac{\partial I}{\partial t} = 0 ∂x∂Idtdx+∂y∂Idtdy+∂t∂I=0
− ∂ I ∂ t = ∂ I ∂ x d x d t + ∂ I ∂ y d y d t = ∂ I ∂ x u + ∂ I ∂ y v -\frac{\partial I}{\partial t} = \frac{\partial I}{\partial x} \frac{dx}{dt} + \frac{\partial I}{\partial y} \frac{dy}{dt} = \frac{\partial I}{\partial x} u + \frac{\partial I}{\partial y} v −∂t∂I=∂x∂Idtdx+∂y∂Idtdy=∂x∂Iu+∂y∂Iv
− I t = I x d x d t + I y d y d t = I x u + I y v -I_{t} = I_{x} \frac{dx}{dt} + I_{y} \frac{dy}{dt} = I_{x} u + I_{y} v −It=Ixdtdx+Iydtdy=Ixu+Iyv
− I t = [ I x I y ] [ u v ] -I_{t} = \begin{bmatrix} I_{x} & I_{y} \end{bmatrix} \begin{bmatrix} u \\ v \end{bmatrix} −It=[IxIy][uv]
其中, I x I_{x} Ix和 I y I_{y} Iy为图像在 ( x , y ) (x, y) (x,y)处的梯度, I t I_{t} It为图像在 ( x , y ) (x, y) (x,y)处关于时间 t t t的导数, I t ≈ I ( x , y , t ) − I ( x , y , t − 1 ) I_{t} \approx I(x, y, t) - I(x, y, t-1) It≈I(x,y,t)−I(x,y,t−1)。
这就是基本的光流约束方程。
不过对于某一个像素点而言,存在两个未知量 ( u , v ) (u, v) (u,v),但只有一个方程是无法求解的。
先小结一下光流的两个假设:
- 灰度不变:图像场景中的目标的像素在帧间运动时灰度保持不变。用于得到光流法基本方程;
- 运动较小:保证灰度可以对位置求偏导(换句话说,小运动情况下我们才能用前后顿之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数),这也是光流法不可或缺的假定。
LK光流
LK光流在基本的光流算法基础上增加了一个假设:
- 邻域相同:每个像素点的局部邻域内的所有像素点的光流保持一致。
LK光流证法一
由于基本的光流约束方程中,一个方程两个未知量没法求解。LK光流算法考虑到了像素点的邻域,将问题转变成了计算某些点集的光流,联立多个方程,从而解决了这个问题。
假设在邻域 R R R内 n n n个像素点,光流保持一致,可以得到:
− I t k = [ I x k I y k ] [ u v ] , k ∈ R -I_{tk} = \begin{bmatrix} I_{xk} & I_{yk} \end{bmatrix} \begin{bmatrix} u \\ v \end{bmatrix},k\in R −Itk=[IxkIyk][uv],k∈R
即:
[ I x 1 I y 1 I x 2 I y 2 . . . . . . I x n I y n ] [ u v ] = [ − I t 1 − I t 2 . . . − I t n ] \begin{bmatrix} I_{x1} & I_{y1} \\ I_{x2} & I_{y2} \\ ... & ... \\ I_{xn} & I_{yn} \end{bmatrix} \begin{bmatrix} u \\ v \end{bmatrix} = \begin{bmatrix} -I_{t1} \\ -I_{t2} \\ ... \\ -I_{tn} \end{bmatrix} Ix1Ix2...IxnIy1Iy2...Iyn [uv]= −It1−It2...−Itn
为了简单起见,记作:
A [ u v ] = b A\begin{bmatrix} u \\ v \end{bmatrix} = b A[uv]=b
这是一个超正定方程,一种解法就是计算其最小二乘解。即,当 A T A A^{T}A ATA可逆时:
[ u v ] = ( A T A ) − 1 A T b \begin{bmatrix} u \\ v \end{bmatrix} = (A^{T}A)^{-1}A^{T}b [uv]=(ATA)−1ATb
带入:
[ u v ] = [ ∑ i = 1 n I x i 2 ∑ i = 1 n I x i I y i ∑ i = 1 n I x i I y i ∑ i = 1 n I y i 2 ] − 1 [ − ∑ i = 1 n I x i I t − ∑ i = 1 n I y i I t ] \begin{bmatrix} u \\ v \end{bmatrix} = \begin{bmatrix} \sum_{i=1}^{n} I_{xi}^{2} & \sum_{i=1}^{n} I_{xi}I_{yi} \\ \sum_{i=1}^{n} I_{xi}I_{yi} & \sum_{i=1}^{n} I_{yi}^{2} \end{bmatrix} ^{-1}\begin{bmatrix} -\sum_{i=1}^{n}I_{xi}I_{t} \\ -\sum_{i=1}^{n}I_{yi}I_{t} \end{bmatrix} [uv]=[∑i=1nIxi2∑i=1nIxiIyi∑i=1nIxiIyi∑i=1nIyi2]−1[−∑i=1nIxiIt−∑i=1nIyiIt]
一般来说,在图像中沿两个方向都有像素变化的区域(例如角点),其对应的 A T A A^{T}A ATA是可逆的;相反的,在灰度变化很小的区域,一般对应的 A T A A^{T}A ATA是不可逆的。
这限制了LK光流法的使用范围,这也是被称为稀疏光流法的主要原因。
LK光流证法二
假设相邻的两幅图像 I I I和 J J J,对于 I I I中像素点 u = [ u x u y ] T u=\begin{bmatrix} u_{x} & u_{y} \end{bmatrix}^{T} u=[uxuy]T,需要在图像 J J J中找到像素点 v = u + d = [ u x + d x u y + d y ] T v=u+d=\begin{bmatrix} u_{x}+d_{x} & u_{y}+d_{y} \end{bmatrix}^{T} v=u+d=[ux+dxuy+dy]T,使其最为相似(灰度值差別最小)。我们把 d = [ d x d y ] T d=\begin{bmatrix} d_{x} & d_{y} \end{bmatrix}^{T} d=[dxdy]T称为在 u u u处的光流。
当然为了求解,需要引入一个具有相同光流的邻域,其大小使用 w x w_{x} wx、 w y w_{y} wy两个参数表示。则求解 d d d转换为使下述目标函数取最小值的优化问题:
ε ( d ) = ε ( d x , d y ) = ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( I ( x , y ) − J ( x + d x , y + d y ) ) 2 \varepsilon (d)=\varepsilon (d_{x}, d_{y})=\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(I(x, y)-J(x+d_{x}, y+d_{y}))^{2} ε(d)=ε(dx,dy)=x=ux−wx∑ux+wxy=uy−wy∑uy+wy(I(x,y)−J(x+dx,y+dy))2
在最优解处导数为0,则得到:
∂ ε ( d ) ∂ d = − 2 ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( I ( x , y ) − J ( x + d x , y + d y ) ) [ ∂ J ∂ x ∂ J ∂ y ] \frac{\partial \varepsilon (d)}{\partial d}=-2\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(I(x, y)-J(x+d_{x}, y+d_{y}))\begin{bmatrix} \frac{\partial J}{\partial x} & \frac{\partial J}{\partial y} \end{bmatrix} ∂d∂ε(d)=−2x=ux−wx∑ux+wxy=uy−wy∑uy+wy(I(x,y)−J(x+dx,y+dy))[∂x∂J∂y∂J]
使用一阶泰勒展开,得到:
∂ ε ( d ) ∂ d ≈ − 2 ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( I ( x , y ) − J ( x , y ) − [ ∂ J ∂ x ∂ J ∂ y ] [ d x d y ] ) [ ∂ J ∂ x ∂ J ∂ y ] \frac{\partial \varepsilon (d)}{\partial d} \approx -2\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(I(x, y)-J(x, y)-\begin{bmatrix} \frac{\partial J}{\partial x} & \frac{\partial J}{\partial y} \end{bmatrix} \begin{bmatrix} d_{x} \\ d_{y} \end{bmatrix})\begin{bmatrix} \frac{\partial J}{\partial x} & \frac{\partial J}{\partial y} \end{bmatrix} ∂d∂ε(d)≈−2x=ux−wx∑ux+wxy=uy−wy∑uy+wy(I(x,y)−J(x,y)−[∂x∂J∂y∂J][dxdy])[∂x∂J∂y∂J]
其中, I ( x , y ) − J ( x , y ) I(x, y)-J(x, y) I(x,y)−J(x,y)是像素点 ( x , y ) (x, y) (x,y)关于时间的图像导数,记作 δ I \delta I δI; [ ∂ J ∂ x ∂ J ∂ y ] \begin{bmatrix} \frac{\partial J}{\partial x} & \frac{\partial J}{\partial y} \end{bmatrix} [∂x∂J∂y∂J]是图像 J J J在像素点 ( x , y ) (x, y) (x,y)处的梯度,将其近似为 [ ∂ I ∂ x ∂ I ∂ y ] \begin{bmatrix} \frac{\partial I}{\partial x} & \frac{\partial I}{\partial y} \end{bmatrix} [∂x∂I∂y∂I],并记 ∇ I = [ ∂ I ∂ x ∂ I ∂ y ] T \nabla I=\begin{bmatrix} \frac{\partial I}{\partial x} & \frac{\partial I}{\partial y} \end{bmatrix}^{T} ∇I=[∂x∂I∂y∂I]T。
1 2 ∂ ε ( d ) ∂ d ≈ ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( ∇ I T d − δ I ) ∇ I T \frac{1}{2}\frac{\partial \varepsilon (d)}{\partial d} \approx \sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(\nabla I^{T}d-\delta I)\nabla I^{T} 21∂d∂ε(d)≈x=ux−wx∑ux+wxy=uy−wy∑uy+wy(∇ITd−δI)∇IT
需要注意, ∇ I T d − δ I \nabla I^{T}d-\delta I ∇ITd−δI为标量,所以:
1 2 [ ∂ ε ( d ) ∂ d ] T ≈ ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( ∇ I T d − δ I ) ∇ I \frac{1}{2}\begin{bmatrix}\frac{\partial \varepsilon (d)}{\partial d}\end{bmatrix}^{T} \approx \sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(\nabla I^{T}d-\delta I)\nabla I 21[∂d∂ε(d)]T≈x=ux−wx∑ux+wxy=uy−wy∑uy+wy(∇ITd−δI)∇I
1 2 [ ∂ ε ( d ) ∂ d ] T ≈ ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y [ I x 2 I x I y I x I y I y 2 ] d − ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y [ δ I ⋅ I x δ I ⋅ I y ] \frac{1}{2}\begin{bmatrix}\frac{\partial \varepsilon (d)}{\partial d}\end{bmatrix}^{T} \approx \sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}\begin{bmatrix} I_{x}^{2} & I_{x}I_{y} \\ I_{x}I_{y} & I_{y}^{2} \end{bmatrix}d-\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}\begin{bmatrix} \delta I \cdot I_{x} \\ \delta I \cdot I_{y} \end{bmatrix} 21[∂d∂ε(d)]T≈x=ux−wx∑ux+wxy=uy−wy∑uy+wy[Ix2IxIyIxIyIy2]d−x=ux−wx∑ux+wxy=uy−wy∑uy+wy[δI⋅IxδI⋅Iy]
记:
G = ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y [ I x 2 I x I y I x I y I y 2 ] G=\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}\begin{bmatrix} I_{x}^{2} & I_{x}I_{y} \\ I_{x}I_{y} & I_{y}^{2} \end{bmatrix} G=x=ux−wx∑ux+wxy=uy−wy∑uy+wy[Ix2IxIyIxIyIy2]
b = ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y [ δ I ⋅ I x δ I ⋅ I y ] b=\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}\begin{bmatrix} \delta I \cdot I_{x} \\ \delta I \cdot I_{y} \end{bmatrix} b=x=ux−wx∑ux+wxy=uy−wy∑uy+wy[δI⋅IxδI⋅Iy]
则:
d = G − 1 b d=G^{-1}b d=G−1b
两种证法
仔细观察两种证法的结果,可以看出最终得到的结果是一样的。现在梳理一下光流计算中,需要用到的值:
-
I x I_{x} Ix和 I y I_{y} Iy:图像在 ( x , y ) (x, y) (x,y)处的梯度;
-
δ I \delta I δI或 I t I_{t} It:图像在 ( x , y ) (x,y) (x,y)处对时间的变化。
迭代求解LK
有时候我们为了求得更加精确的
d
d
d,可以使用迭代求解
的方式更新
d
d
d。
使用 K ( K ≥ 1 ) K(K\ge 1) K(K≥1)代表迭代次数,对于第 k k k次迭代,前面的第 k − 1 k-1 k−1次迭代用于提供初始值 d k − 1 = [ d x k − 1 d y k − 1 ] T d^{k-1}=\begin{bmatrix} d_{x}^{k-1} & d_{y}^{k-1}\end{bmatrix}^{T} dk−1=[dxk−1dyk−1]T,这里把它作为图像 J J J移动的初始值,移动之后得到图像 J k J_{k} Jk,且 J k ( x , y ) = J ( x + d x k − 1 , y + d y k − 1 ) J_{k}(x,y)=J(x+d_{x}^{k-1},y+d_{y}^{k-1}) Jk(x,y)=J(x+dxk−1,y+dyk−1),之后的问题就是计算优化变量 η k = [ η x k η y k ] T \eta ^{k}=\begin{bmatrix} \eta_{x}^{k} & \eta_{y}^{k}\end{bmatrix}^{T} ηk=[ηxkηyk]T,使下面的目标函数最小:
ε k ( η k ) = ε ( η x k , η y k ) = ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( I ( x , y ) − J k ( x + η x k , y + η y k ) ) 2 \varepsilon^{k} (\eta^{k})=\varepsilon (\eta_{x}^{k}, \eta_{y}^{k})=\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(I(x, y)-J_{k}(x+\eta_{x}^{k}, y+\eta_{y}^{k}))^{2} εk(ηk)=ε(ηxk,ηyk)=x=ux−wx∑ux+wxy=uy−wy∑uy+wy(I(x,y)−Jk(x+ηxk,y+ηyk))2
根据上文的第二种证法的推导形式,最终可以整理为:
η k = G − 1 b k \eta^{k}=G^{-1}b_{k} ηk=G−1bk
注意:这里的 G G G在迭代计算中保持不变,因此只需要计算一次。 b k b_{k} bk每次需要重新计算,之后第 k k k次迭代的结果 d k = d k − 1 + η k d_{k}=d_{k-1}+\eta^{k} dk=dk−1+ηk。
当迭代次数达到设定次数,或者计算得到的 η k \eta^{k} ηk的大小小于设定阀值时,迭代计算结束。
金字塔LK
这里重新把LK中的三条假设拿出来:
- 同一个空间点的像素灰度值,在各个图像中固定不变;
- 相邻帧间物体的运动足够小;
- 邻域具有相同的光流。
这三条假设都是强假设,但实际场景中尤其是第二条很难满足。但是对于我们一直在用的一阶泰勒展开,只有在变量变化很小的情况下才会成立。但如果帧间像素点的运动比较大时,泰勒展开就不能适用了。
原理
金字塔LK主要就是用来解决帧间变化较大的问题。
举例来说,对于分辨率为 800 × 600 800\times 600 800×600的图像,若两帧间某个像素点的运动为 40 × 40 40\times 40 40×40,当把图像分辨率改为 400 × 300 400\times 300 400×300时,运动变为 20 × 20 20\times 20 20×20。以此类推,总能在图像像素降低到一定程度时,原本较大的像素间运动变得足够小,能够使用泰勒展开式进行计算。
把原始图像作为第 0 0 0层,宽高缩小 2 L 2^{L} 2L倍的图像作为第 L L L层,多层图像组织起来,就像是一个金字塔。
其中计算时需要注意一些小问题:
- 在图像降采样之前通常使用
低通滤波器
滤波,防止降采样后发生锯齿现象,其中就会碰到对于图像边界像素如何滤波的问题; - 原始图像尺寸无法保证宽高严格满足
整除
2 L 2^{L} 2L,因此降采样后新的图像的宽高如何确定,有不同的解决方法。可以对原始图像调整宽高满足整除 2 L 2^{L} 2L,或者用下面的公式确定新图像的宽高:
n x L ≤ n x L − 1 + 1 2 n_{x}^{L}\le \frac{n_{x}^{L-1}+1}{2} nxL≤2nxL−1+1
n y L ≤ n y L − 1 + 1 2 n_{y}^{L}\le \frac{n_{y}^{L-1}+1}{2} nyL≤2nyL−1+1
其中, n x L n_{x}^{L} nxL和 n y L n_{y}^{L} nyL分别代表第 L L L层图像的宽和高。
接下来,就是利用金字塔图像从最高的第 L L L层图像,开始向下迭代计算每层中对应像素点的位移。其实这部分操作和迭代求解LK部分的原理相同。
使用 I L I^{L} IL、 J L J^{L} JL代表图像 I I I和 J J J的第 L L L层金字塔图像,假设 u = [ u x u y ] T u=\begin{bmatrix}u_{x} & u_{y}\end{bmatrix}^{T} u=[uxuy]T为图像 I I I中需要跟踪的像素点坐标,则在 I L I^{L} IL中对应的坐标 u L = [ u x L u y L ] T = u / 2 L u^{L}=\begin{bmatrix}u_{x}^{L} & u_{y}^{L}\end{bmatrix}^{T}=u/2^{L} uL=[uxLuyL]T=u/2L。
首先在最高层 L m L_{m} Lm层计算光流大小,把它作为第 L m − 1 L_{m}-1 Lm−1层光流的初始值,再计算该层的光流大小;之后把得到的光流大小作为 L m − 2 L_{m}-2 Lm−2层光流大小的初始值。以此类推,直至第 0 0 0层图像,得到光流的最终结果。
以第 L + 1 L+1 L+1层到第 L L L层之间的计算为例,详细说明下计算流程。
假设通过第
L
+
1
L+1
L+1层计算得到第
L
L
L层光流大小的初始值为
g
L
=
[
g
x
L
g
y
L
]
T
g^{L}=\begin{bmatrix}g_{x}^{L} & g_{y}^{L}\end{bmatrix}^{T}
gL=[gxLgyL]T,那么我们要做的就是求得
d
L
=
[
d
x
L
d
y
L
]
d^{L}=\begin{bmatrix}d_{x}^{L} & d_{y}^{L}\end{bmatrix}
dL=[dxLdyL],使
下面的优化函数最小:
ε k ( d k ) = ε ( d x k , d y k ) = ∑ x = u x − w x u x + w x ∑ y = u y − w y u y + w y ( I L ( x , y ) − J L ( x + g x k + d x k , y + g y k + d y k ) ) 2 \varepsilon^{k} (d^{k})=\varepsilon (d_{x}^{k}, d_{y}^{k})=\sum_{x=u_{x}-w_{x}}^{u_{x}+w_{x}}\sum_{y=u_{y}-w_{y}}^{u_{y}+w_{y}}(I^{L}(x, y)-J^{L}(x+g_{x}^{k}+d_{x}^{k}, y+g_{y}^{k}+d_{y}^{k}))^{2} εk(dk)=ε(dxk,dyk)=x=ux−wx∑ux+wxy=uy−wy∑uy+wy(IL(x,y)−JL(x+gxk+dxk,y+gyk+dyk))2
由于 g L = [ g x L g y L ] T g^{L}=\begin{bmatrix}g_{x}^{L} & g_{y}^{L}\end{bmatrix}^{T} gL=[gxLgyL]T已经比较接近真实值了,所以这里我们很有底气地适用泰勒展开处理 J L ( x + g x k + d x k , y + g y k + d y k ) J^{L}(x+g_{x}^{k}+d_{x}^{k}, y+g_{y}^{k}+d_{y}^{k}) JL(x+gxk+dxk,y+gyk+dyk),后面的处理和常规LK类似,不再赘述。
计算得到 d L = [ d x L d y L ] d^{L}=\begin{bmatrix}d_{x}^{L} & d_{y}^{L}\end{bmatrix} dL=[dxLdyL]后,定义 g L − 1 = 2 ( g L + d L ) g^{L-1}=2(g^{L}+d^{L}) gL−1=2(gL+dL)作为第 L − 1 L-1 L−1层光流大小的初始值,重复上述计算过程逐层迭代计算即可。特别的,在最高层 L m L_{m} Lm处,定义 g L m = [ 0 0 ] T g^{L_{m}}=\begin{bmatrix}0 & 0\end{bmatrix}^{T} gLm=[00]T。
通过观察我们可以发现,最终计算得到的光流 d = ∑ L = 0 L m 2 L d L d=\sum_{L=0}^{L_{m}}2^{L}d^{L} d=∑L=0Lm2LdL。这个公式表明虽然原始图像中像素位移比较大,但对于每层图像来说对应的 d L d^{L} dL都比较小,因此可以使用泰勒展开简化求解。
这里的金字塔LK的流程就有点像:准确值=估计值+残差。估计值由上一层金字塔提供,残差在本层中适用迭代法求出最佳的残差。估计值的初始值为顶层金字塔光流 [ 0 0 ] T \begin{bmatrix}0 & 0\end{bmatrix}^{T} [00]T,残差在每层金字塔都会初始化为 [ 0 0 ] T \begin{bmatrix}0 & 0\end{bmatrix}^{T} [00]T。
整体流程
现在把金字塔LK光流算法的整体流程使用伪代码梳理一遍。
目标:对于图像 I I I中的像素点 u u u,寻找在图像 J J J中灰度最相似的像素点。
Step1
为图像 I I I和 J J J分别建立金字塔表达 { I L } L = 0 , 1 , . . . , L m \{I^{L}\}_{L=0,1,...,L_{m}} {IL}L=0,1,...,Lm和 { J L } L = 0 , 1 , . . . , L m \{J^{L}\}_{L=0,1,...,L_{m}} {JL}L=0,1,...,Lm
Step2
初始化最高层光流初始值 g L m = [ 0 0 ] T g^{L_{m}}=\begin{bmatrix}0 & 0\end{bmatrix}^{T} gLm=[00]T
f o r L = L m ; L ≥ 0 ; L − − for\ L=L_{m};L\ge 0;L-- for L=Lm;L≥0;L−−
计算图像 I L I^{L} IL中像素点 u u u对应的位置: u L = [ u x L u y L ] = u / 2 L u^{L}=\begin{bmatrix}u_{x}^{L} & u_{y}^{L}\end{bmatrix}=u/2^{L} uL=[uxLuyL]=u/2L
计算图像 I L I^{L} IL在 x x x方向地梯度: I x L ( x , y ) = I L ( x + 1 , y ) − I L ( x − 1 , y ) 2 I_{x}^{L}(x,y)=\frac{I^{L}(x+1,y)-I^{L}(x-1,y)}{2} IxL(x,y)=2IL(x+1,y)−IL(x−1,y)
计算图像 I L I^{L} IL在 y y y方向地梯度: I y L ( x , y ) = I L ( x , y + 1 ) − I L ( x , y − 1 ) 2 I_{y}^{L}(x,y)=\frac{I^{L}(x,y+1)-I^{L}(x,y-1)}{2} IyL(x,y)=2IL(x,y+1)−IL(x,y−1)
计算空间矩阵 G = ∑ x = u x L − w x u x L + w x ∑ y = u y L − w y u y L + w y [ I x L 2 I x L I y L I x L I y L I y L 2 ] G=\sum_{x=u_{x}^{L}-w_{x}}^{u_{x}^{L}+w_{x}}\sum_{y=u_{y}^{L}-w_{y}}^{u_{y}^{L}+w_{y}}\begin{bmatrix} I_{x}^{L^{2}} & I_{x}^{L}I_{y}^{L} \\ I_{x}^{L}I_{y}^{L} & I_{y}^{L^{2}} \end{bmatrix} G=∑x=uxL−wxuxL+wx∑y=uyL−wyuyL+wy[IxL2IxLIyLIxLIyLIyL2]
初始化迭代LK初始值: d 0 = [ 0 0 ] T d^{0}=\begin{bmatrix}0 & 0\end{bmatrix}^{T} d0=[00]T
f o r k = 1 ; k ≤ K ∣ ∣ f a b s f ( η ) < t h r e s h o l d ; k + + for\ k=1;k\le K||fabsf(\eta)<threshold;k++ for k=1;k≤K∣∣fabsf(η)<threshold;k++
计算图像差异: δ I k ( x , y ) = I L ( x , y ) − J L ( x + g x L + d x k − 1 , y + g y L + d y k − 1 ) \delta I^{k}(x,y)=I^{L}(x,y)-J^{L}(x+g_{x}^{L}+d_{x}^{k-1}, y+g_{y}^{L}+d_{y}^{k-1}) δIk(x,y)=IL(x,y)−JL(x+gxL+dxk−1,y+gyL+dyk−1)
计算匹配差异向量: b k = ∑ x = u x L − w x u x L + w x ∑ y = u y L − w y u y L + w y [ δ I k ( x , y ) ⋅ I x ( x , y ) δ I k ( x , y ) ⋅ I y ( x , y ) ] b_{k}=\sum_{x=u_{x}^{L}-w_{x}}^{u_{x}^{L}+w_{x}}\sum_{y=u_{y}^{L}-w_{y}}^{u_{y}^{L}+w_{y}}\begin{bmatrix} \delta I^{k}(x,y) \cdot I_{x}(x,y) \\ \delta I^{k}(x,y) \cdot I_{y}(x,y) \end{bmatrix} bk=∑x=uxL−wxuxL+wx∑y=uyL−wyuyL+wy[δIk(x,y)⋅Ix(x,y)δIk(x,y)⋅Iy(x,y)]
计算光流: η k = G − 1 b k \eta^{k}=G^{-1}b_{k} ηk=G−1bk
为下一次迭代提供初始值: d k = d k − 1 + η k d^{k}=d^{k-1}+\eta^{k} dk=dk−1+ηk
结束 k k k上的迭代
得到第 L L L层图像上的光流优化值: d L = d k d^{L}=d^{k} dL=dk
为第 L − 1 L-1 L−1层光流提供光流初始值: g L − 1 = 2 ( g L + d L ) g^{L-1}=2(g^{L}+d^{L}) gL−1=2(gL+dL)
结束 L L L上的迭代
最终计算得到光流 d = g 0 + d 0 d=g^{0}+d^{0} d=g0+d0
图像 J J J中对应坐标 v = u + d v=u+d v=u+d
补充
- 实际计算中得到的类似于
(
x
+
g
x
L
+
d
x
k
−
1
,
y
+
g
y
L
+
d
y
k
−
1
)
(x+g_{x}^{L}+d_{x}^{k-1}, y+g_{y}^{L}+d_{y}^{k-1})
(x+gxL+dxk−1,y+gyL+dyk−1)往往不是整数点像素,所以其像素灰度值需要插值计算得到,一般可以用
双线性插值
计算; - 在对图像进行类似于
卷积
操作时(求梯度、高斯滤波等),需要注意位于图像边界处像素点的处理,这里会发生像素越界,需要采用一些方法处理; - 使用光流法计算匹配点时,当
v
=
u
+
d
v=u+d
v=u+d超出图像
J
J
J时,我们判定为匹配失败:不过即使
v
=
u
+
d
v=u+d
v=u+d位于图像
J
J
J中,也未必是正确的匹配点,毕竟光流法的前提假设过于理想。且该方法中未提供依据判断匹配是否成功,需要其他的方法来滤除该方法计算得到的误匹配,一种常用的方法是基于
RANSAC
计算两帧图像的变换矩阵。
应用:基于中值流的目标跟踪
TLD算法的跟踪模块借鉴了中值流算法,用于上一帧目标 b b bb bb到当前帧目标 b b bb bb的预测。
中值流算法的流程如下:
- 初始化第 t t t帧中的目标 b b bb bb,在 b b bb bb内均匀取点用于跟踪。TLD的作者在目标 b b bb bb内均匀选取了100个像素点作为跟踪点;
- 利用LK光流法确定第 t t t帧与第 t + 1 t+1 t+1帧之间的光流情况。用预测到的第 t + 1 t+1 t+1帧中这些点的位置,反向利用LK光流法得到这些点的后向跟踪轨迹。这样,每个跟踪点都会在第一帧中由前向和后向得到两个位置,这两个位置的欧式距离就作为每个点的 F B e r r o r FBerror FBerror;
- 计算每个跟踪点在两帧之间的匹配度(即计算 n c c ncc ncc的值);
- 利用所有的跟踪点预测
b
b
bb
bb的变化有失妥当,因为有的跟踪点可能并不对
b
b
bb
bb的变化起作用,甚至会影响
b
b
bb
bb的预测。所以需要筛选得到一部分可靠的点。这也就是
中值流算法的核心部分
。将这些跟踪点在相邻两顿之间的 F B e r r o r FBerror FBerror和 n c c ncc ncc值进行排序,得到中值,利用中值,得到同时小于 F B e r r o r FBerror FBerror中值与 n c c ncc ncc中值的特征点(分别过滤 50 % 50\% 50%跟踪点),这样保留不到 50 % 50\% 50%的点作为跟踪成功的点; - 利用剩下的这些点来预测上一顿的目标 b b bb bb在当前帧的位置和大小。
位置(位移估计):用保留的这些点的 ( x , y ) (x,y) (x,y)位移的中值作为目标 b b bb bb的位移。
大小(尺度估计):计算上一帧中保留的这些点之间的欧氏距离 d 1 d1 d1,当前帧中保留的这些点之间的欧氏距离 d 2 d2 d2。并以此得到伸缩比的中值 s s s。 s = m e d i a n ( d 2 / d 1 ) s=median(d2/d1) s=median(d2/d1),这个伸缩比的中值作为目标 b b bb bb尺度伸缩值。
OpenCV接口
CV_EXPORTS_W void calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,
InputArray prevPts, CV_OUT InputOutputArray nextPts,
OutputArray status, OutputArray err,
Size winSize=Size(21,21), int maxLevel=3,
TermCriteria criteria=TermCriteria(
TermCriteria::COUNT+TermCriteria::EPS,
30, 0.01),
double derivLambda=0.5,
int flags=0,
double minEigThreshold=1e-4);
各个参数代表的含义如下:
- prevImg
你的标定图像的灰度图 - nextImg
你想搜寻的图像的灰度图 - prevPts
输入的标定图像的特征点(可以是其他特征点检测方法找到的点) - nextPts
输出场景的特征点 - status
输出状态向量(无符号char),如果在当前图像中能够光流得到标定的特征点位置改变,则设置status的对应位置为1,否则设置为0 - err
输出错误向量;向量的每个元素被设为相应特征的一个错误,误差测量的类型可以在flags参数中设置;如果流不被发现然后错误未被定义(使用status(状态)参数找到此情形)。 - winSize
在每个金字塔水平搜寻窗口的尺寸 - maxLevel
金字塔的高度,初始为3层
相关阅读
- Pyramidal Implementation of the Lucas Kanade Feature Tracker Description of the algorithm
- 金字塔LK光流法数学原理学习笔记
- 总结:光流–LK光流–基于金字塔分层的LK光流–中值流
- 图像金字塔(高斯金字塔 与 拉普拉斯金字塔)
- 多元函数的泰勒(Taylor)展开式
- 光流金字塔calcOpticalFlowPyrLK进行特征点跟踪