OpenCV-Python 光流

目標

在本章中,

  • 我們將瞭解光流的概念及其使用Lucas-Kanade方法的估計。
  • 我們將使用cv.calcOpticalFlowPyrLK()之類的函數來跟蹤視頻中的特徵點。
  • 我們將使用cv.calcOpticalFlowFarneback()方法創建一個密集的光流場。

光流

光流是由物體或照相機的運動引起的兩個連續幀之間圖像物體的視運動的模式。它是2D向量場,其中每個向量都是位移向量,表示點從第一幀到第二幀的運動。考慮下面的圖片(圖片提供:Wikipedia關於Optical Flow的文章)。

OpenCV-Python 光流 | 四十八

它顯示了一個球連續5幀運動。箭頭顯示其位移向量。光流在以下領域具有許多應用:

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

光流基於以下幾個假設進行工作:

  1. 在連續的幀之間,對象的像素強度不變。
  2. 相鄰像素具有相似的運動。

考慮第一幀中的像素$I(x,y,t)$(在此處添加新維度:時間。之前我們只處理圖像,因此不需要時間)。它在$dt$時間之後拍攝的下一幀中按距離$(dx,dy)$移動。因此,由於這些像素相同且強度不變,因此可以說

OpenCV-Python 光流 | 四十八

然後採用泰勒級數的右側逼近,去掉常用項併除以$dt$得到下面的式子

OpenCV-Python 光流 | 四十八

其中

OpenCV-Python 光流 | 四十八

上述方程式稱為光流方程式。在其中,我們可以找到$fx$和$fy$,它們是圖像漸變。同樣,$f_t$是隨時間變化的梯度。但是$(u,v)$是未知的。我們不能用兩個未知變量來求解這個方程。因此,提供了幾種解決此問題的方法,其中一種是Lucas-Kanade。

Lucas-Kanade 方法

之前我們已經看到一個假設,即所有相鄰像素將具有相似的運動。Lucas-Kanade方法在該點周圍需要3x3色塊。因此,所有9個點都具有相同的運動。我們可以找到這9點的$(fx,fy,ft)$。所以現在我們的問題變成了求解帶有兩個未知變量的9個方程組的問題。用最小二乘擬合法可獲得更好的解決方案。下面是最終的解決方案,它是兩個方程式-兩個未知變量問題,求解以獲得解決答案。


OpenCV-Python 光流 | 四十八

(用哈里斯拐角檢測器檢查逆矩陣的相似性。這表示拐角是更好的跟蹤點。)因此,從用戶的角度來看,這個想法很簡單,我們給一些跟蹤點,我們接收到這些光流矢量點。但是同樣存在一些問題。到現在為止,我們只處理小動作,所以當大動作時它就失敗了。為了解決這個問題,我們使用金字塔。當我們上金字塔時,較小的動作將被刪除,較大的動作將變為較小的動作。因此,通過在此處應用Lucas-Kanade,我們可以獲得與尺度一致的光流。

OpenCV中的Lucas-Kanade

OpenCV在單個函數cv.calcOpticalFlowPyrLK()中提供所有這些功能。在這裡,我們創建一個簡單的應用程序來跟蹤視頻中的某些點。為了確定點,我們使用cv.goodFeaturesToTrack()。我們採用第一幀,檢測其中的一些Shi-Tomasi角點,然後使用Lucas-Kanade光流迭代地跟蹤這些點。對於函數cv.calcOpticalFlowPyrLK(),我們傳遞前一幀,前一點和下一幀。它返回下一個點以及一些狀態碼,如果找到下一個點,狀態碼的值為1,否則為零。我們將這些下一個點迭代地傳遞為下一步中的上一個點。請參見下面的代碼:

<code>import numpy as np
import cv2 as cv
import argparse
parser = argparse.ArgumentParser(description='This sample demonstrates Lucas-Kanade Optical Flow calculation. \\
The example file can be downloaded from: \\
https://www.bogotobogo.com/python/OpenCV_Python/images/mean_shift_tracking/slow_traffic_small.mp4')
parser.add_argument('image', type=str, help='path to image file')
args = parser.parse_args()
cap = cv.VideoCapture(args.image)
# 用於ShiTomasi拐點檢測的參數
feature_params = dict( maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7,
blockSize = 7 )
# lucas kanade光流參數
lk_params = dict( winSize = (15,15),
maxLevel = 2,
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
# 創建一些隨機的顏色
color = np.random.randint(0,255,(100,3))
# 拍攝第一幀並在其中找到拐角
ret, old_frame = cap.read()
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
p0 = cv.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 創建用於作圖的掩碼圖像
mask = np.zeros_like(old_frame)
while(1):
ret,frame = cap.read()
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 計算光流
p1, st, err = cv.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 = cv.line(mask, (a,b),(c,d), color[i].tolist(), 2)
frame = cv.circle(frame,(a,b),5,color[i].tolist(),-1)
img = cv.add(frame,mask)
cv.imshow('frame',img)
k = cv.waitKey(30) & 0xff
if k == 27:

break
# 現在更新之前的幀和點
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)/<code>

(此代碼不會檢查下一個關鍵點的正確性。因此,即使任何特徵點在圖像中消失了,光流也有可能找到下一個看起來可能與它接近的下一個點。因此,對於穩健的跟蹤,實際上 應該以特定的時間間隔檢測點。OpenCV樣本附帶了這樣一個樣本,該樣本每5幀發現一次特徵點,並且還對光流點進行了後向檢查,以僅選擇良好的流點。請參閱代碼 samples/python/lk_track.py)。

查看我們得到的結果:

OpenCV-Python 光流 | 四十八

OpenCV中的密集光流

Lucas-Kanade方法計算稀疏特徵集的光流(在我們的示例中為使用Shi-Tomasi算法檢測到的角)。OpenCV提供了另一種算法來查找密集的光流。它計算幀中所有點的光通量。它基於Gunner Farneback的算法,在2003年Gunner Farneback的“基於多項式展開的兩幀運動估計”中對此進行了解釋。

下面的示例顯示瞭如何使用上述算法找到密集的光流。我們得到一個帶有光流矢量$(u,v)$的2通道陣列。我們找到了它們的大小和方向。我們對結果進行顏色編碼,以實現更好的可視化。方向對應於圖像的色相值。幅度對應於值平面。請參見下面的代碼:

<code>import numpy as np
import cv2 as cv
cap = cv.VideoCapture(cv.samples.findFile("vtest.avi"))
ret, frame1 = cap.read()
prvs = cv.cvtColor(frame1,cv.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255
while(1):
ret, frame2 = cap.read()
next = cv.cvtColor(frame2,cv.COLOR_BGR2GRAY)
flow = cv.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
mag, ang = cv.cartToPolar(flow[...,0], flow[...,1])
hsv[...,0] = ang*180/np.pi/2
hsv[...,2] = cv.normalize(mag,None,0,255,cv.NORM_MINMAX)
bgr = cv.cvtColor(hsv,cv.COLOR_HSV2BGR)
cv.imshow('frame2',bgr)
k = cv.waitKey(30) & 0xff
if k == 27:
break
elif k == ord('s'):
cv.imwrite('opticalfb.png',frame2)

cv.imwrite('opticalhsv.png',bgr)
prvs = next/<code>

查看以下結果:

OpenCV-Python 光流 | 四十八


分享到:


相關文章: