OpenCV 光流
本教程將討論在 OpenCV 中使用光流檢測視訊中的移動物件。
在 OpenCV 中使用光流檢測視訊中的運動物體
光流可以檢測 OpenCV 中視訊中存在的運動物體。我們還可以使用光流檢測物體的運動路徑。
在光流中,物體的位置在兩幀之間進行比較,如果物體的位置在幀之間發生變化,我們可以將其標記為移動物體,並使用 OpenCV 將其突出顯示。例如,我們有一個視訊,我們想要突出顯示一個移動的物件。
首先,我們需要從視訊中獲取兩幀,前一幀,下一幀。我們將使用 OpenCV 的 calcOpticalFlowFarneback()
函式來查詢視訊中移動的物件。
calcOpticalFlowFarneback()
函式使用兩個幀並比較這些幀中物件的位置,如果物件的位置發生變化,該函式將該物件儲存在二維陣列中。
我們可以使用 cartToPolar()
和 calcOpticalFlowFarneback()
返回的 2D 陣列來查詢給定視訊中存在的物件的大小和角度。
之後,我們可以根據圖形上移動物體的大小和角度繪製不同的顏色,以視覺化物體。例如,讓我們使用一條狗的視訊並突出顯示它的動作。
請參閱下面的程式碼。
import numpy as np
import cv2
cap_video = cv2.VideoCapture("bon_fire_dog_2.mp4")
ret, frame1 = cap_video.read()
prvs_frame = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv_drawing = np.zeros_like(frame1)
hsv_drawing[..., 1] = 255
while 1:
ret, frame2 = cap_video.read()
if not ret:
print("No frames available!")
break
next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
flow_object = cv2.calcOpticalFlowFarneback(
prvs_frame, next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0
)
magnitude, angle = cv2.cartToPolar(flow_object[..., 0], flow_object[..., 1])
hsv_drawing[..., 0] = angle * 180 / np.pi / 2
hsv_drawing[..., 2] = cv2.normalize(magnitude, None, 0, 255, cv2.NORM_MINMAX)
bgr_drawing = cv2.cvtColor(hsv_drawing, cv2.COLOR_HSV2BGR)
cv2.imshow("frame2", bgr_drawing)
cv2.waitKey(10)
prvs_frame = next_frame
cv2.destroyAllWindows()
輸出:
正如你在上面的輸出中看到的那樣,狗被標記為不同的顏色,因為狗是視訊中唯一移動的物件。在上面的程式碼中,OpenCV 的 cvtColor()
函式用於將視訊的彩色幀轉換為灰度。
zeros_like()
函式建立一個黑色繪圖以顯示移動物件。calcOpticalFlowFarneback()
函式查詢移動物件。
calcOpticalFlowFarneback()
函式的第一個引數是第一個 8 位單通道影象或前一幀。第二個引數是第二個影象或下一幀。
第三個引數是將儲存流物件的輸出陣列。第四個引數是用於為影象構建金字塔的影象比例。
第五個引數定義了金字塔的層數,如果我們不想使用額外的層,我們可以將其值設定為 1。第六個引數是平均視窗大小,它的值定義了演算法的速度。
較小的視窗大小意味著速度會很慢,但輸出會很銳利。第七個引數定義了演算法在每一層的迭代次數。
第八個引數用於設定畫素鄰域的大小,用於查詢每個畫素的多項式。第 9 個引數用於設定多項式的標準差,第 10 個引數用於設定標誌。
normalize()
函式用於使用 MINMAX
歸一化來歸一化移動物件的大小。
在 OpenCV 中跟蹤視訊中物件的運動
我們還可以跟蹤視訊中正在移動的特徵點。
例如,要跟蹤狗的移動位置,我們需要獲取一些特徵點,然後進行跟蹤。我們可以使用 OpenCV 的 goodFeaturesToTrack()
函式來獲取特徵點。
之後,我們需要在 calcOpticalFlowPyrLK()
函式中將這些特徵點與前一幀和下一幀一起傳遞,以跟蹤給定點以及視訊幀。該函式將返回下一個點、狀態和錯誤。
我們可以使用 OpenCV 的 line()
和 circle()
函式使用輸出來繪製直線和圓。之後,我們可以使用 OpenCV 的 add()
函式將繪圖新增到原始視訊中。
請參閱下面的程式碼。
import numpy as np
import cv2
cap_video = cv2.VideoCapture("bon_fire_dog_2.mp4")
feature_parameters = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
lk_parameters = dict(
winSize=(15, 15),
maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03),
)
random_color = np.random.randint(0, 255, (100, 3))
ret, previous_frame = cap_video.read()
previous_gray = cv2.cvtColor(previous_frame, cv2.COLOR_BGR2GRAY)
p0_point = cv2.goodFeaturesToTrack(previous_gray, mask=None, **feature_parameters)
mask_drawing = np.zeros_like(previous_frame)
while 1:
ret, frame = cap_video.read()
if not ret:
print("No frames available!")
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
p1, st_d, error = cv2.calcOpticalFlowPyrLK(
previous_gray, frame_gray, p0_point, None, **lk_parameters
)
if p1 is not None:
good_new_point = p1[st_d == 1]
good_old_point = p0_point[st_d == 1]
for i, (new_point, old_point) in enumerate(zip(good_new_point, good_old_point)):
a, b = new_point.ravel()
c, d = old_point.ravel()
mask_drawing = cv2.line(
mask_drawing,
(int(a), int(b)),
(int(c), int(d)),
random_color[i].tolist(),
2,
)
frame = cv2.circle(frame, (int(a), int(b)), 5, random_color[i].tolist(), -1)
img = cv2.add(frame, mask_drawing)
cv2.imshow("frame", img)
cv2.waitKey(30)
previous_gray = frame_gray.copy()
p0_point = good_new_point.reshape(-1, 1, 2)
cv2.destroyAllWindows()
輸出:
如你所見,視訊中正在跟蹤特徵點。當我們想要跟蹤視訊中物件的運動時,此演算法很有用。
在上面的程式碼中,goodFeaturesToTrack()
函式的第一個引數是我們想要跟蹤特徵點的幀。第二個引數是包含角點的輸出。
第三個引數 maxCorners
設定最大角。第四個引數 minDistance
用於設定質量等級,第五個引數用於設定點之間的最小距離。
第六個引數 mask
用於設定使用蒙版從中提取點的幀部分,如果我們想從整個影象中提取點,我們可以將蒙版設定為 none
.
第七個引數 blockSize
用於設定塊大小,第八個引數用於設定梯度大小。
在上面的程式碼中,我們使用 dict()
函式定義了一些屬性,然後在程式碼中傳遞它們,但我們也可以在函式內部定義屬性。
calcOpticalFlowPyrLK()
函式的第一個引數是第一個輸入影象或前一幀,第二個引數是第二個影象(或下一幀)。
第三個引數是上一個輸入點,第四個是下一個輸出點。第五個引數 status
是狀態,如果找到該點的流並且它是輸出引數,則該點的狀態將為 1。
第六個引數 err
是錯誤向量和輸出引數。第七個引數 winSize
用於設定每個金字塔的視窗大小,第八個引數 maxLevel
用於設定金字塔的數量。
最後一個引數 criteria
用於設定演算法的標準。