OpenCV 光流

Ammar Ali 2024年2月15日 OpenCV OpenCV Video
  1. 在 OpenCV 中使用光流檢測視訊中的運動物體
  2. 在 OpenCV 中跟蹤視訊中物件的運動
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 用於設定演算法的標準。

Enjoying our tutorials? Subscribe to DelftStack on YouTube to support us in creating more high-quality video guides. Subscribe
作者: Ammar Ali
Ammar Ali avatar Ammar Ali avatar

Hello! I am Ammar Ali, a programmer here to learn from experience, people, and docs, and create interesting and useful programming content. I mostly create content about Python, Matlab, and Microcontrollers like Arduino and PIC.

LinkedIn Facebook

相關文章 - OpenCV Video