Path: blob/master/modules/video/src/bgfg_gaussmix2.cpp
16337 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Copyright (C) 2013, OpenCV Foundation, all rights reserved.14// Third party copyrights are property of their respective owners.15//16// Redistribution and use in source and binary forms, with or without modification,17// are permitted provided that the following conditions are met:18//19// * Redistribution's of source code must retain the above copyright notice,20// this list of conditions and the following disclaimer.21//22// * Redistribution's in binary form must reproduce the above copyright notice,23// this list of conditions and the following disclaimer in the documentation24// and/or other materials provided with the distribution.25//26// * The name of the copyright holders may not be used to endorse or promote products27// derived from this software without specific prior written permission.28//29// This software is provided by the copyright holders and contributors "as is" and30// any express or implied warranties, including, but not limited to, the implied31// warranties of merchantability and fitness for a particular purpose are disclaimed.32// In no event shall the Intel Corporation or contributors be liable for any direct,33// indirect, incidental, special, exemplary, or consequential damages34// (including, but not limited to, procurement of substitute goods or services;35// loss of use, data, or profits; or business interruption) however caused36// and on any theory of liability, whether in contract, strict liability,37// or tort (including negligence or otherwise) arising in any way out of38// the use of this software, even if advised of the possibility of such damage.39//40//M*/4142/*//Implementation of the Gaussian mixture model background subtraction from:43//44//"Improved adaptive Gaussian mixture model for background subtraction"45//Z.Zivkovic46//International Conference Pattern Recognition, UK, August, 200447//http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf48//The code is very fast and performs also shadow detection.49//Number of Gausssian components is adapted per pixel.50//51// and52//53//"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"54//Z.Zivkovic, F. van der Heijden55//Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.56//57//The algorithm similar to the standard Stauffer&Grimson algorithm with58//additional selection of the number of the Gaussian components based on:59//60//"Recursive unsupervised learning of finite mixture models "61//Z.Zivkovic, F.van der Heijden62//IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 200463//http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf64//65//66//Example usage with as cpp class67// BackgroundSubtractorMOG2 bg_model;68//For each new image the model is updates using:69// bg_model(img, fgmask);70//71//Example usage as part of the CvBGStatModel:72// CvBGStatModel* bg_model = cvCreateGaussianBGModel2( first_frame );73//74// //update for each frame75// cvUpdateBGStatModel( tmp_frame, bg_model );//segmentation result is in bg_model->foreground76//77// //release at the program termination78// cvReleaseBGStatModel( &bg_model );79//80//Author: Z.Zivkovic, www.zoranz.net81//Date: 7-April-2011, Version:1.082///////////*/8384#include "precomp.hpp"85#include "opencl_kernels_video.hpp"8687namespace cv88{8990/*91Interface of Gaussian mixture algorithm from:9293"Improved adaptive Gaussian mixture model for background subtraction"94Z.Zivkovic95International Conference Pattern Recognition, UK, August, 200496http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf9798Advantages:99-fast - number of Gausssian components is constantly adapted per pixel.100-performs also shadow detection (see bgfg_segm_test.cpp example)101102*/103104// default parameters of gaussian background detection algorithm105static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2106static const float defaultVarThreshold2 = 4.0f*4.0f;107static const int defaultNMixtures2 = 5; // maximal number of Gaussians in mixture108static const float defaultBackgroundRatio2 = 0.9f; // threshold sum of weights for background test109static const float defaultVarThresholdGen2 = 3.0f*3.0f;110static const float defaultVarInit2 = 15.0f; // initial variance for new components111static const float defaultVarMax2 = 5*defaultVarInit2;112static const float defaultVarMin2 = 4.0f;113114// additional parameters115static const float defaultfCT2 = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components116static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection117static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation118119120class BackgroundSubtractorMOG2Impl CV_FINAL : public BackgroundSubtractorMOG2121{122public:123//! the default constructor124BackgroundSubtractorMOG2Impl()125{126frameSize = Size(0,0);127frameType = 0;128129nframes = 0;130history = defaultHistory2;131varThreshold = defaultVarThreshold2;132bShadowDetection = 1;133134nmixtures = defaultNMixtures2;135backgroundRatio = defaultBackgroundRatio2;136fVarInit = defaultVarInit2;137fVarMax = defaultVarMax2;138fVarMin = defaultVarMin2;139140varThresholdGen = defaultVarThresholdGen2;141fCT = defaultfCT2;142nShadowDetection = defaultnShadowDetection2;143fTau = defaultfTau;144#ifdef HAVE_OPENCL145opencl_ON = true;146#endif147}148//! the full constructor that takes the length of the history,149// the number of gaussian mixtures, the background ratio parameter and the noise strength150BackgroundSubtractorMOG2Impl(int _history, float _varThreshold, bool _bShadowDetection=true)151{152frameSize = Size(0,0);153frameType = 0;154155nframes = 0;156history = _history > 0 ? _history : defaultHistory2;157varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2;158bShadowDetection = _bShadowDetection;159160nmixtures = defaultNMixtures2;161backgroundRatio = defaultBackgroundRatio2;162fVarInit = defaultVarInit2;163fVarMax = defaultVarMax2;164fVarMin = defaultVarMin2;165166varThresholdGen = defaultVarThresholdGen2;167fCT = defaultfCT2;168nShadowDetection = defaultnShadowDetection2;169fTau = defaultfTau;170name_ = "BackgroundSubtractor.MOG2";171#ifdef HAVE_OPENCL172opencl_ON = true;173#endif174}175//! the destructor176~BackgroundSubtractorMOG2Impl() CV_OVERRIDE {}177//! the update operator178void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;179180//! computes a background image which are the mean of all background gaussians181virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;182183//! re-initiaization method184void initialize(Size _frameSize, int _frameType)185{186frameSize = _frameSize;187frameType = _frameType;188nframes = 0;189190int nchannels = CV_MAT_CN(frameType);191CV_Assert( nchannels <= CV_CN_MAX );192CV_Assert( nmixtures <= 255);193194#ifdef HAVE_OPENCL195if (ocl::isOpenCLActivated() && opencl_ON)196{197create_ocl_apply_kernel();198199bool isFloat = CV_MAKETYPE(CV_32F,nchannels) == frameType;200kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_mog2_oclsrc, format( "-D CN=%d -D FL=%d -D NMIXTURES=%d", nchannels, isFloat, nmixtures));201202if (kernel_apply.empty() || kernel_getBg.empty())203opencl_ON = false;204}205else opencl_ON = false;206207if (opencl_ON)208{209u_weight.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);210u_weight.setTo(Scalar::all(0));211212u_variance.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);213u_variance.setTo(Scalar::all(0));214215if (nchannels==3)216nchannels=4;217u_mean.create(frameSize.height * nmixtures, frameSize.width, CV_32FC(nchannels)); //4 channels218u_mean.setTo(Scalar::all(0));219220//make the array for keeping track of the used modes per pixel - all zeros at start221u_bgmodelUsedModes.create(frameSize, CV_8UC1);222u_bgmodelUsedModes.setTo(cv::Scalar::all(0));223}224else225#endif226{227// for each gaussian mixture of each pixel bg model we store ...228// the mixture weight (w),229// the mean (nchannels values) and230// the covariance231bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F );232//make the array for keeping track of the used modes per pixel - all zeros at start233bgmodelUsedModes.create(frameSize,CV_8U);234bgmodelUsedModes = Scalar::all(0);235}236}237238virtual int getHistory() const CV_OVERRIDE { return history; }239virtual void setHistory(int _nframes) CV_OVERRIDE { history = _nframes; }240241virtual int getNMixtures() const CV_OVERRIDE { return nmixtures; }242virtual void setNMixtures(int nmix) CV_OVERRIDE { nmixtures = nmix; }243244virtual double getBackgroundRatio() const CV_OVERRIDE { return backgroundRatio; }245virtual void setBackgroundRatio(double _backgroundRatio) CV_OVERRIDE { backgroundRatio = (float)_backgroundRatio; }246247virtual double getVarThreshold() const CV_OVERRIDE { return varThreshold; }248virtual void setVarThreshold(double _varThreshold) CV_OVERRIDE { varThreshold = _varThreshold; }249250virtual double getVarThresholdGen() const CV_OVERRIDE { return varThresholdGen; }251virtual void setVarThresholdGen(double _varThresholdGen) CV_OVERRIDE { varThresholdGen = (float)_varThresholdGen; }252253virtual double getVarInit() const CV_OVERRIDE { return fVarInit; }254virtual void setVarInit(double varInit) CV_OVERRIDE { fVarInit = (float)varInit; }255256virtual double getVarMin() const CV_OVERRIDE { return fVarMin; }257virtual void setVarMin(double varMin) CV_OVERRIDE { fVarMin = (float)varMin; }258259virtual double getVarMax() const CV_OVERRIDE { return fVarMax; }260virtual void setVarMax(double varMax) CV_OVERRIDE { fVarMax = (float)varMax; }261262virtual double getComplexityReductionThreshold() const CV_OVERRIDE { return fCT; }263virtual void setComplexityReductionThreshold(double ct) CV_OVERRIDE { fCT = (float)ct; }264265virtual bool getDetectShadows() const CV_OVERRIDE { return bShadowDetection; }266virtual void setDetectShadows(bool detectshadows) CV_OVERRIDE267{268if (bShadowDetection == detectshadows)269return;270bShadowDetection = detectshadows;271#ifdef HAVE_OPENCL272if (!kernel_apply.empty())273{274create_ocl_apply_kernel();275CV_Assert( !kernel_apply.empty() );276}277#endif278}279280virtual int getShadowValue() const CV_OVERRIDE { return nShadowDetection; }281virtual void setShadowValue(int value) CV_OVERRIDE { nShadowDetection = (uchar)value; }282283virtual double getShadowThreshold() const CV_OVERRIDE { return fTau; }284virtual void setShadowThreshold(double value) CV_OVERRIDE { fTau = (float)value; }285286virtual void write(FileStorage& fs) const CV_OVERRIDE287{288writeFormat(fs);289fs << "name" << name_290<< "history" << history291<< "nmixtures" << nmixtures292<< "backgroundRatio" << backgroundRatio293<< "varThreshold" << varThreshold294<< "varThresholdGen" << varThresholdGen295<< "varInit" << fVarInit296<< "varMin" << fVarMin297<< "varMax" << fVarMax298<< "complexityReductionThreshold" << fCT299<< "detectShadows" << (int)bShadowDetection300<< "shadowValue" << (int)nShadowDetection301<< "shadowThreshold" << fTau;302}303304virtual void read(const FileNode& fn) CV_OVERRIDE305{306CV_Assert( (String)fn["name"] == name_ );307history = (int)fn["history"];308nmixtures = (int)fn["nmixtures"];309backgroundRatio = (float)fn["backgroundRatio"];310varThreshold = (double)fn["varThreshold"];311varThresholdGen = (float)fn["varThresholdGen"];312fVarInit = (float)fn["varInit"];313fVarMin = (float)fn["varMin"];314fVarMax = (float)fn["varMax"];315fCT = (float)fn["complexityReductionThreshold"];316bShadowDetection = (int)fn["detectShadows"] != 0;317nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);318fTau = (float)fn["shadowThreshold"];319}320321protected:322Size frameSize;323int frameType;324Mat bgmodel;325Mat bgmodelUsedModes;//keep track of number of modes per pixel326327#ifdef HAVE_OPENCL328//for OCL329330mutable bool opencl_ON;331332UMat u_weight;333UMat u_variance;334UMat u_mean;335UMat u_bgmodelUsedModes;336337mutable ocl::Kernel kernel_apply;338mutable ocl::Kernel kernel_getBg;339#endif340341int nframes;342int history;343int nmixtures;344//! here it is the maximum allowed number of mixture components.345//! Actual number is determined dynamically per pixel346double varThreshold;347// threshold on the squared Mahalanobis distance to decide if it is well described348// by the background model or not. Related to Cthr from the paper.349// This does not influence the update of the background. A typical value could be 4 sigma350// and that is varThreshold=4*4=16; Corresponds to Tb in the paper.351352/////////////////////////353// less important parameters - things you might change but be careful354////////////////////////355float backgroundRatio;356// corresponds to fTB=1-cf from the paper357// TB - threshold when the component becomes significant enough to be included into358// the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.359// For alpha=0.001 it means that the mode should exist for approximately 105 frames before360// it is considered foreground361// float noiseSigma;362float varThresholdGen;363//correspondts to Tg - threshold on the squared Mahalan. dist. to decide364//when a sample is close to the existing components. If it is not close365//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.366//Smaller Tg leads to more generated components and higher Tg might make367//lead to small number of components but they can grow too large368float fVarInit;369float fVarMin;370float fVarMax;371//initial variance for the newly generated components.372//It will will influence the speed of adaptation. A good guess should be made.373//A simple way is to estimate the typical standard deviation from the images.374//I used here 10 as a reasonable value375// min and max can be used to further control the variance376float fCT;//CT - complexity reduction prior377//this is related to the number of samples needed to accept that a component378//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get379//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)380381//shadow detection parameters382bool bShadowDetection;//default 1 - do shadow detection383unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value384float fTau;385// Tau - shadow threshold. The shadow is detected if the pixel is darker386//version of the background. Tau is a threshold on how much darker the shadow can be.387//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow388//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.389390String name_;391392template <typename T, int CN>393void getBackgroundImage_intern(OutputArray backgroundImage) const;394395#ifdef HAVE_OPENCL396bool ocl_getBackgroundImage(OutputArray backgroundImage) const;397bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);398void create_ocl_apply_kernel();399#endif400};401402struct GaussBGStatModel2Params403{404//image info405int nWidth;406int nHeight;407int nND;//number of data dimensions (image channels)408409bool bPostFiltering;//default 1 - do postfiltering - will make shadow detection results also give value 255410double minArea; // for postfiltering411412bool bInit;//default 1, faster updates at start413414/////////////////////////415//very important parameters - things you will change416////////////////////////417float fAlphaT;418//alpha - speed of update - if the time interval you want to average over is T419//set alpha=1/T. It is also useful at start to make T slowly increase420//from 1 until the desired T421float fTb;422//Tb - threshold on the squared Mahalan. dist. to decide if it is well described423//by the background model or not. Related to Cthr from the paper.424//This does not influence the update of the background. A typical value could be 4 sigma425//and that is Tb=4*4=16;426427/////////////////////////428//less important parameters - things you might change but be careful429////////////////////////430float fTg;431//Tg - threshold on the squared Mahalan. dist. to decide432//when a sample is close to the existing components. If it is not close433//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.434//Smaller Tg leads to more generated components and higher Tg might make435//lead to small number of components but they can grow too large436float fTB;//1-cf from the paper437//TB - threshold when the component becomes significant enough to be included into438//the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.439//For alpha=0.001 it means that the mode should exist for approximately 105 frames before440//it is considered foreground441float fVarInit;442float fVarMax;443float fVarMin;444//initial standard deviation for the newly generated components.445//It will will influence the speed of adaptation. A good guess should be made.446//A simple way is to estimate the typical standard deviation from the images.447//I used here 10 as a reasonable value448float fCT;//CT - complexity reduction prior449//this is related to the number of samples needed to accept that a component450//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get451//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)452453//even less important parameters454int nM;//max number of modes - const - 4 is usually enough455456//shadow detection parameters457bool bShadowDetection;//default 1 - do shadow detection458unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result459float fTau;460// Tau - shadow threshold. The shadow is detected if the pixel is darker461//version of the background. Tau is a threshold on how much darker the shadow can be.462//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow463//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.464};465466struct GMM467{468float weight;469float variance;470};471472// shadow detection performed per pixel473// should work for rgb data, could be useful for gray scale and depth data as well474// See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.475CV_INLINE bool476detectShadowGMM(const float* data, int nchannels, int nmodes,477const GMM* gmm, const float* mean,478float Tb, float TB, float tau)479{480float tWeight = 0;481482// check all the components marked as background:483for( int mode = 0; mode < nmodes; mode++, mean += nchannels )484{485GMM g = gmm[mode];486487float numerator = 0.0f;488float denominator = 0.0f;489for( int c = 0; c < nchannels; c++ )490{491numerator += data[c] * mean[c];492denominator += mean[c] * mean[c];493}494495// no division by zero allowed496if( denominator == 0 )497return false;498499// if tau < a < 1 then also check the color distortion500if( numerator <= denominator && numerator >= tau*denominator )501{502float a = numerator / denominator;503float dist2a = 0.0f;504505for( int c = 0; c < nchannels; c++ )506{507float dD= a*mean[c] - data[c];508dist2a += dD*dD;509}510511if (dist2a < Tb*g.variance*a*a)512return true;513};514515tWeight += g.weight;516if( tWeight > TB )517return false;518};519return false;520}521522//update GMM - the base update function performed per pixel523//524//"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"525//Z.Zivkovic, F. van der Heijden526//Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.527//528//The algorithm similar to the standard Stauffer&Grimson algorithm with529//additional selection of the number of the Gaussian components based on:530//531//"Recursive unsupervised learning of finite mixture models "532//Z.Zivkovic, F.van der Heijden533//IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004534//http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf535536class MOG2Invoker : public ParallelLoopBody537{538public:539MOG2Invoker(const Mat& _src, Mat& _dst,540GMM* _gmm, float* _mean,541uchar* _modesUsed,542int _nmixtures, float _alphaT,543float _Tb, float _TB, float _Tg,544float _varInit, float _varMin, float _varMax,545float _prune, float _tau, bool _detectShadows,546uchar _shadowVal)547{548src = &_src;549dst = &_dst;550gmm0 = _gmm;551mean0 = _mean;552modesUsed0 = _modesUsed;553nmixtures = _nmixtures;554alphaT = _alphaT;555Tb = _Tb;556TB = _TB;557Tg = _Tg;558varInit = _varInit;559varMin = MIN(_varMin, _varMax);560varMax = MAX(_varMin, _varMax);561prune = _prune;562tau = _tau;563detectShadows = _detectShadows;564shadowVal = _shadowVal;565}566567void operator()(const Range& range) const CV_OVERRIDE568{569int y0 = range.start, y1 = range.end;570int ncols = src->cols, nchannels = src->channels();571AutoBuffer<float> buf(src->cols*nchannels);572float alpha1 = 1.f - alphaT;573float dData[CV_CN_MAX];574575for( int y = y0; y < y1; y++ )576{577const float* data = buf.data();578if( src->depth() != CV_32F )579src->row(y).convertTo(Mat(1, ncols, CV_32FC(nchannels), (void*)data), CV_32F);580else581data = src->ptr<float>(y);582583float* mean = mean0 + ncols*nmixtures*nchannels*y;584GMM* gmm = gmm0 + ncols*nmixtures*y;585uchar* modesUsed = modesUsed0 + ncols*y;586uchar* mask = dst->ptr(y);587588for( int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels )589{590//calculate distances to the modes (+ sort)591//here we need to go in descending order!!!592bool background = false;//return value -> true - the pixel classified as background593594//internal:595bool fitsPDF = false;//if it remains zero a new GMM mode will be added596int nmodes = modesUsed[x];//current number of modes in GMM597float totalWeight = 0.f;598599float* mean_m = mean;600601//////602//go through all modes603for( int mode = 0; mode < nmodes; mode++, mean_m += nchannels )604{605float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found606int swap_count = 0;607////608//fit not found yet609if( !fitsPDF )610{611//check if it belongs to some of the remaining modes612float var = gmm[mode].variance;613614//calculate difference and distance615float dist2;616617if( nchannels == 3 )618{619dData[0] = mean_m[0] - data[0];620dData[1] = mean_m[1] - data[1];621dData[2] = mean_m[2] - data[2];622dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];623}624else625{626dist2 = 0.f;627for( int c = 0; c < nchannels; c++ )628{629dData[c] = mean_m[c] - data[c];630dist2 += dData[c]*dData[c];631}632}633634//background? - Tb - usually larger than Tg635if( totalWeight < TB && dist2 < Tb*var )636background = true;637638//check fit639if( dist2 < Tg*var )640{641/////642//belongs to the mode643fitsPDF = true;644645//update distribution646647//update weight648weight += alphaT;649float k = alphaT/weight;650651//update mean652for( int c = 0; c < nchannels; c++ )653mean_m[c] -= k*dData[c];654655//update variance656float varnew = var + k*(dist2-var);657//limit the variance658varnew = MAX(varnew, varMin);659varnew = MIN(varnew, varMax);660gmm[mode].variance = varnew;661662//sort663//all other weights are at the same place and664//only the matched (iModes) is higher -> just find the new place for it665for( int i = mode; i > 0; i-- )666{667//check one up668if( weight < gmm[i-1].weight )669break;670671swap_count++;672//swap one up673std::swap(gmm[i], gmm[i-1]);674for( int c = 0; c < nchannels; c++ )675std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);676}677//belongs to the mode - bFitsPDF becomes 1678/////679}680}//!bFitsPDF)681682//check prune683if( weight < -prune )684{685weight = 0.0;686nmodes--;687}688689gmm[mode-swap_count].weight = weight;//update weight by the calculated value690totalWeight += weight;691}692//go through all modes693//////694695// Renormalize weights. In the special case that the pixel does696// not agree with any modes, set weights to zero (a new mode will be added below).697float invWeight = 0.f;698if (std::abs(totalWeight) > FLT_EPSILON) {699invWeight = 1.f/totalWeight;700}701702for( int mode = 0; mode < nmodes; mode++ )703{704gmm[mode].weight *= invWeight;705}706707//make new mode if needed and exit708if( !fitsPDF && alphaT > 0.f )709{710// replace the weakest or add a new one711int mode = nmodes == nmixtures ? nmixtures-1 : nmodes++;712713if (nmodes==1)714gmm[mode].weight = 1.f;715else716{717gmm[mode].weight = alphaT;718719// renormalize all other weights720for( int i = 0; i < nmodes-1; i++ )721gmm[i].weight *= alpha1;722}723724// init725for( int c = 0; c < nchannels; c++ )726mean[mode*nchannels + c] = data[c];727728gmm[mode].variance = varInit;729730//sort731//find the new place for it732for( int i = nmodes - 1; i > 0; i-- )733{734// check one up735if( alphaT < gmm[i-1].weight )736break;737738// swap one up739std::swap(gmm[i], gmm[i-1]);740for( int c = 0; c < nchannels; c++ )741std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);742}743}744745//set the number of modes746modesUsed[x] = uchar(nmodes);747mask[x] = background ? 0 :748detectShadows && detectShadowGMM(data, nchannels, nmodes, gmm, mean, Tb, TB, tau) ?749shadowVal : 255;750}751}752}753754const Mat* src;755Mat* dst;756GMM* gmm0;757float* mean0;758uchar* modesUsed0;759760int nmixtures;761float alphaT, Tb, TB, Tg;762float varInit, varMin, varMax, prune, tau;763764bool detectShadows;765uchar shadowVal;766};767768#ifdef HAVE_OPENCL769770bool BackgroundSubtractorMOG2Impl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate)771{772bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;773774if( needToInitialize )775initialize(_image.size(), _image.type());776777++nframes;778learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );779CV_Assert(learningRate >= 0);780781_fgmask.create(_image.size(), CV_8U);782UMat fgmask = _fgmask.getUMat();783784const double alpha1 = 1.0f - learningRate;785786UMat frame = _image.getUMat();787788float varMax = MAX(fVarMin, fVarMax);789float varMin = MIN(fVarMin, fVarMax);790791int idxArg = 0;792idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame));793idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_bgmodelUsedModes));794idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_weight));795idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_mean));796idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_variance));797idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask));798799idxArg = kernel_apply.set(idxArg, (float)learningRate); //alphaT800idxArg = kernel_apply.set(idxArg, (float)alpha1);801idxArg = kernel_apply.set(idxArg, (float)(-learningRate*fCT)); //prune802803idxArg = kernel_apply.set(idxArg, (float)varThreshold); //c_Tb804idxArg = kernel_apply.set(idxArg, backgroundRatio); //c_TB805idxArg = kernel_apply.set(idxArg, varThresholdGen); //c_Tg806idxArg = kernel_apply.set(idxArg, varMin);807idxArg = kernel_apply.set(idxArg, varMax);808idxArg = kernel_apply.set(idxArg, fVarInit);809idxArg = kernel_apply.set(idxArg, fTau);810if (bShadowDetection)811kernel_apply.set(idxArg, nShadowDetection);812813size_t globalsize[] = {(size_t)frame.cols, (size_t)frame.rows, 1};814return kernel_apply.run(2, globalsize, NULL, true);815}816817bool BackgroundSubtractorMOG2Impl::ocl_getBackgroundImage(OutputArray _backgroundImage) const818{819_backgroundImage.create(frameSize, frameType);820UMat dst = _backgroundImage.getUMat();821822int idxArg = 0;823idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_bgmodelUsedModes));824idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_weight));825idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_mean));826idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst));827kernel_getBg.set(idxArg, backgroundRatio);828829size_t globalsize[2] = {(size_t)u_bgmodelUsedModes.cols, (size_t)u_bgmodelUsedModes.rows};830831return kernel_getBg.run(2, globalsize, NULL, false);832}833834void BackgroundSubtractorMOG2Impl::create_ocl_apply_kernel()835{836int nchannels = CV_MAT_CN(frameType);837bool isFloat = CV_MAKETYPE(CV_32F,nchannels) == frameType;838String opts = format("-D CN=%d -D FL=%d -D NMIXTURES=%d%s", nchannels, isFloat, nmixtures, bShadowDetection ? " -D SHADOW_DETECT" : "");839kernel_apply.create("mog2_kernel", ocl::video::bgfg_mog2_oclsrc, opts);840}841842#endif843844void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate)845{846CV_INSTRUMENT_REGION();847848#ifdef HAVE_OPENCL849if (opencl_ON)850{851CV_OCL_RUN(_fgmask.isUMat(), ocl_apply(_image, _fgmask, learningRate))852853opencl_ON = false;854nframes = 0;855}856#endif857858bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;859860if( needToInitialize )861initialize(_image.size(), _image.type());862863Mat image = _image.getMat();864_fgmask.create( image.size(), CV_8U );865Mat fgmask = _fgmask.getMat();866867++nframes;868learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );869CV_Assert(learningRate >= 0);870871parallel_for_(Range(0, image.rows),872MOG2Invoker(image, fgmask,873bgmodel.ptr<GMM>(),874(float*)(bgmodel.ptr() + sizeof(GMM)*nmixtures*image.rows*image.cols),875bgmodelUsedModes.ptr(), nmixtures, (float)learningRate,876(float)varThreshold,877backgroundRatio, varThresholdGen,878fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau,879bShadowDetection, nShadowDetection),880image.total()/(double)(1 << 16));881}882883template <typename T, int CN>884void BackgroundSubtractorMOG2Impl::getBackgroundImage_intern(OutputArray backgroundImage) const885{886CV_INSTRUMENT_REGION();887888Mat meanBackground(frameSize, frameType, Scalar::all(0));889int firstGaussianIdx = 0;890const GMM* gmm = bgmodel.ptr<GMM>();891const float* mean = reinterpret_cast<const float*>(gmm + frameSize.width*frameSize.height*nmixtures);892Vec<float,CN> meanVal(0.f);893for(int row=0; row<meanBackground.rows; row++)894{895for(int col=0; col<meanBackground.cols; col++)896{897int nmodes = bgmodelUsedModes.at<uchar>(row, col);898float totalWeight = 0.f;899for(int gaussianIdx = firstGaussianIdx; gaussianIdx < firstGaussianIdx + nmodes; gaussianIdx++)900{901GMM gaussian = gmm[gaussianIdx];902size_t meanPosition = gaussianIdx*CN;903for(int chn = 0; chn < CN; chn++)904{905meanVal(chn) += gaussian.weight * mean[meanPosition + chn];906}907totalWeight += gaussian.weight;908909if(totalWeight > backgroundRatio)910break;911}912float invWeight = 0.f;913if (std::abs(totalWeight) > FLT_EPSILON) {914invWeight = 1.f/totalWeight;915}916917meanBackground.at<Vec<T,CN> >(row, col) = Vec<T,CN>(meanVal * invWeight);918meanVal = 0.f;919920firstGaussianIdx += nmixtures;921}922}923meanBackground.copyTo(backgroundImage);924}925926void BackgroundSubtractorMOG2Impl::getBackgroundImage(OutputArray backgroundImage) const927{928CV_Assert(frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_32FC1 || frameType == CV_32FC3);929930#ifdef HAVE_OPENCL931if (opencl_ON)932{933CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage))934935opencl_ON = false;936}937#endif938939switch(frameType)940{941case CV_8UC1:942getBackgroundImage_intern<uchar,1>(backgroundImage);943break;944case CV_8UC3:945getBackgroundImage_intern<uchar,3>(backgroundImage);946break;947case CV_32FC1:948getBackgroundImage_intern<float,1>(backgroundImage);949break;950case CV_32FC3:951getBackgroundImage_intern<float,3>(backgroundImage);952break;953}954}955956Ptr<BackgroundSubtractorMOG2> createBackgroundSubtractorMOG2(int _history, double _varThreshold,957bool _bShadowDetection)958{959return makePtr<BackgroundSubtractorMOG2Impl>(_history, (float)_varThreshold, _bShadowDetection);960}961962}963964/* End of file. */965966967