OpenCV Tutorials 22 - 背景差减法

616 阅读4分钟

如何使用背景差减法

如何使用背景差减法

一、概念

  1. 背景减法 (BS) 是一种常用且广泛使用的技术,用于使用静态摄像机生成前景蒙版(即包含属于场景中移动对象的像素的二值图像)。
  2. 顾名思义,BS 计算前景蒙版,在当前帧和背景模型之间进行减法运算,包含场景的静态部分,或者更一般地说,考虑到观察场景的特征,所有可以被视为背景的东西。

download.png

  1. 背景建模包括两个主要步骤:
    • 背景初始化
    • 背景更新

第一步,计算背景的初始模型,第二步更新模型以适应场景中可能发生的变化。

本小节中,我们主要学习如何使用 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()

download.png

三、解释

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()

结果如下:

download.png

可以看出,knn提取出来的前景更加明了