首先

  在这一章节中,我主要想要进行介绍的是关于运动背景分割法(Background Segment,BS)方法的介绍。
  其主要进行的是:通过不同方法拟合各种模型来建立起背景图像。之后再将当前帧与背景图像进行相减,从而得到运动的区域,该思想与时间背景中值差分的思想相似,感兴趣的读者可以前往进行查看:时间中值滤波
  假设我们当前已经获得了某个背景图片,此时镜头内会出现障碍物,例如车辆或其他移动物体出现在摄像头内并遮挡背景,这种时候我们就可以使用这种方法来得到我们视野内的有关运动物体的位置。
  基于这种思想,在OpenCV库(此处使用的是OpenCV-contrib)中我们可以使用相关的背景分割器来进行相关的操作。

方法选择

  这里我们主要进行涉及的有以下几种方式的选择:
  ·GMG:基于像素颜色进行背景建模,其主要依赖的是像素点的数值。
  ·CNT:基于像素点计数进行背景建模,其主要依赖的是像素点相同数值的计数值。
  ·KNN:基于K最近邻进行背景建模,根据附近颜色的相似度来进行的背景提取。
  ·MOG:基于混合高斯进行背景建模。
  ·MOG2:基于混合高斯进行背景建模,MOG的升级版本。

具体实现

  在这个部分中,我使用了一个模板视频,其名为vtest.avi,与py文件在同一目录下。需要vtest.avi的,可以去如下网址下载:
  https://pan.baidu.com/s/1mNAsa4QotXqohDRHSGIHig
  提取码:yr2m
  首先,我们可以把所有的算法都放在一个列表当中:

suanfa = [
    (cv2.bgsegm.createBackgroundSubtractorGMG(20, 0.7), 'GMG'),
    (cv2.bgsegm.createBackgroundSubtractorCNT(), 'CNT'),
    (cv2.createBackgroundSubtractorKNN(), 'KNN'),
    (cv2.bgsegm.createBackgroundSubtractorMOG(), 'MOG'),
    (cv2.createBackgroundSubtractorMOG2(), 'MOG2'),
]

  之后我们可以根据列表中的索引,来采用各种各样的背景提取算法。
  构建好算法列表后,即可使用index索引来进行算法的采用。完整代码如下所示:

import cv2
from time import *

suanfa = [
    (cv2.bgsegm.createBackgroundSubtractorGMG(20, 0.7), 'GMG'),
    (cv2.bgsegm.createBackgroundSubtractorCNT(), 'CNT'),
    (cv2.createBackgroundSubtractorKNN(), 'KNN'),
    (cv2.bgsegm.createBackgroundSubtractorMOG(), 'MOG'),
    (cv2.createBackgroundSubtractorMOG2(), 'MOG2'),
]

def main():
    # 背景分割识别器序号
    algo_index = 0
    subtractor = suanfa[algo_index][0]
    videoPath = "vtest.avi"
    show_fgmask = False
    # 读取视频
    capture = cv2.VideoCapture(videoPath)
    # 当前帧数
    frame_num = 0
    # 总执行时间
    sum_Time = 0.0
    while True:
        ret, frame = capture.read()
        if not ret:
            return
        begin_time = time()
        fgmask = subtractor.apply(frame)
        end_time = time()
        run_time = end_time - begin_time
        sum_Time = sum_Time + run_time
        # 平均执行时间
        average_Time = sum_Time / (frame_num + 1)

        if show_fgmask:
            segm = fgmask
        else:
            segm = (frame * 0.5).astype('uint8')
            cv2.add(frame, (0, 50, 50, 0), segm, fgmask)
        # 显示当前方法
        cv2.putText(segm, suanfa[algo_index][1], (10, 30), cv2.FONT_HERSHEY_PLAIN, 2.0, (0, 0, 255),
                    2,
                    cv2.LINE_AA)
        # 显示当前每帧执行时间
        cv2.putText(segm, "averageTime {} s".format(average_Time), (10, 90), cv2.FONT_HERSHEY_PLAIN, 2.0, (255, 0, 0), 2, cv2.LINE_AA)
        cv2.imshow('img', segm)
        key = cv2.waitKey(1) & 0xFF
        frame_num = frame_num + 1
        if key == ord('q'):  # 按'q'健退出循环
            break
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()

  这里我们选择的index序号为0,表示的是0号算法,也就是GMG,其运行结果如下图所示。

  可以看到的是有人走过的地方,已经用浅黄色进行了标识。
  但此时,图像中有很多的噪点,此处可以使用cv2.erode来进行去噪,改进代码如下所示:

import cv2
import numpy as np
from time import *

suanfa = [
    (cv2.bgsegm.createBackgroundSubtractorGMG(20, 0.7), 'GMG'),
    (cv2.bgsegm.createBackgroundSubtractorCNT(), 'CNT'),
    (cv2.createBackgroundSubtractorKNN(), 'KNN'),
    (cv2.bgsegm.createBackgroundSubtractorMOG(), 'MOG'),
    (cv2.createBackgroundSubtractorMOG2(), 'MOG2'),
]

def main():
    # 背景分割识别器序号
    algo_index = 0
    subtractor = suanfa[algo_index][0]
    videoPath = "vtest.avi"
    show_fgmask = False
    # 读取视频
    capture = cv2.VideoCapture(videoPath)
    # 当前帧数
    frame_num = 0
    # 总执行时间
    sum_Time = 0.0
    while True:
        ret, frame = capture.read()
        if not ret:
            return
        begin_time = time()
        fgmask = subtractor.apply(frame)

        kernel = np.ones((3, 3), np.uint8)
        fgmask = cv2.erode(fgmask, kernel, iterations=1)

        end_time = time()
        run_time = end_time - begin_time
        sum_Time = sum_Time + run_time
        # 平均执行时间
        average_Time = sum_Time / (frame_num + 1)

        if show_fgmask:
            segm = fgmask
        else:
            segm = (frame * 0.5).astype('uint8')
            cv2.add(frame, (0, 50, 50, 0), segm, fgmask)

        # 显示当前方法
        cv2.putText(segm, suanfa[algo_index][1], (10, 30), cv2.FONT_HERSHEY_PLAIN, 2.0, (0, 0, 255),
                    2,
                    cv2.LINE_AA)
        # 显示当前每帧执行时间
        cv2.putText(segm, "averageTime {} s".format(average_Time), (10, 90), cv2.FONT_HERSHEY_PLAIN, 2.0, (255, 0, 0), 2, cv2.LINE_AA)
        cv2.imshow('img', segm)
        frame_num = frame_num + 1
        if cv2.waitKey(1) == ord('q'):
            break
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()

  其运行结果如下图所示。

  可以看出,其结果已经比之前好了不少。
  如果想要进行方法的修改的话,只需要修改代码中的algo_index即可,如果我改为1,其运行结果则会变成如下图所示。

  此时算法变成了CNT,可以看出的是,其结果比他要好许多。

总结

  如果追求速度的话,可以尝试使用CNT,MOG2,KNN。
  如果追求质量的话,可以使用MOG2,KNN。
  总的来说实际应用中,MOG2用的最多,KNN其次,CNT一般用于Raspberry Pi和多检测任务中。
  本文到此结束,谢谢大家。