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
用于设置算法的标准。