各位同学好,今天和大家分享一下opencv背景建模相关操作。主要介绍两种背景建模方法,帧差法和混合高斯模型。
案例简介:现有一份路口摄像机拍摄的行人流视频,通过背景建模方法,区分背景和前景,完成行人识别。
数据集链接:https://pan.baidu.com/s/1xPg1nQ0Ic_P-mGvdFD0t2w 提取码:1234
背景建模需要满足一定条件,保证摄像机拍摄位置不变,保证背景是基本不发生变化的。如路口的监控摄像机,只有车流人流等前景部分能发生移动,而马路树木等背景不能发生移动。
判断视频中前景和背景的方法主要有两种:帧差法、混合高斯模型
如下图,摄像机拍摄获取一帧一帧的连续图像,需要摄像机识别出,哪些部分是运动的,哪些部分是静止的。在摄像机拍摄位置不变的前提下,一个物体位置发生了变化,那么像素点数值也会发生变化;不发生运动的物体,它的像素点数值不基本会发生的变化。
由于场景中的目标在运动,位置产生了差异,摄像头捕捉到的图像不同。该算法通过对时间上连续的两帧图像进行差分算法,不同帧对应的像素点相减,判断灰度差异的绝对值的变化情况,当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。
代表像素点坐标, 代表第n帧图像, 代表第n-1帧图像,差分结果为 。如果差分结果大于阈值 ,将该像素点指定成255白点,代表前景;否则指定为0,代表背景。
第一,如果图像中存在噪音点,帧差法会引入噪音。第二,如上图右侧,人的轮廓是白色,但轮廓内是黑色的,因为人在前后两帧图像中的位置变化很小,会存在空洞问题。帧差法不常用。
在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯模型的个数可以自适应。然后在测试阶段对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。由于整个过程GMM模型在不断更新学习中,所以对动态背景具有一定的鲁棒性,整体的检测效果会得到提升。
可以简单理解一下。在图像中判断前景和背景,比如在运动过程中,人服从一种分布,背景也服从一种分布。判断视频中的像素点符合哪一种分布,符合运动的就判断是前景,符合静止的就判断为背景。
然而,背景的实际分布应当是多个高斯分布混合在一起的,每个高斯模型也有其对应的权重。下图左侧是图像背景满足的两种分布,得到右侧的结果也是不同的,两种分布需要对应不同的权重。虽然整体的背景是由多个高斯分布融合在一起的,但每个分布所占的权重不一定都相同。权重是由图像中像素点的个数决定的。
对于视频中某一块区域的一个像素点来说,虽然在视频中的位置不发生改变,但它的值会发生变化。像素点值的变化情况应当符合高斯分布。
1. 首先初始化每个高斯模型矩阵参数。均值,标准差
2. 取视频中T帧数据图像,用来训练高斯混合模型。来了第一个像素点之后用它来当作第一个高斯分布。如:
3. 当后面来的像素值,与前面已有的高斯的均值比较,如果该像素点的值与其模型均值差在3倍的方差内,则属于该分布,并对其进行参数更新。
如:第二帧的像素点的值为105,比较 ,因此第二帧的像素点属于第一帧像素的分布,因此这两个数据归纳在一起,更新第一个分布的均值,标准差。
4. 如果下次来的像素点不满足当前的高斯分布,用它来创建一个新的高斯分布。
如:第三帧的像素点的值为200,不满足更新后,那么创建一个新的高斯分布。如:
一般情况下,高斯分布的个数设置3-5个,过多的话结果不好
在测试阶段,对新来的像素点的值,与学习好了的混合高斯模型中的每一个均值比较,如果差值在2倍的方差之间,就认为是背景,否则认为是前景。将前景赋值为255,背景赋值为0
shape: 卷积核形状。矩形:MORPH_RECT,十字型:MORPH_CROSS,椭圆形MORPH_ELLIPSE
ksize: 卷积核大小
anchor:坐标(x,y),元素内的锚定位置。默认值为(-1,-1),即结构化元素的中心。
import numpy as np
import cv2
# 指定文件夹
filepath = 'C:\\Users\\admin\\.spyder-py3\\test\\opencv\\img\\class'
# 获取文件夹中某个视频
video = cv2.VideoCapture(filepath + '\\test.avi')
# 自定义卷积核--矩形,用于形态学处理
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
# 释放资源
video.release()
cv2.destroyAllWindows()
某一帧的效果图如下