6.2 背景建模
所谓背景建模,就是将背景识别出来,与前景进行区分的过程。
6.2.1方法介绍
背景建模需要满足一定条件,保证摄像机拍摄位置不变,保证背景是基本不发生变化的。如路口的监控摄像机,只有车流人流等前景部分能发生移动,而马路树木等背景不能发生移动。
判断视频中前景和背景的方法主要有两种:帧差法、混合高斯模型
帧差法
如下图,摄像机拍摄获取一帧一帧的连续图像,需要摄像机识别出,哪些部分是运动的,哪些部分是静止的。在摄像机拍摄位置不变的前提下,一个物体位置发生了变化,那么像素点数值也会发生变化;不发生运动的物体,它的像素点数值不基本会发生的变化。
由于场景中的目标在运动,位置产生了差异,摄像头捕捉到的图像不同。该算法通过对时间上连续的两帧图像进行差分算法,不同帧对应的像素点相减,判断灰度差异的绝对值的变化情况,当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。
帧差法公式如下
(x, y) 代表像素点坐标, 代表第n 帧图像,
代表第
帧图像,差分结果为
。如果差分结果大于阈值 T,将该像素点指定成255白点,代表前景;否则指定为0,代表背景。
帧差法缺点
第一,如果图像中存在噪音点,帧差法会引入噪音。第二,如上图右侧,人的轮廓是白色,但轮廓内是黑色的,因为人在前后两帧图像中的位置变化很小,会存在空洞问题。帧差法不常用。
混合高斯模型
在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯模型的个数可以自适应。然后在测试阶段对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。由于整个过程GMM模型在不断更新学习中,所以对动态背景具有一定的鲁棒性,整体的检测效果会得到提升。
可以简单理解一下。在图像中判断前景和背景,比如在运动过程中,人服从一种分布,背景也服从一种分布。判断视频中的像素点符合哪一种分布,符合运动的就判断是前景,符合静止的就判断为背景。
然而,背景的实际分布应当是多个高斯分布混合在一起的,每个高斯模型也有其对应的权重。下图左侧是图像背景满足的两种分布,得到右侧的结果也是不同的,两种分布需要对应不同的权重。虽然整体的背景是由多个高斯分布融合在一起的,但每个分布所占的权重不一定都相同。权重是由图像中像素点的个数决定的。
对于视频中某一块区域的一个像素点来说,虽然在视频中的位置不发生改变,但它的值会发生变化。像素点值的变化情况应当符合高斯分布。
混合高斯模型的学习方法
- 首先初始化每个高斯模型矩阵参数。均值μ
,标准差σ
- 取视频中T
帧数据图像,用来训练高斯混合模型。来了第一个像素点之后用它来当作第一个高斯分布。如:μ1=100, σ1=5
。
- 当后面来的像素值,与前面已有的高斯的均值比较,如果该像素点的值与其模型均值差在3倍的方差内,则属于该分布,并对其进行参数更新。
如:第二帧的像素点的值为105 ,比较 ,因此第二帧的像素点属于第一帧像素的分布,因此这两个数据归纳在一起,更新第一个分布的均值μ
,标准差σ
。
如果下次来的像素点不满足当前的高斯分布,用它来创建一个新的高斯分布。
如:第三帧的像素点的值为200 ,不满足更新后σ ,那么创建一个新的高斯分布。如:
一般情况下,高斯分布的个数设置3-5个,过多的话结果不好
混合高斯模型测试方法
在测试阶段,对新来的像素点的值,与学习好了的混合高斯模型中的每一个均值比较,如果差值在2倍的方差之间,就认为是背景,否则认为是前景。将前景赋值为255,背景赋值为0
6.2.2 相关函数
混合高斯模型背景建模方法
函数原型:cv2.createBackgroundSubtractorMOG2().apply(image)
参数:
返回值:
自定义卷积核
函数原型:rectkernel = cv2.getStructuringElement(shape, ksize, anchor=None)
参数:
- shape: 卷积核形状。矩形:MORPH_RECT,十字型:MORPH_CROSS,椭圆形MORPH_ELLIPSE
- ksize: 卷积核大小
- anchor:坐标(x,y),元素内的锚定位置。默认值为(-1,-1),即结构化元素的中心。
返回值:卷积核矩阵
6.2.3 示例
import numpy as np
import cv2
# 指定文件夹
filepath = 'C:\\Users\\opencv\\img\\class'
# 获取文件夹中某个视频
video = cv2.VideoCapture(filepath + '\\test.avi')
#video = cv2.VideoCapture(0) #若没有测试文件,可用当前摄像头
# 自定义卷积核--矩形,用于形态学处理
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
# 创建混合高斯模型用于背景建模
back = cv2.createBackgroundSubtractorMOG2()
# 读取每一帧并处理
while True:
ret, frame = video.read() #每次读取一帧,返回是否打开和每帧图像
img = back.apply(frame) #背景建模
# 开运算(先腐蚀后膨胀),去除噪声
img_close = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# 轮廓检测,获取最外层轮廓,只保留终点坐标
contours,hierarchy = cv2.findContours(img_close,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 计算轮廓外接矩形
for cnt in contours:
# 计算轮廓周长
length = cv2.arcLength(cnt,True)
if length>188:
# 得到外接矩形的要素
x,y,w,h = cv2.boundingRect(cnt)
# 画出这个矩形,在原视频帧图像上画,左上角坐标(x,y),右下角坐标(x+w,y+h)
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),2)
# 图像展示
cv2.imshow('frame',frame) # 原图
cv2.imshow('img',img) # 高斯模型图
# 设置关闭条件,一帧200毫秒
k = cv2.waitKey(100) & 0xff
if k == 27: #27代表退出键ESC
break
if k == 32: #32代表空格键,暂停
cv2.waitKey(0)
continue
# 释放资源
video.release()
cv2.destroyAllWindows()
某一帧的效果图如下: