視頻分析(二):光流(Optical Flow)

目標

在這一章當中,html

  • 咱們將使用Lucas-Kanade方法理解光流的概念及其估計。
  • 咱們將使用像cv2.calcOpticalFlowPyrLK()這樣的函數來跟蹤視頻中的特徵點。

光學流程

光流是由物體或相機的運動引發的圖像對象在兩個連續幀之間的視在運動模式。它是2D矢量場,其中每一個矢量是一個位移矢量,顯示點從第一幀到第二幀的移動。考慮下面的圖片(圖片提供:維基百科有關光流的文章)。python

光流量

它顯示了一個連續5幀移動的球。箭頭顯示其位移矢量。光流在如下領域有許多應用:算法

  • 運動結構
  • 視頻壓縮
  • 視頻穩定...

光流在幾個假設下工做:dom

  1. 物體的像素強度在連續幀之間不會改變。
  2. 相鄰像素具備類似的運動。

考慮I(X,Y,t)的第一幀中的像素(檢查一個新的維度,時間,在這裏添加,以前咱們只處理圖像,因此不須要時間)。它(DX,DY)DT時間以後的下一幀中按距離移動。因此,因爲這些像素相同,強度不變,咱們能夠說,ide

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。spa

Lucas-Kanade方法

咱們以前已經看到一個假設,即全部相鄰的像素都會有類似的運動。盧卡斯 - 卡納德方法在這點上須要3x3的補丁。全部9點都有相同的動做。咱們能夠找到(f_x,f_y,f_t)這9點。因此如今咱們的問題變成了求解9個有兩個未知變量的方程,這些變量是超定的。使用最小二乘擬合法能夠得到更好的解決方案。如下是最終的解決方案,它是兩個方程 - 兩個未知問題並解決獲得解決方案。code

\ 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}

(檢查逆矩陣與哈里斯角點檢測器的類似性,它表示角點是更好的跟蹤點。)orm

因此從用戶的角度來看,想法很簡單,咱們給出一些跟蹤點,咱們收到這些點的光流向量。但也有一些問題。直到如今,咱們正在處理小的議案。因此當運動很大時就失敗了。咱們再一次去金字塔。當咱們在金字塔上走時,小的運動被移除,大的運動變成小的運動。所以,在那裏應用盧卡斯 - 卡納德,咱們能夠獲得光流和規模。

盧卡斯- 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

相關文章
相關標籤/搜索