文章目录
前言
在计算机视觉领域,背景建模(Background Modeling)是视频分析任务(如运动检测、目标跟踪)的核心技术之一。通过分离视频中的前景(运动物体)和背景(静态场景),我们可以有效识别和跟踪目标对象。OpenCV提供了多种背景建模算法,本文将介绍其原理及实践方法。
一、背景建模是什么?
1、定义
指在计算机视觉中,从视频序列中提取出静态背景的一种技术。在视频中,背景通常被定义为相对稳定的部分,例如墙壁、地面或天空等。背景建模的目标是将动态的前景对象与静态的背景进行分离,以便进一步分析和处理。
2、目的
通过背景建模,我们可以实现很多应用,例如运动检测、目标跟踪等。
3、常用背景建模算法
1) 帧差法(Frame Difference)
-
原理:比较连续两帧的像素差异
-
优点:计算简单、实时性高
-
缺点:无法处理复杂场景
-
OpenCV实现:通过absdiff()函数实现
该方法将连续的视频帧与背景进行比较,通过像素值的差异来提取前景目标。当像素差异超过设定的阈值时,将该像素标记为前景。该方法简单直观,适用于简单场景和静态背景。
帧差法非常简单,但是会引入噪音和空洞(人物中间是黑色的)问题。
2) 高斯混合模型(MOG/MOG2)
-
原理:使用多个高斯分布建模背景像素
-
MOG2改进:自适应调整高斯分布数量,支持阴影检测
-
适用场景:动态背景较多的环境
-
OpenCV函数:createBackgroundSubtractorMOG2()
它假设每个像素的背景像素值服从多个高斯分布。算法通过对每个像素进行建模,并根据新的观测值进行更新,最终得到背景模型。当新的观测值与背景模型不匹配时,将其标记为前景。
BackgroundSubtractorMOG2算法能够自适应地调整模型的数量和混合权重,适用于复杂场景和动态背景。
3)K最近邻(KNN)
-
原理:基于像素值的最近邻匹配判断背景
-
优势:处理光照变化效果较好
-
OpenCV函数:createBackgroundSubtractorKNN()
该方法主要通过对每个像素周围的邻近像素进行聚类来建模背景。该算法将每个像素看作一个样本点,在每次输入新的观测帧时,将其与背景模型进行比较,并根据像素值的差异度量其是否为前景。BackgroundSubtractorKNN算法具有较快的处理速度和一定的鲁棒性,适用于实时背景建模和前景检测。
4) GMG算法
-
原理:结合统计方法和形态学操作
-
特点:适合静态摄像头场景,需要初始化时间
-
函数:createBackgroundSubtractorGMG()
4、步骤
1)初始化背景模型
从视频序列或摄像头中获取第一帧图像作为初始背景图像。
2)处理每一帧图像
获取下一帧图像,将其与背景图像进行比较。
3)计算帧差图像
将当前帧图像与背景图像进行像素级别的差分计算,得到帧差图像。
4)二值化处理
将帧差图像转换为二值图像,根据设置的阈值将差异像素标记为前景或背景。
5)前景检测
根据二值化处理得到的前景图像,可以进行一系列处理,如轮廓检测、面积过滤等,以获得更精确的前景区域。
6)更新背景模型
在每一帧图像处理后,更新背景模型,可以采用移动平均或其他方法来更新背景的估计。
7)重复以上步骤
持续处理每一帧图像,直到视频序列结束或达到设定的停止条件。
二、代码部分
1、案例实现
首先准备一个视频文件“test.avi”
import cv2
# 经典的测试视频
cap = cv2.VideoCapture('test.avi') # 打开视频文件,或者打开摄像头
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) # 设置卷积核形态,cv2.MORPH_CROSS表示设置的是十字形卷积核,大小为3*3
fgbg = cv2.createBackgroundSubtractorMOG2() # 创建混合高斯模型,用于背最建模,从视频帧中分离出前景对象。
while 1: # 定义一个死循环,用于反复从视频中提取出每一帧画面
ret, frame = cap.read() # 读取视频文件的每一帧画面,返回值ret为True表示正常读取到图像,frame表示从视频中获取当前一帧图片
cv2.imshow('frame', frame) # 展示读取到的每一帧画面,以此来构成视频的画面
fgmask = fgbg.apply(frame) # 调用高斯混合模型中的用法apply对获取到的每一帧图像进行前景背景分隔算法,生成一个背景掩码,这个背景掩码的大小是与输入图像大小相同的二值图像,前景为白色,背景为黑色
cv2.imshow('fgmask', fgmask) # 展示背景掩码对应的图像
fgmask_new = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel) # 上述生成的掩码图像有很多噪声点,此处使用开运算,即先腐蚀后膨胀去除噪声点
cv2.imshow('fgmask1', fgmask_new) # 展示处理完的图像
# 寻找视频中的轮廓
_, contours, h = cv2.findContours(fgmask_new, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE) # 对上述处理完的图像进行轮廓检测,cv2.RETR_EXTERNAL表示只检测最外侧轮廓,cv2.CHAIN_APPROX_SIMPLE表示删除轮廓上冗余点来简化形状,只保留端点,并用线段连接
# _表示修改后的图像,受OpenCV版本影响可能没有,contours是一个列表存放提取到的每一个轮廓,坐标点集的形式,h表示轮廓的层级信息
for c in contours: # 遍历每一个轮廓
perimeter = cv2.arcLength(c, True) # 计算轮廓周长
if perimeter > 188: # 判断轮廓周长的大小,用来筛选周长大于188的轮廓
# 找到一个直矩形(不会旋转)
x, y, w, h = cv2.boundingRect(c) # 对输入的轮廓进行处理,返回该轮廓的坐标和高宽
# 在原视频上绘制出这个轮廓的外接矩形
fgmask_new_rect = cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('fgmask_new_rect', fgmask_new_rect) # 展示绘制的图像
k = cv2.waitKey(60)
if k == 27: # 勇于接收键盘esc键,以此来中断死循环
break
2、运行结果
4、卷积核形态
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
设置卷积核形态,cv2.MORPH_CROSS表示这里我们用的是十字形卷积核,大小为3*3
感兴趣的可以去试试 矩形卷积核 MORPH_RECT、椭圆形卷积核 MORPH_ELLIPSE 等。
总结
OpenCV的背景建模工具为运动检测提供了高效解决方案。实际应用中需注意:
-
根据场景特点选择合适算法
-
通过参数调优平衡灵敏度与噪声
-
结合后处理提升结果质量
-
对于复杂场景可考虑深度学习方案(如Background Matting)
通过本文介绍的方法,读者可以快速搭建基础的运动检测系统,并在此基础上扩展更复杂的功能。