OpenCV_基于混合高斯模型GMM的运动目标检测
OpenCV的video module中包含了几种较为常用的背景减除方法,其中混合高斯模型(Gaussian of Mixture Models, GMM)方法效果较好。
常用的目标检测方法:1)帧间差分;2)背景减除;
其中背景减除方法的关键在于建立一个鲁棒的背景模型(背景图像),常用的建立背景模型方法有:
1)均值法;2)中值法;3)滑动平均滤波法;4)单高斯;5)混合高斯模型;6)codebook,等。
混合高斯模型的原理:
每个像素的R、G、B三个通道像素值的变化分别由一个混合高斯模型分布来刻画。这样的好处在于,同一个像素位置处可以呈现多个模态的像素值变化(例如水波纹,晃动的叶子等)。
GMM的出处:Adaptive background mixture models for real-time tracking (1999年由Chris Stau er提出)
OpenCV版本:2.4.2
下面的代码实现了基于GMM的运动目标检测,同时能够消除运动阴影; (基于文献:Improved adaptive Gausian mixture model for background subtraction)
- // 基于混合高斯模型的运动目标检测
- // Author: www.icvpr.com
- // Blog: http://blog.csdn.net/icvpr
- #include <iostream>
- #include <string>
- #include <opencv2/opencv.hpp>
- int main(int argc, char** argv)
- {
- std::string videoFile = "../test.avi";
- cv::VideoCapture capture;
- capture.open(videoFile);
- if (!capture.isOpened())
- {
- std::cout<<"read video failure"<<std::endl;
- return -1;
- }
- cv::BackgroundSubtractorMOG2 mog;
- cv::Mat foreground;
- cv::Mat background;
- cv::Mat frame;
- long frameNo = 0;
- while (capture.read(frame))
- {
- ++frameNo;
- std::cout<<frameNo<<std::endl;
- // 运动前景检测,并更新背景
- mog(frame, foreground, 0.001);
- // 腐蚀
- cv::erode(foreground, foreground, cv::Mat());
- // 膨胀
- cv::dilate(foreground, foreground, cv::Mat());
- mog.getBackgroundImage(background); // 返回当前背景图像
- cv::imshow("video", foreground);
- cv::imshow("background", background);
- if (cv::waitKey(25) > 0)
- {
- break;
- }
- }
- return 0;
- }
OpenCV中实现了两个版本的高斯混合背景/前景分割方法(Gaussian Mixture-based Background/Foreground Segmentation Algorithm)[1-2],调用接口很明朗,效果也很好。
BackgroundSubtractorMOG 使用示例
- int main(){
- VideoCapture video("1.avi");
- Mat frame,mask,thresholdImage, output;
- video>>frame;
- BackgroundSubtractorMOG bgSubtractor(20,10,0.5,false);
- while(true){
- video>>frame;
- ++frameNum;
- bgSubtractor(frame,mask,0.001);
- imshow("mask",mask);
- waitKey(10);
- }
- return 0;
- }
构造函数可以使用默认构造函数或带形参的构造函数:
- BackgroundSubtractorMOG::BackgroundSubtractorMOG()
- BackgroundSubtractorMOG::BackgroundSubtractorMOG(int history, int nmixtures,
- double backgroundRatio, double noiseSigma=0)
其中history为使用历史帧的数目,nmixtures为混合高斯数量,backgroundRatio为背景比例,noiseSigma为噪声权重。
而调用的接口只有重载操作符():
- void BackgroundSubtractorMOG::operator()(InputArray image, OutputArray fgmask, double learningRate=0)
以下是使用BackgroundSubtractorMOG进行前景/背景检测的一个截图。
BackgroundSubtractorMOG2 使用示例
- int main(){
- VideoCapture video("1.avi");
- Mat frame,mask,thresholdImage, output;
- //video>>frame;
- BackgroundSubtractorMOG2 bgSubtractor(20,16,true);
- while(true){
- video>>frame;
- ++frameNum;
- bgSubtractor(frame,mask,0.001);
- cout<<frameNum<<endl;
- //imshow("mask",mask);
- //waitKey(10);
- }
- return 0;
- }
同样的,构造函数可以使用默认构造函数和带形参的构造函数
- BackgroundSubtractorMOG2::BackgroundSubtractorMOG2()
- BackgroundSubtractorMOG2::BackgroundSubtractorMOG2(int history,
- float varThreshold, bool bShadowDetection=true )
history同上,varThreshold表示马氏平方距离上使用的来判断是否为背景的阈值(此值不影响背景更新速率),bShadowDetection表示是否使用阴影检测(如果开启阴影检测,则mask中使用127表示阴影)。
使用重载操作符()调用每帧检测函数:
- void BackgroundSubtractorMOG2::operator()(InputArray image, OutputArray fgmask, double learningRate=-1)
同时BackgroundSubtractorMOG2提供了getBackgroundImage()函数用以返回背景图像:
- void BackgroundSubtractorMOG2::getBackgroundImage(OutputArray backgroundImage)
另外OpenCV的refman中说新建对象以后还有其他和模型油有关的参数可以修改,不过比较坑的是opencv把这个这些函数参数声明为protected,同时没有提供访问接口,所以要修改的话还是要自己修改源文件提供访问接口。
- 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.
以下是使用BackgroundSubtractorMOG2检测的前景和背景:
参考文献:
[1] KaewTraKulPong, Pakorn, and Richard Bowden. "An improved adaptive background mixture model for real-time tracking with shadow detection." Video-Based Surveillance Systems. Springer US, 2002. 135-144.
[2] Zivkovic, Zoran. "Improved adaptive Gaussian mixture model for background subtraction." Pattern Recognition, 2004. ICPR 2004. Proceedings of the 17th International Conference on. Vol. 2. IEEE, 2004.
(转载请注明作者和出处:http://blog.csdn.net/xiaowei_cqu 未经允许请勿用于商业用途)
转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7566070
在做火灾检测时尝试了一下用OpenCV中自带的高斯混合背景建模,由于是快速应用,也没推敲太细,日后有机会再研究吧。
如果只是应用,不推敲原理是很简单的。OpenCV自己有一个高斯模型,源代码在安装目录...\opencvsetuppath\src\cvaux的cvbgfg_gaussmix.cpp文件中。
具体算法是根据P. KaewTraKulPong and R. Bowden的论文《An Improved Adaptive Background Mixture Model for Real-time Tracking with Shadow Detection》实现的
源码看起来很简单,除了构造、析构函数,不过几个函数:
void BackgroundSubtractorMOG::initialize(Size _frameSize, int _frameType);
//处理单通道图像
static void process8uC1( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fgmask, double learningRate );
//处理多通道图像
static void process8uC3( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fgmask, double learningRate );
//重载构造符(在这里转由 process8uC1 或 process8uC3 处理)
void BackgroundSubtractorMOG::operator()(const Mat& image, Mat& fgmask, double learningRate);
//释放高斯模型
icvReleaseGaussianBGModel( CvGaussBGModel** bg_model );
//更新模型
icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel* bg_model, double learningRate );
//建立模型
cvCreateGaussianBGModel( IplImage* first_frame, CvGaussBGStatModelParams* parameters );
//初始化函数
void BackgroundSubtractorMOG::initialize(Size _frameSize, int _frameType);
//处理单通道图像
static void process8uC1( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fgmask, double learningRate );
//处理多通道图像
static void process8uC3( BackgroundSubtractorMOG& obj, const Mat& image, Mat& fgmask, double learningRate );
//重载构造符(在这里转由 process8uC1 或 process8uC3 处理)
void BackgroundSubtractorMOG::operator()(const Mat& image, Mat& fgmask, double learningRate);
//释放高斯模型
icvReleaseGaussianBGModel( CvGaussBGModel** bg_model );
//更新模型
icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel* bg_model, double learningRate );
//建立模型
cvCreateGaussianBGModel( IplImage* first_frame, CvGaussBGStatModelParams* parameters );
如果我们不关心具体的参数,都是用默认值的话,很容易就能建立一个高斯背景提取的模型。
//用第一帧初始化模型
CvBGStatModel* bgModel= cvCreateGaussianBGModel(pInitFrame);
//
void FireDetector::CheckFireMove2(IplImage *pImgFrame)
{
cvUpdateBGStatModel( pImgFrame, bgModel );
// 以左下角为坐标原点
bgModel->foreground->origin = bgModel->background->origin = 1;
//滤除噪声
//cvErode(bgModel->background, bgModel->background);
//cvErode(bgModel->foreground, bgModel->foreground);
cvMorphologyEx( pImgMotion, pImgMotion, 0, 0, CV_MOP_CLOSE, 3);
cvMorphologyEx( pImgMotion, pImgMotion, 0, 0, CV_MOP_OPEN, 1 );
}
//用第一帧初始化模型
CvBGStatModel* bgModel= cvCreateGaussianBGModel(pInitFrame);
//
void FireDetector::CheckFireMove2(IplImage *pImgFrame)
{
cvUpdateBGStatModel( pImgFrame, bgModel );
// 以左下角为坐标原点
bgModel->foreground->origin = bgModel->background->origin = 1;
//滤除噪声
//cvErode(bgModel->background, bgModel->background);
//cvErode(bgModel->foreground, bgModel->foreground);
cvMorphologyEx( pImgMotion, pImgMotion, 0, 0, CV_MOP_CLOSE, 3);
cvMorphologyEx( pImgMotion, pImgMotion, 0, 0, CV_MOP_OPEN, 1 );
}
如果再简化就是
cvUpdateBGStatModel( pImgFrame, bgModel );
一句话,用新的帧更新背景。
有关高斯背景的原理,日后再补上吧~