运动目标检测——GMM高斯混合模型

对基于高斯混合模型的背景建模方法进行总结

1. 1999,Stauffer C & Grimson GMM

  • paper: Adaptive Background Mixture Models for Real-Time Tracking
  • 博客上的许多代码都是基于这个模型来写的;
  • bgslibrary的DPGrimsonGMMBGS代码
  • OpenCV中的MOG,位于之前的版本中。在OpenCV3.x的版本中没有MOG源码了。Opencv2.4.13.6的源码地址:opencv/modules/video/src/bgfg_gaussmix.cpp

2. 2001,P. KadewTraKuPong and R. Bowden

  • paper:An improved adaptive background mixture model for real-time tracking with shadow detection, Proc. 2nd European Workshp on Advanced Video-Based Surveillance Systems, 2001
  • bgslibrary的GUI里面有,就是mixtureOfGuassianV1.

简单描述: OpenCV实现的是这篇文章。他的创新点就是用EM初始化每个高斯模型的参数;传统的高斯模型存在学习速度慢,特别对于复杂的环境,并且不能区分移动的阴影和移动的目标。这篇文章提出在更新方程上做了一定的修改,在不同的阶段使用不同的方程,并且介绍了一种阴影检测的方法。

3. Zivkovic,2004,2006.

Paper:

  • (1) ICPR2004. Z.Zivkovic, Improved adaptive Gausian mixture model for background subtraction, International Conference Pattern Recognition, UK, August, 2004, The code is very fast and performs also shadow detection. Number of Gausssian components is adapted per pixel. 这篇文章的创新点是对混合高斯模型参数更新的方式,递归方程被用来不断的更新参数
  • (2) Z.Zivkovic, F. van der Heijden, Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction, Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006. The algorithm similar to the standard Stauffer&Grimson algorithm with additional selection of the number of the Gaussian components based on:
  • (3)PAMI2004. Z.Zivkovic, F.van der Heijden, Recursive unsupervised learning of finite mixture models, IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004.

code:

  • bgslibrary里的MixtureOfGaussianV2,调用了opencv2.4.13.6里面的opencv/modules/video/src/bgfg_gaussmix2.cpp源码的实现,MixtureOfGaussianV2只是调用了这个函数。

  • bgslibrary里的DPZivkovicAGMMBGS也实现的是上面的文献(2)的代码。

  • opencv3.4.0中MOG2的使用方法:https://docs.opencv.org/3.4/d1/dc5/tutorial_background_subtraction.html

参考pudn的代码,把mog2从opencv里面提出来单独运行。代码见后页

3.1 算法流程分析

  • I(k+1)是当前像素值;如果当前像素属于当前模型,则这个模型的权重增加α。
  •  ρ=α/w,用ρ来对当前像素加权,去更新均值和方差。权重越大,那么当前模型占主要模型,那么背景模型的均值和方差就更新的慢一点,当前像素对模型的更改要小一些,历史的权重要大一些。如果权重太小,那么当前像素对模型的贡献就要大一些,历史的权重就会小一些。
  • 如果α学习率很大,那么三个值就会增加的更快。模型的权重、均值、方差就会变化更大,即当前像素对模型的影响会更大,所以背景更新的会越快。更新的快就会更适应动态变化,将动态变化较快的融入到背景模型中,检测不会很灵敏
  • 所以α是权重的学习率,累加学习率;ρ是均值和方差的累加学习率。
  • 模型排队:按照权重来排队;Grimson中用w/σ来排队。
  • 检查是否为前景像素:权重累积<0.9 且 4*σ  不去访问最后的概率<0.1的模型
  • 检查是否属于这个背景模型:3*σ

3.2 效果分析

  • 静止变为运动后:需要20帧左右才会消除鬼影。说明新像素融入到模型中的,然后成为背景的实际需要20帧。更新率为0.005,20帧之后,20*0.005=0.1,增加了0.1的权重后,就可以变成背景。初始权重为1/2的样子。
  •  

 

main中的对象定义和使用;使用时mog2(current_gray, fore, learning_rate)

  • 构造函数给出了一些参数默认设置

4. Git desktop使用方法

https://docs.github.com/cn/desktop/getting-started-with-github-desktop 

https://www.cnblogs.com/schaepher/p/4823181.html

 

main

#include "mog2.h"   


#define  DEFAULT_WIDTH 352   
#define  DEFAULT_HEIGHT 288   

#include "malloc.h"   
#include "stdio.h"   
#include "stdlib.h"   

#include "math.h"   

#include "cv.h"   
#include "highgui.h"   
#include "cxcore.h"   
#include <cvaux.h>   

#include<cstdio>   
#include<iostream>   

#include <string.h>   

using namespace cv;
using namespace std;

using namespace ZWQ;

int  main()
{
	//char file_name[120] = { 0 };
	char save_name[120] = { 0 };
	char save_image_file[130] = { 0 };
	FILE*  pFile = fopen("videofile_mog.txt", "r+");
	int len = 0;


	Mat frame;
	Mat foreground;
	Mat background;

	BackgroundSubtractorMOG2_Z mog2;
	int num;
#if 0
	while (!feof(pFile))
	{

		memset(file_name, 0, sizeof(file_name));
		fgets(file_name, sizeof(file_name)-1, pFile);
		printf("%s", file_name);

		if (!feof(pFile))
		{
			memset(save_name, 0, sizeof(save_name));
			fgets(save_name, sizeof(save_name)-1, pFile);
			printf("%s\n", save_name);
			memset(save_image_file, 0, sizeof(save_image_file));
		}

		len = sizeof(file_name);
		while (len>0)
		{
			if ('\n' == file_name[len - 1])
			{
				file_name[len - 1] = '\0'; // ȥ��'\n'�ַ�     
				break;
			}
			len--;
		}

		len = sizeof(save_name);
		while (len>0)
		{
			if ('\n' == save_name[len - 1])
			{
				save_name[len - 1] = '\0'; // ȥ��'\n'�ַ�     
				break;
			}
			len--;
		}
#endif
		while (true){
			string file_name = "G:/202007_work/data_video/fire.avi";

			VideoCapture  capture(file_name);
			num = 1;
			while (capture.read(frame))
			{
				//sprintf(save_image_file, "%s%d.jpg", save_name, num);  //���ɱ���ͼƬ·��   
				std::vector<cv::Mat>SrcMatpart(frame.channels());      //生成与通道数数目相等的图像容器
				cv::split(frame, SrcMatpart);                                          //分解与通道数数目相等的图像容器
				mog2(SrcMatpart[0], foreground, 0.005);
				cv::medianBlur(foreground, foreground, 3);


				//std::vector<cv::Mat>mask(frame.channels());
				//for (int i = 0; i < frame.channels(); i++){
				//	foreground.copyTo(mask[i]);
				//	mask[i] /= 255;
				//	mask[i] = 1 - mask[i];
				//}

				//cv::Mat MergeMat;
				//cv::merge(mask, MergeMat);                                //合成与通道数数目相等的图像容器

				cv::Mat colorMask(frame.size(),frame.type(),Scalar(1,1,1));
				//colorMask = colorMask.mul(MergeMat);

				for (int i = 0; i < frame.rows;i++)
				for (int j = 0; j < frame.cols; j++){
					if (foreground.at<uchar>(i, j) == 0)
						colorMask.at<Vec3b>(i, j) = Vec3b(1,1,1);
					else
						colorMask.at<Vec3b>(i, j) = Vec3b(0, 0, 255);;
				}


				imshow("前景叠加在背景上", colorMask.mul(frame));
				//imwrite(save_image_file, foreground);
				imshow("fore", foreground);
				waitKey(500);   
				num++;
			}
		}
	fclose(pFile);
	system("pause");
	getchar();
	return 0;

	return  0;
}

 

mog2.h

#ifndef __MOG2_HPP__ 
#define __MOG2_HPP__ 


#include <list> 

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/video/background_segm.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdio.h>


#include "opencv2/core/mat.hpp"
#include "opencv2/core/internal.hpp"


using namespace std;
using namespace cv;


namespace ZWQ
{

	/*!
	The Base Class for Background/Foreground Segmentation

	The class is only used to define the common interface for
	the whole family of background/foreground segmentation algorithms.
	*/
	class CV_EXPORTS_W BackgroundSubtractor /*: public Algorithm*/
	{
	public:
		//! the virtual destructor 
		virtual ~BackgroundSubtractor();
		//! the update operator that takes the next video frame and returns the current foreground mask as 8-bit binary image. 
		CV_WRAP_AS(apply) virtual void operator()(InputArray image, OutputArray fgmask,
			double learningRate = 0);

		//! computes a background image 
		virtual void getBackgroundImage(OutputArray backgroundImage) const;
	};


	/*!
	The class implements the following algorithm:
	"Improved adaptive Gausian mixture model for background subtraction"
	Z.Zivkovic
	International Conference Pattern Recognition, UK, August, 2004.
	http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
	*/
	class CV_EXPORTS BackgroundSubtractorMOG2_Z : public BackgroundSubtractor
	{
	public:
		//! the default constructor 
		BackgroundSubtractorMOG2_Z();
		//! the full constructor that takes the length of the history, the number of gaussian mixtures, the background ratio parameter and the noise strength 
		BackgroundSubtractorMOG2_Z(int history, float varThreshold, bool bShadowDetection = true);
		//! the destructor 
		virtual ~BackgroundSubtractorMOG2_Z();
		//! the update operator 
		virtual void operator()(InputArray image, OutputArray fgmask, double learningRate = -1);

		//! computes a background image which are the mean of all background gaussians 
		virtual void getBackgroundImage(OutputArray backgroundImage) const;

		//! re-initiaization method 
		virtual void initialize(Size frameSize, int frameType);

		// virtual AlgorithmInfo* info() const; 

	protected:
		Size frameSize;
		int frameType;
		Mat bgmodel;
		Mat bgmodelUsedModes;//keep track of number of modes per pixel 
		int nframes;
		int history;
		int nmixtures;
		//! here it is the maximum allowed number of mixture components. 
		//! Actual number is determined dynamically per pixel 
		double varThreshold;
		// threshold on the squared Mahalanobis distance to decide if it is well described 
		// by the background model or not. Related to Cthr from the paper. 
		// This does not influence the update of the background. A typical value could be 4 sigma 
		// and that is varThreshold=4*4=16; Corresponds to Tb in the paper. 

		/ 
		// less important parameters - things you might change but be carefull 
		 
		float backgroundRatio;
		// corresponds to fTB=1-cf from the paper 
		// TB - threshold when the component becomes significant enough to be included into 
		// the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0. 
		// For alpha=0.001 it means that the mode should exist for approximately 105 frames before 
		// it is considered foreground 
		// float noiseSigma; 
		float varThresholdGen;
		//correspondts to Tg - threshold on the squared Mahalan. dist. to decide 
		//when a sample is close to the existing components. If it is not close 
		//to any a new component will be generated. I use 3 sigma => Tg=3*3=9. 
		//Smaller Tg leads to more generated components and higher Tg might make 
		//lead to small number of components but they can grow too large 
		float fVarInit;
		float fVarMin;
		float fVarMax;
		//initial variance  for the newly generated components. 
		//It will will influence the speed of adaptation. A good guess should be made. 
		//A simple way is to estimate the typical standard deviation from the images. 
		//I used here 10 as a reasonable value 
		// min and max can be used to further control the variance 
		float fCT;//CT - complexity reduction prior 
		//this is related to the number of samples needed to accept that a component 
		//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get 
		//the standard Stauffer&Grimson algorithm (maybe not exact but very similar) 

		//shadow detection parameters 
		bool bShadowDetection;//default 1 - do shadow detection 
		unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value 
		float fTau;
		// Tau - shadow threshold. The shadow is detected if the pixel is darker 
		//version of the background. 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 
		//See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. 
	};
}
#endif

mog2.cpp


//#include "precomp.hpp"   


#include "mog2.h"   

namespace ZWQ
{

	/*
	Interface of Gaussian mixture algorithm from:

	"Improved adaptive Gausian mixture model for background subtraction"
	Z.Zivkovic
	International Conference Pattern Recognition, UK, August, 2004
	http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf

	Advantages:
	-fast - number of Gausssian components is constantly adapted per pixel.
	-performs also shadow detection (see bgfg_segm_test.cpp example)

	*/

	// default parameters of gaussian background detection algorithm   
	static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2   
	static const float defaultVarThreshold2 = 4.0f*4.0f;
	static const int defaultNMixtures2 = 5; // maximal number of Gaussians in mixture   
	static const float defaultBackgroundRatio2 = 0.9f; // threshold sum of weights for background test   
	static const float defaultVarThresholdGen2 = 3.0f*3.0f;
	static const float defaultVarInit2 = 15.0f; // initial variance for new components   
	static const float defaultVarMax2 = 5 * defaultVarInit2;
	static const float defaultVarMin2 = 4.0f;

	// additional parameters   
	static const float defaultfCT2 = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components   
	static const unsigned char defaultnShadowDetection2 = (unsigned char)0; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection   
	static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation   

	struct GaussBGStatModel2Params
	{
		//image info   
		int nWidth;
		int nHeight;
		int nND;//number of data dimensions (image channels)   

		bool bPostFiltering;//defult 1 - do postfiltering - will make shadow detection results also give value 255   
		double  minArea; // for postfiltering   

		bool bInit;//default 1, faster updates at start   

		/   
		//very important parameters - things you will change   
		   
		float fAlphaT;
		//alpha - speed of update - if the time interval you want to average over is T   
		//set alpha=1/T. It is also usefull at start to make T slowly increase   
		//from 1 until the desired T   
		float fTb;
		//Tb - threshold on the squared Mahalan. dist. to decide if it is well described   
		//by the background model or not. Related to Cthr from the paper.   
		//This does not influence the update of the background. A typical value could be 4 sigma   
		//and that is Tb=4*4=16;   

		/   
		//less important parameters - things you might change but be carefull   
		   
		float fTg;
		//Tg - threshold on the squared Mahalan. dist. to decide   
		//when a sample is close to the existing components. If it is not close   
		//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.   
		//Smaller Tg leads to more generated components and higher Tg might make   
		//lead to small number of components but they can grow too large   
		float fTB;//1-cf from the paper   
		//TB - threshold when the component becomes significant enough to be included into   
		//the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.   
		//For alpha=0.001 it means that the mode should exist for approximately 105 frames before   
		//it is considered foreground   
		float fVarInit;
		float fVarMax;
		float fVarMin;
		//initial standard deviation  for the newly generated components.   
		//It will will influence the speed of adaptation. A good guess should be made.   
		//A simple way is to estimate the typical standard deviation from the images.   
		//I used here 10 as a reasonable value   
		float fCT;//CT - complexity reduction prior   
		//this is related to the number of samples needed to accept that a component   
		//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get   
		//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)   

		//even less important parameters   
		int nM;//max number of modes - const - 4 is usually enough   

		//shadow detection parameters   
		bool bShadowDetection;//default 1 - do shadow detection   
		unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result   
		float fTau;
		// Tau - shadow threshold. The shadow is detected if the pixel is darker   
		//version of the background. 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   
		//See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.   
	};

	struct GMM
	{
		float weight;
		float variance;
	};

	// shadow detection performed per pixel   
	// should work for rgb data, could be usefull for gray scale and depth data as well   
	// See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.   
	static CV_INLINE bool
		detectShadowGMM(const float* data, int nchannels, int nmodes,
		const GMM* gmm, const float* mean,
		float Tb, float TB, float tau)
	{
			float tWeight = 0;

			// check all the components  marked as background:   
			for (int mode = 0; mode < nmodes; mode++, mean += nchannels)
			{
				GMM g = gmm[mode];

				float numerator = 0.0f;
				float denominator = 0.0f;
				for (int c = 0; c < nchannels; c++)
				{
					numerator += data[c] * mean[c];
					denominator += mean[c] * mean[c];
				}

				// no division by zero allowed   
				if (denominator == 0)
					return false;

				// if tau < a < 1 then also check the color distortion   
				if (numerator <= denominator && numerator >= tau*denominator)
				{
					float a = numerator / denominator;
					float dist2a = 0.0f;

					for (int c = 0; c < nchannels; c++)
					{
						float dD = a*mean[c] - data[c];
						dist2a += dD*dD;
					}

					if (dist2a < Tb*g.variance*a*a)
						return true;
				};

				tWeight += g.weight;
				if (tWeight > TB)
					return false;
			};
			return false;
		}

	//update GMM - the base update function performed per pixel   
	//   
	//"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"   
	//Z.Zivkovic, F. van der Heijden   
	//Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.   
	//   
	//The algorithm similar to the standard Stauffer&Grimson algorithm with   
	//additional selection of the number of the Gaussian components based on:   
	//   
	//"Recursive unsupervised learning of finite mixture models "   
	//Z.Zivkovic, F.van der Heijden   
	//IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004   
	//http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf   

	struct MOG2Invoker
	{
		MOG2Invoker(const Mat& _src, Mat& _dst,
		GMM* _gmm, float* _mean,
		uchar* _modesUsed,
		int _nmixtures, float _alphaT,
		float _Tb, float _TB, float _Tg,
		float _varInit, float _varMin, float _varMax,
		float _prune, float _tau, bool _detectShadows,
		uchar _shadowVal)
		{
			src = &_src;//原始图像
			dst = &_dst;//前景图像
			gmm0 = _gmm;//背景模型换为1维数组
			mean0 = _mean;//背景模型的数据的尾指针
			modesUsed0 = _modesUsed;//已经使用了的模型的数据指针
			nmixtures = _nmixtures;//模型个数
			alphaT = _alphaT;//学习率
			Tb = _Tb;//方差阈值
			TB = _TB;//背景比例
			Tg = _Tg;//方差阈值
			varInit = _varInit;//初始化方差
			varMin = MIN(_varMin, _varMax);//最小方差4
			varMax = MAX(_varMin, _varMax);//最大方差75
			prune = _prune;//-学习率 * CT
			tau = _tau;
			detectShadows = _detectShadows;
			shadowVal = _shadowVal;
			shadowVal = false;

			cvtfunc = src->depth() != CV_32F ? getConvertFunc(src->depth(), CV_32F) : 0;
		}


		/*void operator()(const Range& range) const*/

		void operator()(const BlockedRange& range) const
		{

			int y0 = range.begin(), y1 = range.end();
			int ncols = src->cols, nchannels = src->channels();
			AutoBuffer<float> buf(src->cols*nchannels);//每一行的缓存,定义变量并分配空间
			float alpha1 = 1.f - alphaT;
			float dData[CV_CN_MAX];

			for (int y = y0; y < y1; y++)
			{
				const float* data = buf;
				if (cvtfunc)//data是buf的指针,buf变量赋值
					cvtfunc(src->ptr(y), src->step, 0, 0, (uchar*)data, 0, Size(ncols*nchannels, 1), 0);//把src的每一行取出来给Data
				else
					data = src->ptr<float>(y);

				float* mean = mean0 + ncols*nmixtures*nchannels*y;//背景模型初始指针mean0, mean是尾指针;乘以y后就变成了尾指针。下一行均值所在位置的指针。
				GMM* gmm = gmm0 + ncols*nmixtures*y;//头指针,每行的模型的第一个指针
				uchar* modesUsed = modesUsed0 + ncols*y;//已经用了几个模型了
				uchar* mask = dst->ptr(y);//获取行指针
				//data是每一行的像素值,gmm是背景模型的指针,每一行的每一个;mean是每一个像素的
				for (int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels)
				{
					//calculate distances to the modes (+ sort)  
					//here we need to go in descending order!!!  //降序排列
					bool background = false;//return value -> true - the pixel classified as background  

					//internal:  fitsPDF如果当前像素不属于任何一个模型,就重新建一个模型
					bool fitsPDF = false;//if it remains zero a new GMM mode will be added  
					int nmodes = modesUsed[x], nNewModes = nmodes;//current number of modes in GMM  
					float totalWeight = 0.f;

					float* mean_m = mean;

					//  
					//go through all modes  
					for (int mode = 0; mode < nmodes; mode++, mean_m += nchannels)
					{
						float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found  
						int swap_count = 0;
						  
						//fit not found yet  
						if (!fitsPDF)
						{
							//check if it belongs to some of the remaining modes  
							float var = gmm[mode].variance;

							//calculate difference and distance  
							float dist2;

							if (nchannels == 3)
							{
								dData[0] = mean_m[0] - data[0];
								dData[1] = mean_m[1] - data[1];
								dData[2] = mean_m[2] - data[2];
								dist2 = dData[0] * dData[0] + dData[1] * dData[1] + dData[2] * dData[2];
							}
							else
							{
								dist2 = 0.f;
								for (int c = 0; c < nchannels; c++)
								{
									dData[c] = mean_m[c] - data[c];
									dist2 += dData[c] * dData[c];
								}
							}

							//background? - Tb - usually larger than Tg  
							if (totalWeight < TB && dist2 < Tb*var)
								background = true;

							//check fit  
							if (dist2 < Tg*var)
							{
								/  
								//belongs to the mode  
								fitsPDF = true;

								//update distribution  

								//update weight  
								weight += alphaT;
								float k = alphaT / weight;

								//update mean  
								for (int c = 0; c < nchannels; c++)
									mean_m[c] -= k*dData[c];

								//update variance  
								float varnew = var + k*(dist2 - var);
								//limit the variance  
								varnew = MAX(varnew, varMin);
								varnew = MIN(varnew, varMax);
								gmm[mode].variance = varnew;

								//sort  
								//all other weights are at the same place and  
								//only the matched (iModes) is higher -> just find the new place for it  
								for (int i = mode; i > 0; i--)
								{
									//check one up  
									if (weight < gmm[i - 1].weight)
										break;

									swap_count++;
									//swap one up  
									std::swap(gmm[i], gmm[i - 1]);
									for (int c = 0; c < nchannels; c++)
										std::swap(mean[i*nchannels + c], mean[(i - 1)*nchannels + c]);
								}
								//belongs to the mode - bFitsPDF becomes 1  
								/  
							}
						}//!bFitsPDF)  

						//check prune  
						if (weight < -prune)
						{
							weight = 0.0;
							nmodes--;
						}

						gmm[mode - swap_count].weight = weight;//update weight by the calculated value  
						totalWeight += weight;
					}
					//go through all modes  
					//  

					//renormalize weights  
					totalWeight = 1.f / totalWeight;
					for (int mode = 0; mode < nmodes; mode++)
					{
						gmm[mode].weight *= totalWeight;
					}

					nmodes = nNewModes;

					//make new mode if needed and exit  
					if (!fitsPDF)
					{
						// replace the weakest or add a new one  
						int mode = nmodes == nmixtures ? nmixtures - 1 : nmodes++;

						if (nmodes == 1)
							gmm[mode].weight = 1.f;
						else
						{
							gmm[mode].weight = alphaT;

							// renormalize all other weights  
							for (int i = 0; i < nmodes - 1; i++)
								gmm[i].weight *= alpha1;
						}

						// init  
						for (int c = 0; c < nchannels; c++)
							mean[mode*nchannels + c] = data[c];

						gmm[mode].variance = varInit;

						//sort  
						//find the new place for it  
						for (int i = nmodes - 1; i > 0; i--)
						{
							// check one up  
							if (alphaT < gmm[i - 1].weight)
								break;

							// swap one up  
							std::swap(gmm[i], gmm[i - 1]);
							for (int c = 0; c < nchannels; c++)
								std::swap(mean[i*nchannels + c], mean[(i - 1)*nchannels + c]);
						}
					}

					//set the number of modes  
					modesUsed[x] = uchar(nmodes);
					mask[x] = background ? 0 :
						detectShadows && detectShadowGMM(data, nchannels, nmodes, gmm, mean, Tb, TB, tau) ?
					shadowVal : 255;
				}
			}
		}


		const Mat* src;
		Mat* dst;
		GMM* gmm0;
		float* mean0;
		uchar* modesUsed0;

		int nmixtures;
		float alphaT, Tb, TB, Tg;
		float varInit, varMin, varMax, prune, tau;

		bool detectShadows;
		uchar shadowVal;

		BinaryFunc cvtfunc;
	};

	BackgroundSubtractor::~BackgroundSubtractor()
	{
	}

	void BackgroundSubtractor::operator()(InputArray image, OutputArray fgmask,
		double learningRate)
	{
	}

	void BackgroundSubtractor::getBackgroundImage(OutputArray backgroundImage) const
	{
	}

	BackgroundSubtractorMOG2_Z::BackgroundSubtractorMOG2_Z()
	{
		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_Z::BackgroundSubtractorMOG2_Z(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;
	}

	BackgroundSubtractorMOG2_Z::~BackgroundSubtractorMOG2_Z()
	{
	}


	void BackgroundSubtractorMOG2_Z::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);
	}

	void BackgroundSubtractorMOG2_Z::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(BlockedRange(0, image.rows),
			MOG2Invoker(image, fgmask,
			(GMM*)bgmodel.data,//bgmodel是Mat类型,分配的是(1,rows*cols*5*(2+1))的内存,然后将其data换为GMM格式
			(float*)(bgmodel.data + sizeof(GMM)*nmixtures*image.rows*image.cols),//三维数据,每一个数据都是GMM类型的。
			bgmodelUsedModes.data, nmixtures, (float)learningRate,
			(float)varThreshold,
			backgroundRatio, varThresholdGen,
			fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau,
			bShadowDetection, nShadowDetection));
	}

	void BackgroundSubtractorMOG2_Z::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, "");
		}
	}


}

/* End of file. */

 

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值