视频分析(二):光流(Optical Flow)

开发十年,就只剩下这套Java开发体系了 >>>   hot3.png

目标

在这一章当中,

  • 我们将使用Lucas-Kanade方法理解光流的概念及其估计。
  • 我们将使用像cv2.calcOpticalFlowPyrLK()这样的函数来跟踪视频中的特征点。

光学流程

光流是由物体或相机的运动引起的图像对象在两个连续帧之间的视在运动模式。它是2D矢量场,其中每个矢量是一个位移矢量,显示点从第一帧到第二帧的移动。考虑下面的图片(图片提供:维基百科有关光流的文章)。

光流量

它显示了一个连续5帧移动的球。箭头显示其位移矢量。光流在以下领域有许多应用:

  • 运动结构
  • 视频压缩
  • 视频稳定...

光流在几个假设下工作:

  1. 物体的像素强度在连续帧之间不会改变。
  2. 相邻像素具有相似的运动。

考虑I(X,Y,t)的第一帧中的像素(检查一个新的维度,时间,在这里添加,之前我们只处理图像,所以不需要时间)。它(DX,DY)DT时间之后的下一帧中按距离移动。所以,由于这些像素相同,强度不变,我们可以说,

I(x,y,t)= I(x + dx,y + dy,t + dt)

然后采用泰勒级数近似的右手边,去除常用项并除以DT得到以下等式:

f_x u + f_y v + f_t = 0 \;

哪里:

f_x = \ frac {\ partial f} {\ partial x} \;  ;  \;  f_y = \ frac {\ partial f} {\ partial x} u = \ frac {dx} {dt} \;  ;  \;  v = \ frac {dy} {dt}

以上等式称为光流方程。在它中,我们可以找到F_X并且f_y它们是图像渐变。同样F_T是沿着时间的梯度。但(U,V)未知。我们无法用两个未知变量解决这个方程。所以有几种方法可以解决这个问题,其中之一就是Lucas-Kanade。

Lucas-Kanade方法

我们之前已经看到一个假设,即所有相邻的像素都会有相似的运动。卢卡斯 - 卡纳德方法在这点上需要3x3的补丁。所有9点都有相同的动作。我们可以找到(f_x,f_y,f_t)这9点。所以现在我们的问题变成了求解9个有两个未知变量的方程,这些变量是超定的。使用最小二乘拟合法可以获得更好的解决方案。以下是最终的解决方案,它是两个方程 - 两个未知问题并解决得到解决方案。

\ begin {bmatrix} u \\ v \ end {bmatrix} = \ begin {bmatrix} \ sum_ {i} {f_ {x_i}} ^ 2&\ sum_ {i} {f_ {x_i} f_ {y_i}} \ \ sum_ {i} {f_ {x_i} f_ {y_i}}&\ sum_ {i} {f_ {y_i}} ^ 2 \ end {bmatrix} ^ { -  1} \ begin {bmatrix}  -  \ sum_ {i } {f_ {x_i} f_ {t_i}} \\  -  \ sum_ {i} {f_ {y_i} f_ {t_i}} \ end {bmatrix}

(检查逆矩阵与哈里斯角点检测器的相似性,它表示角点是更好的跟踪点。)

所以从用户的角度来看,想法很简单,我们给出一些跟踪点,我们收到这些点的光流向量。但也有一些问题。直到现在,我们正在处理小的议案。所以当运动很大时就失败了。我们再一次去金字塔。当我们在金字塔上走时,小的运动被移除,大的运动变成小的运动。因此,在那里应用卢卡斯 - 卡纳德,我们可以得到光流和规模。

卢卡斯- Kanade光流OpenCV中

OpenCV在一个函数cv2.calcOpticalFlowPyrLK()中提供了所有这些。在这里,我们创建一个简单的应用程序来跟踪视频中的某些点。为了决定点,我们使用cv2.goodFeaturesToTrack()。我们采用第一帧,检测一些Shi-Tomasi角点,然后使用Lucas-Kanade光流迭代地跟踪这些点。对于函数cv2.calcOpticalFlowPyrLK(),我们传递前一帧,前一点和下一帧。如果找到下一个点,它将返回下一个点以及一些状态值为1的值,否则为零。我们迭代地将这些下一点作为下一步中的前几点。请参阅下面的代码:

import numpy as np
import cv2

cap = cv2.VideoCapture('slow.flv')

# params for ShiTomasi corner detection
feature_params = dict( maxCorners = 100,
                       qualityLevel = 0.3,
                       minDistance = 7,
                       blockSize = 7 )

# Parameters for lucas kanade optical flow
lk_params = dict( winSize  = (15,15),
                  maxLevel = 2,
                  criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

# Create some random colors
color = np.random.randint(0,255,(100,3))

# Take first frame and find corners in it
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)

# Create a mask image for drawing purposes
mask = np.zeros_like(old_frame)

while(1):
    ret,frame = cap.read()
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

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

    # Select good points
    good_new = p1[st==1]
    good_old = p0[st==1]

    # draw the tracks
    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

    # Now update the previous frame and previous points
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()
cap.release()

(这段代码并没有检查下一个关键点的正确性,所以即使任何特征点在图像中消失了,光流也有可能找到可能看起来接近它的下一个点。应该在特定的时间间隔内检测点,OpenCV样本会出现这样一个样本,它每隔5帧就会找到一个特征点,并对所选择的光学流点进行后向检查samples/python2/lk_track.py

查看我们得到的结果:

用于光流的Lucas-Kanade方法

OpenCV中的密集光流

Lucas-Kanade方法计算稀疏特征集的光流(在我们的例子中,使用Shi-Tomasi算法检测拐角)。OpenCV提供了另一种算法来查找密集的光流。它计算帧中所有点的光流。它基于Gunner Farneback的算法,该算法在Gunner Farneback于2003年在“基于多项式展开的两帧运动估计”中进行了解释。

以下示例显示了如何使用上述算法找到密集的光流。我们得到了一个带有光流矢量的双通道阵列(U,V)。我们发现它们的规模和方向。我们对结果进行颜色编码以实现更好的可视化 方向对应于图像的色调值。大小对应于数值平面。请参阅下面的代码:

import cv2
import numpy as np
cap = cv2.VideoCapture("vtest.avi")

ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255

while(1):
    ret, frame2 = cap.read()
    next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)

    flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

    mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])
    hsv[...,0] = ang*180/np.pi/2
    hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)

    cv2.imshow('frame2',rgb)
    k = cv2.waitKey(30) & 0xff
    if k == 27:
        break
    elif k == ord('s'):
        cv2.imwrite('opticalfb.png',frame2)
        cv2.imwrite('opticalhsv.png',rgb)
    prvs = next

cap.release()
cv2.destroyAllWindows()

看到下面的结果:

密集光流

OpenCV在密集光学流程中提供了更高级的示例,请参阅samples/python2/opt_flow.py

其他资源

练习

  1. 检查代码samples/python2/lk_track.py。尝试了解代码。
  2. 检查代码samples/python2/opt_flow.py。尝试了解代码。

参考:

http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html

223916_bL9y_2663968.jpg

你可能感兴趣的:(视频分析(二):光流(Optical Flow))