在這一章當中,html
光流是由物體或相機的運動引發的圖像對象在兩個連續幀之間的視在運動模式。它是2D矢量場,其中每一個矢量是一個位移矢量,顯示點從第一幀到第二幀的移動。考慮下面的圖片(圖片提供:維基百科有關光流的文章)。python
它顯示了一個連續5幀移動的球。箭頭顯示其位移矢量。光流在如下領域有許多應用:算法
- 運動結構
- 視頻壓縮
- 視頻穩定...
光流在幾個假設下工做:dom
考慮第一幀中的像素(檢查一個新的維度,時間,在這裏添加,以前咱們只處理圖像,因此不須要時間)。它在時間以後的下一幀中按距離移動。因此,因爲這些像素相同,強度不變,咱們能夠說,ide
而後採用泰勒級數近似的右手邊,去除經常使用項併除以獲得如下等式:函數
哪裏:編碼
以上等式稱爲光流方程。在它中,咱們能夠找到而且它們是圖像漸變。一樣是沿着時間的梯度。但未知。咱們沒法用兩個未知變量解決這個方程。因此有幾種方法能夠解決這個問題,其中之一就是Lucas-Kanade。spa
咱們以前已經看到一個假設,即全部相鄰的像素都會有類似的運動。盧卡斯 - 卡納德方法在這點上須要3x3的補丁。全部9點都有相同的動做。咱們能夠找到這9點。因此如今咱們的問題變成了求解9個有兩個未知變量的方程,這些變量是超定的。使用最小二乘擬合法能夠得到更好的解決方案。如下是最終的解決方案,它是兩個方程 - 兩個未知問題並解決獲得解決方案。code
(檢查逆矩陣與哈里斯角點檢測器的類似性,它表示角點是更好的跟蹤點。)orm
因此從用戶的角度來看,想法很簡單,咱們給出一些跟蹤點,咱們收到這些點的光流向量。但也有一些問題。直到如今,咱們正在處理小的議案。因此當運動很大時就失敗了。咱們再一次去金字塔。當咱們在金字塔上走時,小的運動被移除,大的運動變成小的運動。所以,在那裏應用盧卡斯 - 卡納德,咱們能夠獲得光流和規模。
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方法計算稀疏特徵集的光流(在咱們的例子中,使用Shi-Tomasi算法檢測拐角)。OpenCV提供了另外一種算法來查找密集的光流。它計算幀中全部點的光流。它基於Gunner Farneback的算法,該算法在Gunner Farneback於2003年在「基於多項式展開的兩幀運動估計」中進行了解釋。
如下示例顯示瞭如何使用上述算法找到密集的光流。咱們獲得了一個帶有光流矢量的雙通道陣列。咱們發現它們的規模和方向。咱們對結果進行顏色編碼以實現更好的可視化 方向對應於圖像的色調值。大小對應於數值平面。請參閱下面的代碼:
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
。
samples/python2/lk_track.py
。嘗試瞭解代碼。samples/python2/opt_flow.py
。嘗試瞭解代碼。參考:
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html