如何使用背景差减法
如何使用背景差减法
一、概念
- 背景减法 (BS) 是一种常用且广泛使用的技术,用于使用静态摄像机生成前景蒙版(即包含属于场景中移动对象的像素的二值图像)。
- 顾名思义,BS 计算前景蒙版,在当前帧和背景模型之间进行减法运算,包含场景的静态部分,或者更一般地说,考虑到观察场景的特征,所有可以被视为背景的东西。
- 背景建模包括两个主要步骤:
- 背景初始化
- 背景更新
第一步,计算背景的初始模型,第二步更新模型以适应场景中可能发生的变化。
本小节中,我们主要学习如何使用 cv::VideoCapture从视频或图像序列中读入数据、使用 cv::BackgroundSubtractor 类创建或者更新背景模块、使用 cv::imshow 展示前景掩膜
二、代码
在下面您可以找到源代码。 我们将让用户选择处理视频文件或图像序列。 我们将在此示例中使用 cv::BackgroundSubtractorMOG2 来生成前景蒙版。 结果以及输入数据显示在屏幕上。
from __future__ import print_function
import cv2 as cv
import argparse
parser = argparse.ArgumentParser(description='This program shows how to use background subtraction methods provided by \
OpenCV. You can process both videos and images.')
parser.add_argument('--input', type=str, help='Path to a video or a sequence of image.', default='vtest.avi')
parser.add_argument('--algo', type=str, help='Background subtraction method (KNN, MOG2).', default='MOG2')
# 传入args = []直接使用默认参数
# 需要将视频文件放置在同目录下
args = parser.parse_args(args = [])
if args.algo == 'MOG2':
backSub = cv.createBackgroundSubtractorMOG2()
else:
backSub = cv.createBackgroundSubtractorKNN()
capture = cv.VideoCapture(cv.samples.findFileOrKeep(args.input))
if not capture.isOpened():
print('Unable to open: ' + args.input)
exit(0)
while True:
ret, frame = capture.read()
if frame is None:
break
fgMask = backSub.apply(frame)
cv.rectangle(frame, (10, 2), (100,20), (255,255,255), -1)
cv.putText(frame, str(capture.get(cv.CAP_PROP_POS_FRAMES)), (15, 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5 , (0,0,0))
cv.imshow('Frame', frame)
cv.imshow('FG Mask', fgMask)
keyboard = cv.waitKey(30)
if keyboard == 'q' or keyboard == 27:
break
# 最后要关闭所有窗口
capture.release
cv.destroyAllWindows()
三、解释
cv::BackgroundSubtractor 对象将用于生成前景蒙版。 在这个例子中,使用了默认参数,但也可以在 create 函数中声明特定的参数。
# 根据输入参数不同利用不同算法创建一个BS对象
if args.algo == 'MOG2':
backSub = cv.createBackgroundSubtractorMOG2()
else:
backSub = cv.createBackgroundSubtractorKNN()
使用VideoCapture对象读取输入视频或者图像序列
capture = cv.VideoCapture(cv.samples.findFileOrKeep(args.input))
# 在逐帧读取之前先确定视频是否已被打开
if not capture.isOpened():
print('Unable to open: ' + args.input)
exit(0)
每一帧都用于计算前景蒙版和更新背景。 如果要更改用于更新背景模型的学习率,可以通过将参数传递给 apply 方法来设置特定的学习率。
# 更新背景模块,得到前景掩膜(和原图像向与就得到前景ROI)
fgMask = backSub.apply(frame)
当前帧号可以从 cv::VideoCapture 对象中提取出来,并标记在当前帧的左上角。 白色矩形用于突出显示黑色的帧编号。
# 获取当前帧编号并将其写在帧的左上角,传入 -1 是填充整个矩形框
cv.rectangle(frame, (10, 2), (100,20), (255,255,255), -1)
cv.putText(frame, str(capture.get(cv.CAP_PROP_POS_FRAMES)), (15, 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5 , (0,0,0))
array([[[111, 147, 182],
[111, 147, 182],
[111, 147, 182],
...,
[ 55, 84, 112],
[ 65, 82, 107],
[ 44, 61, 86]],
[[110, 146, 181],
[110, 146, 181],
[110, 146, 181],
...,
[ 54, 83, 111],
[ 64, 81, 106],
[ 43, 60, 85]],
[[108, 144, 179],
[108, 144, 179],
[108, 144, 179],
...,
[ 61, 87, 117],
[ 67, 82, 105],
[ 45, 60, 83]],
...,
[[ 0, 31, 18],
[ 0, 31, 18],
[ 0, 32, 19],
...,
[ 18, 71, 56],
[ 23, 73, 59],
[ 23, 73, 59]],
[[ 0, 31, 21],
[ 0, 32, 22],
[ 0, 34, 24],
...,
[ 17, 66, 54],
[ 21, 70, 58],
[ 24, 73, 61]],
[[ 0, 32, 22],
[ 0, 33, 23],
[ 0, 37, 27],
...,
[ 23, 72, 60],
[ 25, 74, 62],
[ 26, 75, 63]]], dtype=uint8)
查看最终结果
# 显示原帧和前景掩膜
cv.imshow('Frame', frame)
cv.imshow('FG Mask', fgMask)
四、结果演示
def compare(imgs):
# for i in range(len(imgs)):
# imgs[i][:,-3:-1,:] = [255,255,255]
res = np.hstack(imgs)
cv_show('Compare', res)
def cv_show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
cv.destroyAllWindows()
# BackgroundSubtractor 是前景提取器,要加or
bs_mog2 = cv.createBackgroundSubtractorMOG2()
bs_knn = cv.createBackgroundSubtractorKNN()
# 读取视频
vdo = cv.VideoCapture('vtest.avi')
if not vdo.isOpened:
print('Can not open this file!')
exit(0)
# 逐帧处理并显示
while 1:
ret,frame = vdo.read()
frame = cv.resize(frame, (0,0), fx = 0.8, fy = 0.8)
if not ret:
break
fgmask_mog2 = bs_mog2.apply(frame)
fgmask_knn = bs_knn.apply(frame)
res_mog2 = cv.bitwise_and(frame.copy(), frame.copy(),mask = fgmask_mog2)
res_knn = cv.bitwise_and(frame.copy(), frame.copy(),mask = fgmask_knn)
cv.rectangle(frame,(10, 2), (70,20), (255,255,255), -1)
cv.putText(frame, str(vdo.get(cv.CAP_PROP_POS_FRAMES)), (15, 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5 , (255,0,0))
cv.imshow('Origin', frame)
cv.imshow('Mog2', res_mog2)
cv.imshow('Knn', res_knn)
if cv.waitKey(30)& 0xff == 27:
break
vdo.release()
cv.destroyAllWindows()
结果如下:
可以看出,knn提取出来的前景更加明了