opencv背景建模mog2源码剖析
来源:互联网 发布:Java 线程池 newsingle 编辑:程序博客网 时间:2024/06/06 18:21
前言
opencv实现的背景建模方法有很多,早期的opencv版本modules/video/src下有acmmm2003(2.3.1版本)、codebook(2.3.1版本)、gmg(2.4.8版本)、gaussmix、gassmix2方法。这两天下了个opencv3.0beta版本,video模块中只有gaussmix、gassmix2被留了下来,其他的方法都被丢到legacy模块去了,legacy模块一般是放即将废弃或老旧的算法的,这也说明了mog、mog2方法依旧老当益壮。(3.0版本还多了个KNN的背景建模算法,暂时没有多余的时间去研究)网上有很多相关的高斯背景建模原理介绍,就不再多说,这里主要记录下这两天对mog2代码的理解。
不同建模算法对比
运行速度上,mog2有明显的优势,下图引用自OpenCV中背景建模方法mog2——Adaptive GMM算法小结:mog mog2 gmg 电脑1 26904 14386 25533 电脑2 26947 14578 28834算法结果参考了官方教程,目测效果mog/mog2应该比gmg好点:Original Frame(Below image shows the 200th frame of a video):Result of BackgroundSubtractorMOG:Result of BackgroundSubtractorMOG2(Gray color region shows shadow region.):Result of BackgroundSubtractorGMG:
MOG2算法结构
看了2.3.1、2.4.8、3.0beta三个版本的mog2源码,差别不大。2.4.8代码相对简洁点,3.0beta增加了很多与算法本身无关的接口,因此算法源代码主要参考的是2.4.8版本。下面是算法接口的使用示例:
//Example usage with as cpp classBackgroundSubtractorMOG2 bg_model;//For each new image the model is updates using:bg_model(img, fgmask);可以看到,完成mog2运算只需两个行代码,一是算法对象构建,而是执行算法。相关源码如下
1 对象构建
有两个构建函数,一个是默认参数,另一个使用你指定的参数,构建主要任务就是参数配置。
BackgroundSubtractorMOG2::BackgroundSubtractorMOG2(){ frameSize = Size(0,0); frameType = 0; nframes = 0; history = defaultHistory2; varThreshold = defaultVarThreshold2; bShadowDetection = 1; nmixtures = defaultNMixtures2; backgroundRatio = defaultBackgroundRatio2; fVarInit = defaultVarInit2; fVarMax = defaultVarMax2; fVarMin = defaultVarMin2; varThresholdGen = defaultVarThresholdGen2; fCT = defaultfCT2; nShadowDetection = defaultnShadowDetection2; fTau = defaultfTau;}
BackgroundSubtractorMOG2::BackgroundSubtractorMOG2(int _history, float _varThreshold, bool _bShadowDetection){ frameSize = Size(0,0); frameType = 0; nframes = 0; history = _history > 0 ? _history : defaultHistory2; varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2; bShadowDetection = _bShadowDetection; nmixtures = defaultNMixtures2; backgroundRatio = defaultBackgroundRatio2; fVarInit = defaultVarInit2; fVarMax = defaultVarMax2; fVarMin = defaultVarMin2; varThresholdGen = defaultVarThresholdGen2; fCT = defaultfCT2; nShadowDetection = defaultnShadowDetection2; fTau = defaultfTau;}history:如果不手动设置learningRate,history就被用于计算当前的learningRate,此时history越大,learningRate越小,背景更新越慢
varThreshold[Tb]:方差阈值,用于判断当前像素是前景还是背景
nmixtures:高斯模型个数,默认5个
backgroundRatio[TB]:高斯背景模型权重和阈值,nmixtures个模型按权重排序后,只取模型权重累加值大于backgroundRatio的前几个作为背景模型。也就是说如果该值取得非常小,很可能只使用权重最大的高斯模型作为背景(因为仅一个模型权重就大于backgroundRatio了)
fVarInit:新建高斯模型的方差初始值,默认15
fVarMax:背景更新过程中,用于限制高斯模型方差的最大值,默认20
fVarMin:背景更新过程中,用于限制高斯模型方差的最小值,默认4
varThresholdGen[Tg]:方差阈值,用于是否存在匹配的模型,如果不存在则新建一个
fCT:prune?
fTau: Tau is a threshold on how much darker the shadow can be.Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
2 算法执行
void BackgroundSubtractorMOG2::operator()(InputArray _image, OutputArray _fgmask, double learningRate){ Mat image = _image.getMat(); bool needToInitialize = nframes == 0 || learningRate >= 1 || image.size() != frameSize || image.type() != frameType; if( needToInitialize ) initialize(image.size(), image.type()); _fgmask.create( image.size(), CV_8U ); Mat fgmask = _fgmask.getMat(); ++nframes; learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./min( 2*nframes, history ); CV_Assert(learningRate >= 0); parallel_for_(Range(0, image.rows), MOG2Invoker(image, fgmask, (GMM*)bgmodel.data, (float*)(bgmodel.data + sizeof(GMM)*nmixtures*image.rows*image.cols), bgmodelUsedModes.data, nmixtures, (float)learningRate, (float)varThreshold, backgroundRatio, varThresholdGen, fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau, bShadowDetection, nShadowDetection));}在13行可以看到前面说的history与learningRate的计算式,有3种情况:
1) bg_model(img, fgmask)或bg_model(img, fgmask,-1)。函数输入learningRate为-1,learningRate按history值计算,learningRate=1/min(2*nframes,history)。
2) bg_model(img, fgmask,0)。函数输入learningRate为0,背景模型停止更新。
3) bg_model(img, fgmask,n)。n在0~1之间,背景模型更新速度为n,n越大更新越快,算法内部表现为当前帧参与背景更新的权重越大。
mog2算法主要在MOG2Invoker内实现。
3 mog2背景模型数据结构
1)mog2的背景模型数据分配函数:
void BackgroundSubtractorMOG2::initialize(Size _frameSize, int _frameType){ frameSize = _frameSize; frameType = _frameType; nframes = 0; int nchannels = CV_MAT_CN(frameType); CV_Assert( nchannels <= CV_CN_MAX ); // for each gaussian mixture of each pixel bg model we store ... // the mixture weight (w), // the mean (nchannels values) and // the covariance bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F ); //make the array for keeping track of the used modes per pixel - all zeros at start bgmodelUsedModes.create(frameSize,CV_8U); bgmodelUsedModes = Scalar::all(0);}主要有两个数据块,bgmodel和bgmodelUsedModes。前者即整个算法维护的背景模型数据,后者用于记录对应像素坐标位置所使用的高斯模型个数。
bgmodel的大小为 frameSize.height*frameSize.width*nmixtures*(2 + nchannels)。
从作者的注释中也可以看出,bgmodel里存放的是些什么东西:高斯模型的权重weight,均值mean,方差covariance。这些即是高斯背景建模的核心。
其中权重和方差以下列结构体表示,刚好2个单位的float大小:
struct GMM{ float weight; float variance;};而每个像素通道对应一个均值,刚好有nchannels个单位的float大小。
但要弄清楚这些数据是怎么排列的,还需要看下一个函数,获取mog2背景结果的函数。
2)mog2的背景结果函数:
void BackgroundSubtractorMOG2::getBackgroundImage(OutputArray backgroundImage) const{ int nchannels = CV_MAT_CN(frameType); CV_Assert( nchannels == 3 ); Mat meanBackground(frameSize, CV_8UC3, Scalar::all(0)); int firstGaussianIdx = 0; const GMM* gmm = (GMM*)bgmodel.data; const Vec3f* mean = reinterpret_cast<const Vec3f*>(gmm + frameSize.width*frameSize.height*nmixtures); for(int row=0; row<meanBackground.rows; row++) { for(int col=0; col<meanBackground.cols; col++) { int nmodes = bgmodelUsedModes.at<uchar>(row, col); Vec3f meanVal; float totalWeight = 0.f; for(int gaussianIdx = firstGaussianIdx; gaussianIdx < firstGaussianIdx + nmodes; gaussianIdx++) { GMM gaussian = gmm[gaussianIdx]; meanVal += gaussian.weight * mean[gaussianIdx]; totalWeight += gaussian.weight; if(totalWeight > backgroundRatio) break; } meanVal *= (1.f / totalWeight); meanBackground.at<Vec3b>(row, col) = Vec3b(meanVal); firstGaussianIdx += nmixtures; } } switch(CV_MAT_CN(frameType)) { case 1: { vector<Mat> channels; split(meanBackground, channels); channels[0].copyTo(backgroundImage); break; } case 3: { meanBackground.copyTo(backgroundImage); break; } default: CV_Error(CV_StsUnsupportedFormat, ""); }}}
const Vec3f* mean = reinterpret_cast<const Vec3f*>(gmm + frameSize.width*frameSize.height*nmixtures);从上面这行可以看出均值mean整块数据是放在gmm(权重、方差)后面的,终于,我们得到了mog2中背景模型bgmodel的数据分布结构:
MOG2的背景更新过程
待续。。
MOG2使用示例
引用自 \安装目录\opencv2.4.8\sources\samples\cpp\bgfg_segm.cpp
//this is a sample for foreground detection functionsint main(int argc, const char** argv){help();CommandLineParser parser(argc, argv, keys);bool useCamera = parser.get<bool>("camera");string file = parser.get<string>("file_name"); VideoCapture cap; bool update_bg_model = true; if( useCamera ) cap.open(0); elsecap.open(file.c_str());parser.printParams(); if( !cap.isOpened() ) { printf("can not open camera or video file\n"); return -1; } namedWindow("image", CV_WINDOW_NORMAL); namedWindow("foreground mask", CV_WINDOW_NORMAL); namedWindow("foreground image", CV_WINDOW_NORMAL); namedWindow("mean background image", CV_WINDOW_NORMAL); BackgroundSubtractorMOG2 bg_model; Mat img, fgmask, fgimg; for(;;) { cap >> img; if( img.empty() ) break; if( fgimg.empty() ) fgimg.create(img.size(), img.type()); //update the model bg_model(img, fgmask, update_bg_model ? -1 : 0); fgimg = Scalar::all(0); img.copyTo(fgimg, fgmask); Mat bgimg; bg_model.getBackgroundImage(bgimg); imshow("image", img); imshow("foreground mask", fgmask); imshow("foreground image", fgimg); if(!bgimg.empty()) imshow("mean background image", bgimg ); char k = (char)waitKey(30); if( k == 27 ) break; if( k == ' ' ) { update_bg_model = !update_bg_model; if(update_bg_model) printf("Background update is on\n"); else printf("Background update is off\n"); } } return 0;}
0 0
- opencv背景建模mog2源码剖析
- OpenCV中背景建模方法mog2——Adaptive GMM算法小结
- 利用MOG2背景模型提取运动目标的OpenCV代码
- opencv codebook背景建模
- opencv-背景建模
- 【Python+OpenCV】目标跟踪-背景分割器:KNN、MOG2和GMG
- opencv 高斯背景建模
- 单高斯背景建模opencv仿真
- 背景建模-均值法(Matlab)高斯背景建模(opencv)
- OpenCV中GPU版MOG2的使用
- 【OpenCV】高斯混合背景建模
- 【OpenCV】高斯混合背景建模
- OpenCV移动平均法背景建模
- Opencv学习总结1-背景建模
- opencv学习系列(五)--- 背景建模
- OpenCV:利用背景建模检测运动物体
- 【OpenCV】高斯混合背景建模
- OpenCV混合高斯背景建模
- 入门: 如何在xampp中运行php文件 (WINDOWS)
- 基于链表的四位存储大数计算
- 嵌入式学习方法及概要
- 简易解说拉格朗日对偶(Lagrange duality)
- 让数据库变快的10个建议
- opencv背景建模mog2源码剖析
- 【数据结构】链式存储单链表
- 解决thinkphp display输出问题
- Perl Pack写的一个数据报表程序
- struts2中改变struts.xml默认路径
- android系统之sensor学习
- 多线程的几种实现方法详解
- 06-1. 简单计算器(20)
- iOS之UIView常用方法总结