OpenCV 检测与跟踪移动物体
本文关键词:OpenCV、Python、背景减除器、KNN、MOG2、目标检测与追踪
想象一下,你用手机拍摄视频,按下一个按钮,相机便开始录制。在后台,手机相机快速捕捉图像,当你观看视频时,看到的是一帧接一帧的画面,而你可能没有注意到这个过程的迅速。一秒钟内,手机显示了超过30幅图像。通过比较这些图像并找出它们之间的差异,你就可以检测到移动物体,这正是背景减除器的核心功能。
使用背景减除器检测和追踪鸟类
在本文中,我将解释背景减除器的工作原理、不同类型的背景减除器,以及如何在 Python 中使用 OpenCV 实现这些功能。
检测移动物体的方法
- 基本运动检测
第一种方法也是最直观的,通过计算帧与帧之间的差异,或将某一被认为是“背景”的帧与其他帧进行比较。其基本思路简单明了:首先保存第一帧,然后与新帧进行比较,通过逐像素相减获得移动物体的图像。
这种技术实现较快,但并不适用于所有场景,因为需要将默认帧设置为背景,而背景在应用中往往并不恒定。例如,假设你正在检测汽车。若将默认背景设置为某一帧,可能在一秒后背景就会变化,汽车已经离开该帧。这会导致背景图像变得不准确,因此在环境快速变化的情况下,算法的精度会降低。
观察左侧的图像,算法虽然在工作,但准确性不足。由于视频中背景几乎每秒都在变化,而算法却假定背景是恒定的,因此出现了无意义的区域。
左侧图像显示了默认背景帧与当前帧之间的差异,右侧图像则展示了带有边界框的当前帧。希望你已理解基本运动检测的主要思想。如果不需要高精度,可以考虑使用这种方法。为了解决上述问题,背景减除器应运而生。
- 背景减除
背景减除是计算机视觉中的基本技术,用于在视频流中将移动物体从背景中隔离。通过将视频中的每一帧与背景模型进行比较,可以识别显著差异的区域作为潜在的前景物体。前景信息可以用于各种用途,包括目标检测与追踪。
在背景减除中,背景图像并非恒定不变。由于光照变化、物体移动和场景动态等因素,背景会随时间变化。背景减除算法的目标是适应性地建模和更新背景,以便在变化的环境中准确检测前景物体。
在 OpenCV 中,背景减除器能够检测阴影,并通过阈值处理排除阴影区域。这一特性对于准确检测物体至关重要,因为未识别的阴影可能被误认为是单独的移动物体。
使用 OpenCV 中的背景减除器
OpenCV 提供了几种不同的背景减除器。本文将使用其中两种最常用的减除器:
- K-最近邻 (KNN)
- 高斯混合 (MOG2)
接下来将重点讲解 MOG2 的背景减除过程。
MOG2 的背景减除流程:
- 初始化:初始化 K 个高斯分布的混合,以模拟场景的背景。每个像素的背景模型由高斯混合表示,K 为预定义参数。
- 适应:随着时间推移,更新每个像素的背景模型,调整高斯分布的参数以适应场景变化。
- 前景检测:根据高斯混合模型计算每个像素属于背景的概率,概率低的像素被分类为前景。
- 更新背景:对被分类为背景的像素,更新高斯分布以纳入新的观察结果并适应场景变化。
- 后处理:应用形态学操作(如腐蚀、膨胀等)来细化前景掩码,去除噪声。
代码示例:检测与追踪移动物体
以下是实现过程的代码示例,理解它的最佳方式是复制代码并使用 cv2.imshow
函数观察每一帧操作后的效果。
# import libraries
import cv2
import numpy as np
# KNN
KNN_subtractor = cv2.createBackgroundSubtractorKNN(detectShadows=True) # detectShadows=True : exclude shadow areas from the objects you detected
# MOG2
MOG2_subtractor = cv2.createBackgroundSubtractorMOG2(detectShadows=True) # exclude shadow areas from the objects you detected
# choose your subtractor
bg_subtractor = MOG2_subtractor
camera = cv2.VideoCapture("resources/run.mp4")
while True:
ret, frame = camera.read()
# 每一帧用于计算前景掩码和更新背景。
foreground_mask = bg_subtractor.apply(frame)
# 阈值处理,像素值大于120设为255,其他设为0,创建二值图像。
ret, threshold = cv2.threshold(foreground_mask.copy(), 120, 255, cv2.THRESH_BINARY)
# 膨胀操作,扩展图像中感兴趣区域。
dilated = cv2.dilate(threshold, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)), iterations=2)
# 查找轮廓
contours, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 检查每个轮廓是否超过某一值,绘制边界框
for contour in contours:
if cv2.contourArea(contour) > 50:
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 0), 2)
cv2.imshow("Subtractor", foreground_mask)
cv2.imshow("Threshold", threshold)
cv2.imshow("Detection", frame)
if cv2.waitKey(30) & 0xFF == 27:
break
camera.release()
cv2.destroyAllWindows()