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