- 计算机视觉
- 图像处理
- 机器学习
【免费下载链接】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命名空间访问。使用前需确认:
- 安装包含 contrib 模块的 OpenCV 发行包(
opencv-contrib-python),确保cv.bgsegm命名空间可用; - 准备一段静态摄像头拍摄的视频(教程示例使用
vtest.avi); - 引入
numpy与cv2两个基础库。
import numpy as np import cv2 as cvBackgroundSubtractorMOG:高斯混合模型背景分割
算法原理
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);各参数含义与默认值如下:
| 参数 | 默认值 | 含义与建议 |
|---|---|---|
history | 200 | 历史长度,即背景模型参考的历史帧数;在未显式指定学习率时,初始学习率按1/min(当前帧数, history)计算(见 bgfg_gaussmix.cpp 中apply实现) |
nmixtures | 5 | 每个像素使用的高斯混合分量个数 K。源码构造函数中会将传入值限制在(0, 8]区间内,即最大不超过 8 个分量(见 bgfg_gaussmix.cpp) |
backgroundRatio | 0.7 | 背景比例阈值 T。模型按权重 / 方差排序后,从权重最大的分量开始累加,累加权重首次超过该阈值之前的分量被认定为背景,之后的分量视为前景。源码中该值会被限制在(0, 1] |
noiseSigma | 0 | 噪声强度(亮度或各颜色通道的标准差)。传 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 的核心工作方式:
- 初始化阶段:使用前若干帧(默认 120 帧)进行背景建模。算法为每个像素维护一个"颜色直方图",记录该位置出现过的不同颜色及其累计权重;
- 贝叶斯推理:对每个像素,根据当前颜色在历史直方图中的权重与背景先验概率,用贝叶斯公式计算"该像素属于背景"的后验概率,后验概率过低则判为前景;
- 自适应更新:估计是自适应的——新观测比旧观测获得更高权重,以适应光照的缓慢变化;
- 形态学后处理:算法内部会对前景掩码做形态学滤波(源码中使用中值滤波
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 接口) | 默认值 | 含义与源码注释 |
|---|---|---|
maxFeatures | 64 | 每个像素直方图中维护的最大不同颜色数(稀疏性约束) |
defaultLearningRate | 0.025 | 学习率,取值 0.0 ~ 1.0,决定特征从直方图中被"遗忘"的速度;越接近 1 遗忘越慢,越接近 0 适应越快 |
numFrames | 120 | 用于初始化背景模型的帧数 |
quantizationLevels | 16 | 颜色空间量化级数,即每个通道直方图使用的离散等级数(1 ~ 255) |
backgroundPrior | 0.8 | 每个像素属于背景的先验概率,是一个灵敏度参数(0.0 ~ 1.0) |
decisionThreshold | 0.8 | 前景判定阈值,后验概率超过该值判为前景 |
smoothingRadius | 7 | 前景图去噪的中值滤波核半径(像素) |
updateBackgroundModel | true | 是否更新背景模型 |
minVal/maxVal | 0 / 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可冻结背景模型,适合背景完全静止的受控场景。
- 光照变化剧烈的场景正是 GMG 的强项,但代价是前
更多背景减除算法与实用示例程序
除了 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 验证了三件事:
- GMG 能处理不同类型(7 种深度)、不同尺寸(2~100 像素宽高)、1~4 通道的矩阵;
- 训练模式返回空前景掩码:在 120 帧训练期内,随机生成的模拟图像逐帧送入
apply后,掩码应全为 0(背景),与教程"前几帧黑屏"的说明一致; - 训练结束后异常帧应全部判为前景:训练期图像值域集中在上半区间,之后喂入覆盖全值域的随机图像,掩码应全为 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 与模块测试代码深入理解各自的行为与适用边界。
练习建议
- 用
createBackgroundSubtractorMOG(history=500, nmixtures=3)与默认参数分别运行同一段视频,观察前景掩码差异; - 修改 GMG 的
setNumFrames(30),观察黑屏初始化阶段缩短后的效果与误报变化; - 将开运算结构元素分别换成 3×3、5×5 椭圆,对比 GMG 输出掩码的噪声水平;
- 使用
SyntheticSequenceGenerator生成合成序列,在无真实视频的情况下评估上述算法的鲁棒性。
- 计算机视觉
- 图像处理
- 机器学习
【免费下载链接】opencv_contrib
相关推荐
OpenCV 背景减除(Background Subtraction)实战指南:bgsegm 模块的 MOG 与 GMG 算法详解
OpenCV 背景减除(Background Subtraction)实战指南:bgsegm 模块的 MOG 与 GMG 算法详解 导读 背景减除是视频分析中最
计算机视觉图像处理深度学习机器学习OpenCV bgsegm 模块实战:使用 MOG 与 GMG 算法进行背景减除
OpenCV bgsegm 模块实战:使用 MOG 与 GMG 算法进行背景减除 背景减除(Background Subtraction)是从静态相机视频流中提
计算机视觉图像处理深度学习机器学习OpenCV bgsegm 模块实战指南:GMG 贝叶斯背景减除算法原理、参数调优与代码示例
OpenCV bgsegm 模块实战指南:GMG 贝叶斯背景减除算法原理、参数调优与代码示例 导读 本文聚焦 OpenCV 扩展模块 bgsegm (Impro
计算机视觉图像处理机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考