OpenCV数字图像处理与计算机视觉(python)OpenCv

Lucas-Kanade稀疏光流法

2020-02-25  本文已影响0人  陨星落云
光流法

光流

由于目标对象或者摄像机的移动造成的图像对象在连续两帧图像中的移动被称为光流。如下图所示,它是一个 2D 向量场,可以用来显示一个点从第一帧图像到第二帧图像之间的移动,箭头表示光流场向量。

光流.png

其中,光流包括稀疏光流与稠密光流。图像中的每个像素都使用这种方法,则通常将其称为“稠密光流”。有一种替代类算法被称为“稀疏光流”,仅仅跟踪图像中某些点的子集。

Lucas-Kanade稀疏光流法

假设先验

LK光流全称为Lucas-Kanade光流,算法原理比较好理解,首先,LK光流对应用场景提出了三个假设先验:

算法流程
给定t 时刻的图像上的像素点I(x,y) ,算法的目标是找到在下一时刻该像素的在各个方向上的位移,用公式表达就是:
I ( x , y , t ) = I ( x + \delta x , y + \delta y , t + \delta t ) \tag{1}

图111.png

可以对等号右边的式子采用泰勒展开:

image.png

可以看到等号后边第一项与等号左边相等可以消去,\frac{\partial I}{\partial x}\frac{\partial I}{\partial y} 比较好理解,就是当前时刻图像在δxδy方向的梯度了,\frac{\partial I}{\partial t} 表示的的是时间方向上的梯度,也就是下一帧与当前帧的差分。δt是两帧时间差也就是1,而δxδy就是我们要求解的像素运动。由此我们可以得到:
I _ { x } \delta x + I _ { y } \delta y = - I _ { t }\tag{3}
我们现在有两个未知数但是只有一个方程,无法求解,根据我们在最开始的第三个假设,此时我们可以使用需要求解的像素周围5×5的像素块来帮助我们得到更多的方程式:
\left[ \begin{array} { c } { I _ { x 1 } I _ { y 1 } } \\ { I _ { x 2 } I _ { y 2 } } \\ { \cdots } \\ { I _ { x 24 } I _ { y 24 } } \\ { I _ { x 25 } I _ { y 25 } } \end{array} \right] \left[ \begin{array} { c } { \delta x } \\ { \delta y } \end{array} \right] = \left[ \begin{array} { c } { - I _ { t 1 } } \\ { - I _ { t 2 } } \\ { \cdots } \\ { - I _ { t 24 } } \\ { - I _ { t 25 } } \end{array} \right]\tag{4}
此时便组成了一个超定方程组,也就是方程个数大于未知数个数,这是我们可以使用最小二乘法来求解这个方程组。
\begin{aligned} A x & = - b \\ A ^ { T } A x & = A ^ { T } ( - b ) \\ \end{aligned}\tag{5}
方程的解必须满足以下条件:

flat.png

总结

像素块不能选在平缓区域和边缘区域。如下图所示,像素块最好选在纹理丰富的区域。

high_texture截图_20200225054308.png

LK光流法存在的问题

在实际的拍摄的视频中,每一帧不一定都满足三个假设:亮度恒定、像素偏移小和空间一致性。

代码实现:

import numpy as np
import cv2

cap = cv2.VideoCapture("768x576.avi")

# ShiTomas角点检测的参数
feature_params = dict(maxCorners =100,qualityLevel=0.3,minDistance=7,blockSize=7)

# 金字塔LK算法参数
lk_params = dict(winSize=(15,15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS|cv2.TermCriteria_COUNT,10,0.03))
# 创建随机颜色
color = np.random.randint(0,255,(100,3))

ret,old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame,cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray,mask=None,**feature_params)

mask = np.zeros_like(old_frame)

while(1):
    ret,frame = cap.read()
    
    if ret is True: 
        frame_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
    else:   
        break

    p1,st,err = cv2.calcOpticalFlowPyrLK(old_gray,frame_gray,p0,None,**lk_params)

    good_new =p1[st==1]
    good_old =p0[st==1]

    for i,(new,old) in enumerate(zip(good_new,good_old)):
        a,b = new.ravel()
        c,d = old.ravel()
        mask = cv2.line(mask,(a,b),(c,d),color[i].tolist(),2)
        frame = cv2.circle(frame,(a,b),5,color[i].tolist(),-1)
    img = cv2.add(frame,mask)

    cv2.imshow('frame',img)
    k = cv2.waitKey(30)&0xff
    if k == 27:
        break

    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()
cap.release()

参考文献:

[1] B. Lucas and T. Kanade, “An iterative image registration technique with an application to stereo vision,” in Proc. of International Joint Conf. On Artificial Intelligence, pp.674-679, 1981.

[2] 浙江大学陆系群副教授,《Motion Estimation Optical Flow.PPT》

[3] 《OpenCV-Python 中文教程》

[4] LK稀疏光流法

上一篇下一篇

猜你喜欢

热点阅读