☰
OpenCV bgsegm 模块背景减除实战指南:MOG 与 GMG 算法原理、参数配置与代码实现详解
2026/9/25 2:27:32 网站建设 项目流程
  • 计算机视觉
  • 图像处理
  • 机器学习

【免费下载链接】opencv_contrib

项目地址:https://gitcode.com/gh_mirrors/ope/opencv_contrib
点击查看免费下载

背景减除(Background Subtraction)是众多视觉应用的前置关键步骤,其目标是从静态摄像头画面中分离出移动前景。本文以 opencv_contrib 仓库中 bgsegm 模块的背景减除教程 为主体,系统讲解 bgsegm 提供的两种经典算法——高斯混合模型(GMM)类的BackgroundSubtractorMOG与基于统计背景估计和贝叶斯逐像素分割的BackgroundSubtractorGMG,并结合模块源码与测试用例深入剖析其参数含义、底层实现与调参建议。读完本文,你将能够独立编写可运行的背景减除程序,理解两类算法的适用场景,并掌握利用源码与测试验证算法行为的方法。

背景减除:问题定义与挑战

在许多基于视觉的应用中,首要任务是从画面中把"人"或"车"单独提取出来,再做后续的跟踪、计数或行为分析。典型场景包括:

  • 访客计数器:静态摄像头统计进出房间的人数;
  • 交通摄像头:从固定机位的画面中提取车辆信息;
  • 安防监控:检测进入监控区域的活动目标。

技术上,这些场景都可以归结为从静态背景中提取移动前景。如果手里恰好有一张"纯背景"图像(没有访客的房间、没有车辆的道路),任务就非常简单——用当前帧减去背景图即可得到前景目标。但大多数情况下我们拿不到这样的干净背景,必须从已有的视频序列中在线估计背景模型,这就引出了背景减除算法的核心问题:如何在有前景物体持续出现的条件下,逐步建立并维护一个可靠的背景模型。

更麻烦的是阴影问题:阴影会随着物体一起移动,简单做像素差分会把阴影也标记成前景,导致前景结果被污染。因此,一个好的背景减除算法还需要具备一定的阴影与光照鲁棒性。

针对上述需求,bgsegm 模块(完整模块头文件见 bgsegm.hpp,模块教程索引见 table_of_content_bgsegm.markdown)提供了多种改进的背景/前景分割算法。本文重点讲解其中的BackgroundSubtractorMOG和BackgroundSubtractorGMG两种,其余算法(CNT、GSOC、LSBP 等)将在文末补充介绍。

运行环境与准备

教程中的示例代码采用 Python + OpenCV 接口,所有算法均通过cv.bgsegm命名空间访问。使用前需确认:

  1. 安装包含 contrib 模块的 OpenCV 发行包(opencv-contrib-python),确保cv.bgsegm命名空间可用;
  2. 准备一段静态摄像头拍摄的视频(教程示例使用vtest.avi);
  3. 引入numpy与cv2两个基础库。
import numpy as np import cv2 as cv

BackgroundSubtractorMOG:高斯混合模型背景分割

算法原理

BackgroundSubtractorMOG是一种基于高斯混合模型(Gaussian Mixture Model)的背景/前景分割算法,源自 P. KadewTraKuPong 与 R. Bowden 于 2001 年发表的论文An improved adaptive background mixture model for real-time tracking with shadow detection。

其核心思想是:对每一个背景像素用一个由 K 个高斯分布组成的混合模型来刻画(论文建议 K 取 3 到 5)。每个高斯分量对应场景中该位置可能出现的某种"颜色状态",各分量的权重表示该颜色在场景中停留的时间比例:

  • 停留时间久、更静态的颜色→ 权重高,被认为是"可能的背景颜色";
  • 频繁变化、短暂出现的颜色 → 权重低,更可能属于前景。

每一帧到来时,算法将当前像素值与各高斯分量逐一匹配,命中则更新该分量的均值、方差与权重,并依据权重阈值重新判定该像素属于背景还是前景;未命中则用当前像素值替换权重最低的分量。整个模型是自适应的——背景会随场景缓慢变化而持续更新。

创建函数与参数

使用cv.bgsegm.createBackgroundSubtractorMOG()创建背景减除对象。根据 bgsegm.hpp 中createBackgroundSubtractorMOG的声明,其完整签名为:

Ptr<BackgroundSubtractorMOG> createBackgroundSubtractorMOG(int history=200, int nmixtures=5, double backgroundRatio=0.7, double noiseSigma=0);

各参数含义与默认值如下:

参数默认值含义与建议
history200历史长度,即背景模型参考的历史帧数;在未显式指定学习率时,初始学习率按1/min(当前帧数, history)计算(见 bgfg_gaussmix.cpp 中apply实现)
nmixtures5每个像素使用的高斯混合分量个数 K。源码构造函数中会将传入值限制在(0, 8]区间内,即最大不超过 8 个分量(见 bgfg_gaussmix.cpp)
backgroundRatio0.7背景比例阈值 T。模型按权重 / 方差排序后,从权重最大的分量开始累加,累加权重首次超过该阈值之前的分量被认定为背景,之后的分量视为前景。源码中该值会被限制在(0, 1]
noiseSigma0噪声强度(亮度或各颜色通道的标准差)。传 0 表示使用自动值,源码中默认取30 * 0.5 = 15(见 bgfg_gaussmix.cpp)

此外,源码中还定义了两个内部常量:方差匹配阈值varThreshold = 2.5 * 2.5与新建分量的初始权重defaultInitialWeight = 0.05。对象创建后,可通过getHistory/setHistory、getNMixtures/setNMixtures、getBackgroundRatio/setBackgroundRatio、getNoiseSigma/setNoiseSigma四组接口在运行时读写参数。

底层实现细节(源码级)

阅读 bgfg_gaussmix.cpp 可以印证上述原理:

  • 数据布局:每个像素的背景模型以MixData结构(排序键sortKey = weight/sqrt(var)、权重weight、均值mean、方差var)连续存储在bgmodel中,模型矩阵尺寸为1 × (height × width × nmixtures × (2 + 2×通道数))的 32F 类型(见initialize)。
  • 匹配判定:像素值pix与第 k 个高斯分量的马氏距离平方d2 = (pix - mean)²满足d2 < varThreshold × var即视为命中;命中后按alpha(学习率)更新权重、均值、方差,并按新的排序键插入有序列表。
  • 前景判定:按排序键从大到小累加权重,累加和首次超过backgroundRatio的位置记为kForeground;若命中的分量索引kHit >= kForeground,则该像素标记为前景(输出 255),否则为背景(输出 0)。
  • 学习率策略:apply(image, fgmask, learningRate)中未显式传入学习率(默认 0)时,实际学习率取1/min(当前帧数, history),使模型在初始阶段快速收敛、后期缓慢适应。
  • 输入限制:该实现只支持 8 位 1 通道(灰度)与 8 位 3 通道(BGR)图像,其他格式会抛出StsUnsupportedFormat异常;同时该实现不提供getBackgroundImage支持(调用即报StsNotImplemented)。

Python 示例代码

下面的示例来自教程原文:打开视频,逐帧调用fgbg.apply(frame)得到前景掩码fgmask,并实时显示。按Esc(ASCII 27)退出。

import numpy as np import cv2 as cv cap = cv.VideoCapture('vtest.avi') fgbg = cv.bgsegm.createBackgroundSubtractorMOG() while(1): ret, frame = cap.read() fgmask = fgbg.apply(frame) cv.imshow('frame',fgmask) k = cv.waitKey(30) & 0xff if k == 27: break cap.release() cv.destroyAllWindows()

提示:OpenCV 主库中更新、更完善的cv.createBackgroundSubtractorMOG2()也基于高斯混合模型,并额外支持阴影检测,可参考主仓库的 background subtraction 教程(对应文档@ref tutorial_background_subtraction)对比使用。

BackgroundSubtractorGMG:统计背景估计 + 逐像素贝叶斯分割

算法原理

BackgroundSubtractorGMG结合了统计背景图像估计与逐像素贝叶斯分割两种思想,由 Andrew B. Godbehere、Akihiro Matsukawa 与 Ken Goldberg 在 2012 年发表于Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive Audio Art Installation(美国控制会议 ACC)。据论文所述,该系统曾于 2011 年 3 月 31 日至 7 月 31 日在旧金山当代犹太博物馆支撑了一个名为"Are We There Yet?"的交互式音频艺术装置成功运行——这是一个在可变光照条件下进行访客跟踪的真实落地案例,也正是 GMG 算法对光照变化鲁棒的由来。

GMG 的核心工作方式:

  1. 初始化阶段:使用前若干帧(默认 120 帧)进行背景建模。算法为每个像素维护一个"颜色直方图",记录该位置出现过的不同颜色及其累计权重;
  2. 贝叶斯推理:对每个像素,根据当前颜色在历史直方图中的权重与背景先验概率,用贝叶斯公式计算"该像素属于背景"的后验概率,后验概率过低则判为前景;
  3. 自适应更新:估计是自适应的——新观测比旧观测获得更高权重,以适应光照的缓慢变化;
  4. 形态学后处理:算法内部会对前景掩码做形态学滤波(源码中使用中值滤波medianBlur),去除零散噪声。

创建函数与参数

使用cv.bgsegm.createBackgroundSubtractorGMG()创建对象。根据 bgsegm.hpp 的声明:

Ptr<BackgroundSubtractorGMG> createBackgroundSubtractorGMG(int initializationFrames=120, double decisionThreshold=0.8);

其中initializationFrames是用于初始化背景模型的帧数(默认 120),decisionThreshold是前景判定阈值(默认 0.8,高于该值判为前景)。其余参数通过BackgroundSubtractorGMG的 set/get 接口配置,源码 bgfg_gmg.cpp 中BackgroundSubtractorGMGImpl构造函数给出的完整默认值如下:

参数(set/get 接口)默认值含义与源码注释
maxFeatures64每个像素直方图中维护的最大不同颜色数(稀疏性约束)
defaultLearningRate0.025学习率,取值 0.0 ~ 1.0,决定特征从直方图中被"遗忘"的速度;越接近 1 遗忘越慢,越接近 0 适应越快
numFrames120用于初始化背景模型的帧数
quantizationLevels16颜色空间量化级数,即每个通道直方图使用的离散等级数(1 ~ 255)
backgroundPrior0.8每个像素属于背景的先验概率,是一个灵敏度参数(0.0 ~ 1.0)
decisionThreshold0.8前景判定阈值,后验概率超过该值判为前景
smoothingRadius7前景图去噪的中值滤波核半径(像素)
updateBackgroundModeltrue是否更新背景模型
minVal/maxVal0 / 0图像序列像素的最小/最大值(0 表示自动推断:8U 取 255、16U 取 65535、32F 取 1.0)

底层实现细节(源码级)

阅读 bgfg_gmg.cpp 可确认以下行为:

  • 数据布局:每个像素维护nfeatures(特征计数)、colors(最多maxFeatures个颜色值)与weights(对应权重)三张表;颜色在存入前先按quantizationLevels做量化(见Quantization::apply)。
  • 贝叶斯公式:核心判定语句为posterior = (weight × backgroundPrior) / (weight × backgroundPrior + (1 − weight) × (1 − backgroundPrior)), 当(1 − posterior) > decisionThreshold时判为前景(GMG_LoopBody::operator())。直方图中越常见的颜色,其后验概率越高,越不容易被判成前景。
  • 训练模式:frameNum < numInitializationFrames期间只做训练式更新(插入颜色、权重累加,最后一帧归一化直方图),不输出前景——这正是教程中所说"前几帧你会看到全黑窗口"的原因。
  • 更新策略:更新背景模型时,所有已有权重乘以(1 − learningRate),当前颜色以learningRate的权重插入(insertFeature),超出maxFeatures时丢弃最旧特征。
  • 并行与去噪:逐像素处理通过parallel_for_并行化(GMG_LoopBody实现ParallelLoopBody);若smoothingRadius > 0,帧末对掩码执行medianBlur。
  • 输入支持:支持 8U、16U、32F 深度以及 1/3/4 通道的图像(比 MOG 更宽)。

Python 示例代码

由于 GMG 输出的原始掩码可能残留噪声,教程建议在apply之后再对掩码执行一次形态学开运算(先腐蚀后膨胀),以去除小的噪点。示例使用 3×3 椭圆结构元素:

import numpy as np import cv2 as cv cap = cv.VideoCapture('vtest.avi') kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE,(3,3)) fgbg = cv.bgsegm.createBackgroundSubtractorGMG() while(1): ret, frame = cap.read() fgmask = fgbg.apply(frame) fgmask = cv.morphologyEx(fgmask, cv.MORPH_OPEN, kernel) cv.imshow('frame',fgmask) k = cv.waitKey(30) & 0xff if k == 27: break cap.release() cv.destroyAllWindows()

效果对比

以下三幅图取自教程原文,展示视频第 200 帧的处理结果(图片来源:modules/bgsegm/tutorials/bgsegm_bg_subtraction/images/):

可以看到:MOG 输出的前景掩码较为干净紧凑;GMG 输出经形态学开运算去除噪声后,同样能较好地勾勒出行人轮廓,两者都能有效分离出移动行人目标。

参数调优与实战建议

结合两类算法的源码行为,给出以下调参思路:

  • MOG 调参:
    • 场景背景变化快(如树叶晃动、水面波动)时,适当增大history,让模型更稳定;场景突变(如摄像机切换)时减小history或用较大的learningRate强制重新初始化;
    • nmixtures越大表达能力越强,但计算与内存开销也越大,通常 3 ~ 5 即可;
    • backgroundRatio过小会把更多像素判为前景(噪声增多),过大则可能吞掉慢速运动的前景;
    • noiseSigma影响噪声容忍度,默认 0(自动 15)在多数场景下够用。
  • GMG 调参:
    • 光照变化剧烈的场景正是 GMG 的强项,但代价是前numFrames(默认 120)帧只能看到黑屏,且需要较长的初始化时间;若可接受,可适当减小numFrames以加快收敛;
    • decisionThreshold越高越保守(减少误报),越低越敏感(减少漏报);
    • 前景内孔洞较多时,可把开运算结构元素从 3×3 增大到 5×5 等;反之噪声多时可增大smoothingRadius(内部中值滤波核半径);
    • updateBackgroundModel设为false可冻结背景模型,适合背景完全静止的受控场景。

更多背景减除算法与实用示例程序

除了 MOG 与 GMG,bgsegm 模块在 bgsegm.hpp 中还提供了其他算法,可按需选用:

  • BackgroundSubtractorCNT:基于像素计数的轻量算法,源码注释称其在高端系统上与 MOG2 速度相当,在廉价硬件(如树莓派 3)上比 MOG2 快一倍以上,适合嵌入式场景;
  • BackgroundSubtractorGSOC:GSOC 期间实现的算法,非源自论文,注释称其在 CDNET 2014 数据集上相比 OpenCV 内其他算法表现更好;
  • BackgroundSubtractorLSBP:基于局部 SVD 二值模式(Local SVD Binary Pattern)的背景减除算法;
  • SyntheticSequenceGenerator:合成视频帧序列生成器,可在给定背景与物体图像基础上生成带波浪形背景畸变的测试序列(createSyntheticSequenceGenerator(background, object, amplitude=2.0, wavelength=20.0, wavespeed=0.2, objspeed=6.0)),用于在无真实视频时调试背景减除算法。

模块还附带一个可直接编译运行的交互式示例程序 samples/bgfg.cpp,它封装了 GMG、CNT、KNN、MOG、MOG2、GSOC、LSBP 七种算法,支持以下键盘操作,非常适合横向对比各算法效果:

  • 按c:循环切换算法;
  • 按m:切换显示纯前景掩码或"幽灵叠加"效果(前景叠加在原帧上的可视化);
  • 按n:切换 OpenCV 线程数(1 ~ 8);
  • 按空格:切换显示延时(30ms 与 1ms);
  • 按q或Esc:退出。

测试用例如何验证算法行为

bgsegm 模块的测试代码可以帮我们确认上述算法行为,例如 test/test_backgroundsubtractor_gbh.cpp 针对 GMG 验证了三件事:

  1. GMG 能处理不同类型(7 种深度)、不同尺寸(2~100 像素宽高)、1~4 通道的矩阵;
  2. 训练模式返回空前景掩码:在 120 帧训练期内,随机生成的模拟图像逐帧送入apply后,掩码应全为 0(背景),与教程"前几帧黑屏"的说明一致;
  3. 训练结束后异常帧应全部判为前景:训练期图像值域集中在上半区间,之后喂入覆盖全值域的随机图像,掩码应全为 255(前景)。

类似地,test/test_backgroundsubtractor_lsbp.cpp 通过把输入整体放大 10 倍来验证 LSBP 描述子对光照强度变化的不变性。这些测试既是对实现正确性的保障,也是理解算法语义的绝佳教材。

总结

本文围绕 bgsegm 模块的两大经典背景减除算法展开:

  • MOG(KadewTraKuPong & Bowden, 2001):以 K 个高斯混合模型刻画每个像素,权重反映颜色停留时间,快速、参数少,适合一般监控场景;
  • GMG(Godbehere et al., 2012):以逐像素颜色直方图 + 贝叶斯推理实现统计背景估计,对光照变化更鲁棒,但需要较长的初始化帧数,且建议配合形态学开运算使用。

两者都通过cv.bgsegm.createBackgroundSubtractorXXX()创建、以apply()逐帧输出前景掩码(255 为前景、0 为背景),接口一致、易于替换。读者可在 bgfg.cpp 交互示例中对比七种算法,并结合 bgfg_gaussmix.cpp、bgfg_gmg.cpp 与模块测试代码深入理解各自的行为与适用边界。

练习建议

  1. 用createBackgroundSubtractorMOG(history=500, nmixtures=3)与默认参数分别运行同一段视频,观察前景掩码差异;
  2. 修改 GMG 的setNumFrames(30),观察黑屏初始化阶段缩短后的效果与误报变化;
  3. 将开运算结构元素分别换成 3×3、5×5 椭圆,对比 GMG 输出掩码的噪声水平;
  4. 使用SyntheticSequenceGenerator生成合成序列,在无真实视频的情况下评估上述算法的鲁棒性。
  • 计算机视觉
  • 图像处理
  • 机器学习

【免费下载链接】opencv_contrib

项目地址:https://gitcode.com/gh_mirrors/ope/opencv_contrib
点击查看免费下载

相关推荐

上一篇:Skip框架终极指南:用Swift实现iOS与Android双平台开发
下一篇:MiddleClick-Sonoma:重新定义 macOS 中键点击体验的终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询