Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                          License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
     14 // Copyright (C) 2013, OpenCV Foundation, all rights reserved.
     15 // Third party copyrights are property of their respective owners.
     16 //
     17 // Redistribution and use in source and binary forms, with or without modification,
     18 // are permitted provided that the following conditions are met:
     19 //
     20 //   * Redistribution's of source code must retain the above copyright notice,
     21 //     this list of conditions and the following disclaimer.
     22 //
     23 //   * Redistribution's in binary form must reproduce the above copyright notice,
     24 //     this list of conditions and the following disclaimer in the documentation
     25 //     and/or other materials provided with the distribution.
     26 //
     27 //   * The name of the copyright holders may not be used to endorse or promote products
     28 //     derived from this software without specific prior written permission.
     29 //
     30 // This software is provided by the copyright holders and contributors "as is" and
     31 // any express or implied warranties, including, but not limited to, the implied
     32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     33 // In no event shall the Intel Corporation or contributors be liable for any direct,
     34 // indirect, incidental, special, exemplary, or consequential damages
     35 // (including, but not limited to, procurement of substitute goods or services;
     36 // loss of use, data, or profits; or business interruption) however caused
     37 // and on any theory of liability, whether in contract, strict liability,
     38 // or tort (including negligence or otherwise) arising in any way out of
     39 // the use of this software, even if advised of the possibility of such damage.
     40 //
     41 //M*/
     42 
     43 /*//Implementation of the Gaussian mixture model background subtraction from:
     44 //
     45 //"Improved adaptive Gausian mixture model for background subtraction"
     46 //Z.Zivkovic
     47 //International Conference Pattern Recognition, UK, August, 2004
     48 //http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
     49 //The code is very fast and performs also shadow detection.
     50 //Number of Gausssian components is adapted per pixel.
     51 //
     52 // and
     53 //
     54 //"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"
     55 //Z.Zivkovic, F. van der Heijden
     56 //Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
     57 //
     58 //The algorithm similar to the standard Stauffer&Grimson algorithm with
     59 //additional selection of the number of the Gaussian components based on:
     60 //
     61 //"Recursive unsupervised learning of finite mixture models "
     62 //Z.Zivkovic, F.van der Heijden
     63 //IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004
     64 //http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf
     65 //
     66 //
     67 //Example usage with as cpp class
     68 // BackgroundSubtractorMOG2 bg_model;
     69 //For each new image the model is updates using:
     70 // bg_model(img, fgmask);
     71 //
     72 //Example usage as part of the CvBGStatModel:
     73 // CvBGStatModel* bg_model = cvCreateGaussianBGModel2( first_frame );
     74 //
     75 // //update for each frame
     76 // cvUpdateBGStatModel( tmp_frame, bg_model );//segmentation result is in bg_model->foreground
     77 //
     78 // //release at the program termination
     79 // cvReleaseBGStatModel( &bg_model );
     80 //
     81 //Author: Z.Zivkovic, www.zoranz.net
     82 //Date: 7-April-2011, Version:1.0
     83 ///////////*/
     84 
     85 #include "precomp.hpp"
     86 #include "opencl_kernels_video.hpp"
     87 
     88 namespace cv
     89 {
     90 
     91 /*
     92  Interface of Gaussian mixture algorithm from:
     93 
     94  "Improved adaptive Gausian mixture model for background subtraction"
     95  Z.Zivkovic
     96  International Conference Pattern Recognition, UK, August, 2004
     97  http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
     98 
     99  Advantages:
    100  -fast - number of Gausssian components is constantly adapted per pixel.
    101  -performs also shadow detection (see bgfg_segm_test.cpp example)
    102 
    103 */
    104 
    105 // default parameters of gaussian background detection algorithm
    106 static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2
    107 static const float defaultVarThreshold2 = 4.0f*4.0f;
    108 static const int defaultNMixtures2 = 5; // maximal number of Gaussians in mixture
    109 static const float defaultBackgroundRatio2 = 0.9f; // threshold sum of weights for background test
    110 static const float defaultVarThresholdGen2 = 3.0f*3.0f;
    111 static const float defaultVarInit2 = 15.0f; // initial variance for new components
    112 static const float defaultVarMax2 = 5*defaultVarInit2;
    113 static const float defaultVarMin2 = 4.0f;
    114 
    115 // additional parameters
    116 static const float defaultfCT2 = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components
    117 static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
    118 static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation
    119 
    120 
    121 class BackgroundSubtractorMOG2Impl : public BackgroundSubtractorMOG2
    122 {
    123 public:
    124     //! the default constructor
    125     BackgroundSubtractorMOG2Impl()
    126     {
    127         frameSize = Size(0,0);
    128         frameType = 0;
    129 
    130         nframes = 0;
    131         history = defaultHistory2;
    132         varThreshold = defaultVarThreshold2;
    133         bShadowDetection = 1;
    134 
    135         nmixtures = defaultNMixtures2;
    136         backgroundRatio = defaultBackgroundRatio2;
    137         fVarInit = defaultVarInit2;
    138         fVarMax  = defaultVarMax2;
    139         fVarMin = defaultVarMin2;
    140 
    141         varThresholdGen = defaultVarThresholdGen2;
    142         fCT = defaultfCT2;
    143         nShadowDetection =  defaultnShadowDetection2;
    144         fTau = defaultfTau;
    145 
    146         opencl_ON = true;
    147     }
    148     //! the full constructor that takes the length of the history,
    149     // the number of gaussian mixtures, the background ratio parameter and the noise strength
    150     BackgroundSubtractorMOG2Impl(int _history,  float _varThreshold, bool _bShadowDetection=true)
    151     {
    152         frameSize = Size(0,0);
    153         frameType = 0;
    154 
    155         nframes = 0;
    156         history = _history > 0 ? _history : defaultHistory2;
    157         varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2;
    158         bShadowDetection = _bShadowDetection;
    159 
    160         nmixtures = defaultNMixtures2;
    161         backgroundRatio = defaultBackgroundRatio2;
    162         fVarInit = defaultVarInit2;
    163         fVarMax  = defaultVarMax2;
    164         fVarMin = defaultVarMin2;
    165 
    166         varThresholdGen = defaultVarThresholdGen2;
    167         fCT = defaultfCT2;
    168         nShadowDetection =  defaultnShadowDetection2;
    169         fTau = defaultfTau;
    170         name_ = "BackgroundSubtractor.MOG2";
    171 
    172         opencl_ON = true;
    173     }
    174     //! the destructor
    175     ~BackgroundSubtractorMOG2Impl() {}
    176     //! the update operator
    177     void apply(InputArray image, OutputArray fgmask, double learningRate=-1);
    178 
    179     //! computes a background image which are the mean of all background gaussians
    180     virtual void getBackgroundImage(OutputArray backgroundImage) const;
    181 
    182     //! re-initiaization method
    183     void initialize(Size _frameSize, int _frameType)
    184     {
    185         frameSize = _frameSize;
    186         frameType = _frameType;
    187         nframes = 0;
    188 
    189         int nchannels = CV_MAT_CN(frameType);
    190         CV_Assert( nchannels <= CV_CN_MAX );
    191         CV_Assert( nmixtures <= 255);
    192 
    193         if (ocl::useOpenCL() && opencl_ON)
    194         {
    195             create_ocl_apply_kernel();
    196             kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_mog2_oclsrc, format( "-D CN=%d -D NMIXTURES=%d", nchannels, nmixtures));
    197 
    198             if (kernel_apply.empty() || kernel_getBg.empty())
    199                 opencl_ON = false;
    200         }
    201         else opencl_ON = false;
    202 
    203         if (opencl_ON)
    204         {
    205             u_weight.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);
    206             u_weight.setTo(Scalar::all(0));
    207 
    208             u_variance.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);
    209             u_variance.setTo(Scalar::all(0));
    210 
    211             if (nchannels==3)
    212                 nchannels=4;
    213             u_mean.create(frameSize.height * nmixtures, frameSize.width, CV_32FC(nchannels)); //4 channels
    214             u_mean.setTo(Scalar::all(0));
    215 
    216             //make the array for keeping track of the used modes per pixel - all zeros at start
    217             u_bgmodelUsedModes.create(frameSize, CV_8UC1);
    218             u_bgmodelUsedModes.setTo(cv::Scalar::all(0));
    219         }
    220         else
    221         {
    222             // for each gaussian mixture of each pixel bg model we store ...
    223             // the mixture weight (w),
    224             // the mean (nchannels values) and
    225             // the covariance
    226             bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F );
    227             //make the array for keeping track of the used modes per pixel - all zeros at start
    228             bgmodelUsedModes.create(frameSize,CV_8U);
    229             bgmodelUsedModes = Scalar::all(0);
    230         }
    231     }
    232 
    233     virtual int getHistory() const { return history; }
    234     virtual void setHistory(int _nframes) { history = _nframes; }
    235 
    236     virtual int getNMixtures() const { return nmixtures; }
    237     virtual void setNMixtures(int nmix) { nmixtures = nmix; }
    238 
    239     virtual double getBackgroundRatio() const { return backgroundRatio; }
    240     virtual void setBackgroundRatio(double _backgroundRatio) { backgroundRatio = (float)_backgroundRatio; }
    241 
    242     virtual double getVarThreshold() const { return varThreshold; }
    243     virtual void setVarThreshold(double _varThreshold) { varThreshold = _varThreshold; }
    244 
    245     virtual double getVarThresholdGen() const { return varThresholdGen; }
    246     virtual void setVarThresholdGen(double _varThresholdGen) { varThresholdGen = (float)_varThresholdGen; }
    247 
    248     virtual double getVarInit() const { return fVarInit; }
    249     virtual void setVarInit(double varInit) { fVarInit = (float)varInit; }
    250 
    251     virtual double getVarMin() const { return fVarMin; }
    252     virtual void setVarMin(double varMin) { fVarMin = (float)varMin; }
    253 
    254     virtual double getVarMax() const { return fVarMax; }
    255     virtual void setVarMax(double varMax) { fVarMax = (float)varMax; }
    256 
    257     virtual double getComplexityReductionThreshold() const { return fCT; }
    258     virtual void setComplexityReductionThreshold(double ct) { fCT = (float)ct; }
    259 
    260     virtual bool getDetectShadows() const { return bShadowDetection; }
    261     virtual void setDetectShadows(bool detectshadows)
    262     {
    263         if ((bShadowDetection && detectshadows) || (!bShadowDetection && !detectshadows))
    264             return;
    265         bShadowDetection = detectshadows;
    266         if (!kernel_apply.empty())
    267         {
    268             create_ocl_apply_kernel();
    269             CV_Assert( !kernel_apply.empty() );
    270         }
    271     }
    272 
    273     virtual int getShadowValue() const { return nShadowDetection; }
    274     virtual void setShadowValue(int value) { nShadowDetection = (uchar)value; }
    275 
    276     virtual double getShadowThreshold() const { return fTau; }
    277     virtual void setShadowThreshold(double value) { fTau = (float)value; }
    278 
    279     virtual void write(FileStorage& fs) const
    280     {
    281         fs << "name" << name_
    282         << "history" << history
    283         << "nmixtures" << nmixtures
    284         << "backgroundRatio" << backgroundRatio
    285         << "varThreshold" << varThreshold
    286         << "varThresholdGen" << varThresholdGen
    287         << "varInit" << fVarInit
    288         << "varMin" << fVarMin
    289         << "varMax" << fVarMax
    290         << "complexityReductionThreshold" << fCT
    291         << "detectShadows" << (int)bShadowDetection
    292         << "shadowValue" << (int)nShadowDetection
    293         << "shadowThreshold" << fTau;
    294     }
    295 
    296     virtual void read(const FileNode& fn)
    297     {
    298         CV_Assert( (String)fn["name"] == name_ );
    299         history = (int)fn["history"];
    300         nmixtures = (int)fn["nmixtures"];
    301         backgroundRatio = (float)fn["backgroundRatio"];
    302         varThreshold = (double)fn["varThreshold"];
    303         varThresholdGen = (float)fn["varThresholdGen"];
    304         fVarInit = (float)fn["varInit"];
    305         fVarMin = (float)fn["varMin"];
    306         fVarMax = (float)fn["varMax"];
    307         fCT = (float)fn["complexityReductionThreshold"];
    308         bShadowDetection = (int)fn["detectShadows"] != 0;
    309         nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
    310         fTau = (float)fn["shadowThreshold"];
    311     }
    312 
    313 protected:
    314     Size frameSize;
    315     int frameType;
    316     Mat bgmodel;
    317     Mat bgmodelUsedModes;//keep track of number of modes per pixel
    318 
    319     //for OCL
    320 
    321     mutable bool opencl_ON;
    322 
    323     UMat u_weight;
    324     UMat u_variance;
    325     UMat u_mean;
    326     UMat u_bgmodelUsedModes;
    327 
    328     mutable ocl::Kernel kernel_apply;
    329     mutable ocl::Kernel kernel_getBg;
    330 
    331     int nframes;
    332     int history;
    333     int nmixtures;
    334     //! here it is the maximum allowed number of mixture components.
    335     //! Actual number is determined dynamically per pixel
    336     double varThreshold;
    337     // threshold on the squared Mahalanobis distance to decide if it is well described
    338     // by the background model or not. Related to Cthr from the paper.
    339     // This does not influence the update of the background. A typical value could be 4 sigma
    340     // and that is varThreshold=4*4=16; Corresponds to Tb in the paper.
    341 
    342     /////////////////////////
    343     // less important parameters - things you might change but be carefull
    344     ////////////////////////
    345     float backgroundRatio;
    346     // corresponds to fTB=1-cf from the paper
    347     // TB - threshold when the component becomes significant enough to be included into
    348     // the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
    349     // For alpha=0.001 it means that the mode should exist for approximately 105 frames before
    350     // it is considered foreground
    351     // float noiseSigma;
    352     float varThresholdGen;
    353     //correspondts to Tg - threshold on the squared Mahalan. dist. to decide
    354     //when a sample is close to the existing components. If it is not close
    355     //to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
    356     //Smaller Tg leads to more generated components and higher Tg might make
    357     //lead to small number of components but they can grow too large
    358     float fVarInit;
    359     float fVarMin;
    360     float fVarMax;
    361     //initial variance  for the newly generated components.
    362     //It will will influence the speed of adaptation. A good guess should be made.
    363     //A simple way is to estimate the typical standard deviation from the images.
    364     //I used here 10 as a reasonable value
    365     // min and max can be used to further control the variance
    366     float fCT;//CT - complexity reduction prior
    367     //this is related to the number of samples needed to accept that a component
    368     //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
    369     //the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
    370 
    371     //shadow detection parameters
    372     bool bShadowDetection;//default 1 - do shadow detection
    373     unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
    374     float fTau;
    375     // Tau - shadow threshold. The shadow is detected if the pixel is darker
    376     //version of the background. Tau is a threshold on how much darker the shadow can be.
    377     //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
    378     //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
    379 
    380     String name_;
    381 
    382     bool ocl_getBackgroundImage(OutputArray backgroundImage) const;
    383     bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);
    384     void create_ocl_apply_kernel();
    385 };
    386 
    387 struct GaussBGStatModel2Params
    388 {
    389     //image info
    390     int nWidth;
    391     int nHeight;
    392     int nND;//number of data dimensions (image channels)
    393 
    394     bool bPostFiltering;//defult 1 - do postfiltering - will make shadow detection results also give value 255
    395     double  minArea; // for postfiltering
    396 
    397     bool bInit;//default 1, faster updates at start
    398 
    399     /////////////////////////
    400     //very important parameters - things you will change
    401     ////////////////////////
    402     float fAlphaT;
    403     //alpha - speed of update - if the time interval you want to average over is T
    404     //set alpha=1/T. It is also usefull at start to make T slowly increase
    405     //from 1 until the desired T
    406     float fTb;
    407     //Tb - threshold on the squared Mahalan. dist. to decide if it is well described
    408     //by the background model or not. Related to Cthr from the paper.
    409     //This does not influence the update of the background. A typical value could be 4 sigma
    410     //and that is Tb=4*4=16;
    411 
    412     /////////////////////////
    413     //less important parameters - things you might change but be carefull
    414     ////////////////////////
    415     float fTg;
    416     //Tg - threshold on the squared Mahalan. dist. to decide
    417     //when a sample is close to the existing components. If it is not close
    418     //to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
    419     //Smaller Tg leads to more generated components and higher Tg might make
    420     //lead to small number of components but they can grow too large
    421     float fTB;//1-cf from the paper
    422     //TB - threshold when the component becomes significant enough to be included into
    423     //the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
    424     //For alpha=0.001 it means that the mode should exist for approximately 105 frames before
    425     //it is considered foreground
    426     float fVarInit;
    427     float fVarMax;
    428     float fVarMin;
    429     //initial standard deviation  for the newly generated components.
    430     //It will will influence the speed of adaptation. A good guess should be made.
    431     //A simple way is to estimate the typical standard deviation from the images.
    432     //I used here 10 as a reasonable value
    433     float fCT;//CT - complexity reduction prior
    434     //this is related to the number of samples needed to accept that a component
    435     //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
    436     //the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
    437 
    438     //even less important parameters
    439     int nM;//max number of modes - const - 4 is usually enough
    440 
    441     //shadow detection parameters
    442     bool bShadowDetection;//default 1 - do shadow detection
    443     unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result
    444     float fTau;
    445     // Tau - shadow threshold. The shadow is detected if the pixel is darker
    446     //version of the background. Tau is a threshold on how much darker the shadow can be.
    447     //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
    448     //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
    449 };
    450 
    451 struct GMM
    452 {
    453     float weight;
    454     float variance;
    455 };
    456 
    457 // shadow detection performed per pixel
    458 // should work for rgb data, could be usefull for gray scale and depth data as well
    459 // See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
    460 CV_INLINE bool
    461 detectShadowGMM(const float* data, int nchannels, int nmodes,
    462                 const GMM* gmm, const float* mean,
    463                 float Tb, float TB, float tau)
    464 {
    465     float tWeight = 0;
    466 
    467     // check all the components  marked as background:
    468     for( int mode = 0; mode < nmodes; mode++, mean += nchannels )
    469     {
    470         GMM g = gmm[mode];
    471 
    472         float numerator = 0.0f;
    473         float denominator = 0.0f;
    474         for( int c = 0; c < nchannels; c++ )
    475         {
    476             numerator   += data[c] * mean[c];
    477             denominator += mean[c] * mean[c];
    478         }
    479 
    480         // no division by zero allowed
    481         if( denominator == 0 )
    482             return false;
    483 
    484         // if tau < a < 1 then also check the color distortion
    485         if( numerator <= denominator && numerator >= tau*denominator )
    486         {
    487             float a = numerator / denominator;
    488             float dist2a = 0.0f;
    489 
    490             for( int c = 0; c < nchannels; c++ )
    491             {
    492                 float dD= a*mean[c] - data[c];
    493                 dist2a += dD*dD;
    494             }
    495 
    496             if (dist2a < Tb*g.variance*a*a)
    497                 return true;
    498         };
    499 
    500         tWeight += g.weight;
    501         if( tWeight > TB )
    502             return false;
    503     };
    504     return false;
    505 }
    506 
    507 //update GMM - the base update function performed per pixel
    508 //
    509 //"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"
    510 //Z.Zivkovic, F. van der Heijden
    511 //Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
    512 //
    513 //The algorithm similar to the standard Stauffer&Grimson algorithm with
    514 //additional selection of the number of the Gaussian components based on:
    515 //
    516 //"Recursive unsupervised learning of finite mixture models "
    517 //Z.Zivkovic, F.van der Heijden
    518 //IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004
    519 //http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf
    520 
    521 class MOG2Invoker : public ParallelLoopBody
    522 {
    523 public:
    524     MOG2Invoker(const Mat& _src, Mat& _dst,
    525                 GMM* _gmm, float* _mean,
    526                 uchar* _modesUsed,
    527                 int _nmixtures, float _alphaT,
    528                 float _Tb, float _TB, float _Tg,
    529                 float _varInit, float _varMin, float _varMax,
    530                 float _prune, float _tau, bool _detectShadows,
    531                 uchar _shadowVal)
    532     {
    533         src = &_src;
    534         dst = &_dst;
    535         gmm0 = _gmm;
    536         mean0 = _mean;
    537         modesUsed0 = _modesUsed;
    538         nmixtures = _nmixtures;
    539         alphaT = _alphaT;
    540         Tb = _Tb;
    541         TB = _TB;
    542         Tg = _Tg;
    543         varInit = _varInit;
    544         varMin = MIN(_varMin, _varMax);
    545         varMax = MAX(_varMin, _varMax);
    546         prune = _prune;
    547         tau = _tau;
    548         detectShadows = _detectShadows;
    549         shadowVal = _shadowVal;
    550     }
    551 
    552     void operator()(const Range& range) const
    553     {
    554         int y0 = range.start, y1 = range.end;
    555         int ncols = src->cols, nchannels = src->channels();
    556         AutoBuffer<float> buf(src->cols*nchannels);
    557         float alpha1 = 1.f - alphaT;
    558         float dData[CV_CN_MAX];
    559 
    560         for( int y = y0; y < y1; y++ )
    561         {
    562             const float* data = buf;
    563             if( src->depth() != CV_32F )
    564                 src->row(y).convertTo(Mat(1, ncols, CV_32FC(nchannels), (void*)data), CV_32F);
    565             else
    566                 data = src->ptr<float>(y);
    567 
    568             float* mean = mean0 + ncols*nmixtures*nchannels*y;
    569             GMM* gmm = gmm0 + ncols*nmixtures*y;
    570             uchar* modesUsed = modesUsed0 + ncols*y;
    571             uchar* mask = dst->ptr(y);
    572 
    573             for( int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels )
    574             {
    575                 //calculate distances to the modes (+ sort)
    576                 //here we need to go in descending order!!!
    577                 bool background = false;//return value -> true - the pixel classified as background
    578 
    579                 //internal:
    580                 bool fitsPDF = false;//if it remains zero a new GMM mode will be added
    581                 int nmodes = modesUsed[x], nNewModes = nmodes;//current number of modes in GMM
    582                 float totalWeight = 0.f;
    583 
    584                 float* mean_m = mean;
    585 
    586                 //////
    587                 //go through all modes
    588                 for( int mode = 0; mode < nmodes; mode++, mean_m += nchannels )
    589                 {
    590                     float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found
    591                     int swap_count = 0;
    592                     ////
    593                     //fit not found yet
    594                     if( !fitsPDF )
    595                     {
    596                         //check if it belongs to some of the remaining modes
    597                         float var = gmm[mode].variance;
    598 
    599                         //calculate difference and distance
    600                         float dist2;
    601 
    602                         if( nchannels == 3 )
    603                         {
    604                             dData[0] = mean_m[0] - data[0];
    605                             dData[1] = mean_m[1] - data[1];
    606                             dData[2] = mean_m[2] - data[2];
    607                             dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
    608                         }
    609                         else
    610                         {
    611                             dist2 = 0.f;
    612                             for( int c = 0; c < nchannels; c++ )
    613                             {
    614                                 dData[c] = mean_m[c] - data[c];
    615                                 dist2 += dData[c]*dData[c];
    616                             }
    617                         }
    618 
    619                         //background? - Tb - usually larger than Tg
    620                         if( totalWeight < TB && dist2 < Tb*var )
    621                             background = true;
    622 
    623                         //check fit
    624                         if( dist2 < Tg*var )
    625                         {
    626                             /////
    627                             //belongs to the mode
    628                             fitsPDF = true;
    629 
    630                             //update distribution
    631 
    632                             //update weight
    633                             weight += alphaT;
    634                             float k = alphaT/weight;
    635 
    636                             //update mean
    637                             for( int c = 0; c < nchannels; c++ )
    638                                 mean_m[c] -= k*dData[c];
    639 
    640                             //update variance
    641                             float varnew = var + k*(dist2-var);
    642                             //limit the variance
    643                             varnew = MAX(varnew, varMin);
    644                             varnew = MIN(varnew, varMax);
    645                             gmm[mode].variance = varnew;
    646 
    647                             //sort
    648                             //all other weights are at the same place and
    649                             //only the matched (iModes) is higher -> just find the new place for it
    650                             for( int i = mode; i > 0; i-- )
    651                             {
    652                                 //check one up
    653                                 if( weight < gmm[i-1].weight )
    654                                     break;
    655 
    656                                 swap_count++;
    657                                 //swap one up
    658                                 std::swap(gmm[i], gmm[i-1]);
    659                                 for( int c = 0; c < nchannels; c++ )
    660                                     std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
    661                             }
    662                             //belongs to the mode - bFitsPDF becomes 1
    663                             /////
    664                         }
    665                     }//!bFitsPDF)
    666 
    667                     //check prune
    668                     if( weight < -prune )
    669                     {
    670                         weight = 0.0;
    671                         nmodes--;
    672                     }
    673 
    674                     gmm[mode-swap_count].weight = weight;//update weight by the calculated value
    675                     totalWeight += weight;
    676                 }
    677                 //go through all modes
    678                 //////
    679 
    680                 //renormalize weights
    681                 totalWeight = 1.f/totalWeight;
    682                 for( int mode = 0; mode < nmodes; mode++ )
    683                 {
    684                     gmm[mode].weight *= totalWeight;
    685                 }
    686 
    687                 nmodes = nNewModes;
    688 
    689                 //make new mode if needed and exit
    690                 if( !fitsPDF && alphaT > 0.f )
    691                 {
    692                     // replace the weakest or add a new one
    693                     int mode = nmodes == nmixtures ? nmixtures-1 : nmodes++;
    694 
    695                     if (nmodes==1)
    696                         gmm[mode].weight = 1.f;
    697                     else
    698                     {
    699                         gmm[mode].weight = alphaT;
    700 
    701                         // renormalize all other weights
    702                         for( int i = 0; i < nmodes-1; i++ )
    703                             gmm[i].weight *= alpha1;
    704                     }
    705 
    706                     // init
    707                     for( int c = 0; c < nchannels; c++ )
    708                         mean[mode*nchannels + c] = data[c];
    709 
    710                     gmm[mode].variance = varInit;
    711 
    712                     //sort
    713                     //find the new place for it
    714                     for( int i = nmodes - 1; i > 0; i-- )
    715                     {
    716                         // check one up
    717                         if( alphaT < gmm[i-1].weight )
    718                             break;
    719 
    720                         // swap one up
    721                         std::swap(gmm[i], gmm[i-1]);
    722                         for( int c = 0; c < nchannels; c++ )
    723                             std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
    724                     }
    725                 }
    726 
    727                 //set the number of modes
    728                 modesUsed[x] = uchar(nmodes);
    729                 mask[x] = background ? 0 :
    730                     detectShadows && detectShadowGMM(data, nchannels, nmodes, gmm, mean, Tb, TB, tau) ?
    731                     shadowVal : 255;
    732             }
    733         }
    734     }
    735 
    736     const Mat* src;
    737     Mat* dst;
    738     GMM* gmm0;
    739     float* mean0;
    740     uchar* modesUsed0;
    741 
    742     int nmixtures;
    743     float alphaT, Tb, TB, Tg;
    744     float varInit, varMin, varMax, prune, tau;
    745 
    746     bool detectShadows;
    747     uchar shadowVal;
    748 };
    749 
    750 #ifdef HAVE_OPENCL
    751 
    752 bool BackgroundSubtractorMOG2Impl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate)
    753 {
    754     ++nframes;
    755     learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
    756     CV_Assert(learningRate >= 0);
    757 
    758     _fgmask.create(_image.size(), CV_8U);
    759     UMat fgmask = _fgmask.getUMat();
    760 
    761     const double alpha1 = 1.0f - learningRate;
    762 
    763     UMat frame = _image.getUMat();
    764 
    765     float varMax = MAX(fVarMin, fVarMax);
    766     float varMin = MIN(fVarMin, fVarMax);
    767 
    768     int idxArg = 0;
    769     idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame));
    770     idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_bgmodelUsedModes));
    771     idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_weight));
    772     idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_mean));
    773     idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_variance));
    774     idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask));
    775 
    776     idxArg = kernel_apply.set(idxArg, (float)learningRate);        //alphaT
    777     idxArg = kernel_apply.set(idxArg, (float)alpha1);
    778     idxArg = kernel_apply.set(idxArg, (float)(-learningRate*fCT));   //prune
    779 
    780     idxArg = kernel_apply.set(idxArg, (float)varThreshold); //c_Tb
    781     idxArg = kernel_apply.set(idxArg, backgroundRatio);     //c_TB
    782     idxArg = kernel_apply.set(idxArg, varThresholdGen);     //c_Tg
    783     idxArg = kernel_apply.set(idxArg, varMin);
    784     idxArg = kernel_apply.set(idxArg, varMax);
    785     idxArg = kernel_apply.set(idxArg, fVarInit);
    786     idxArg = kernel_apply.set(idxArg, fTau);
    787     if (bShadowDetection)
    788         kernel_apply.set(idxArg, nShadowDetection);
    789 
    790     size_t globalsize[] = {frame.cols, frame.rows, 1};
    791     return kernel_apply.run(2, globalsize, NULL, true);
    792 }
    793 
    794 bool BackgroundSubtractorMOG2Impl::ocl_getBackgroundImage(OutputArray _backgroundImage) const
    795 {
    796     CV_Assert(frameType == CV_8UC1 || frameType == CV_8UC3);
    797 
    798     _backgroundImage.create(frameSize, frameType);
    799     UMat dst = _backgroundImage.getUMat();
    800 
    801     int idxArg = 0;
    802     idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_bgmodelUsedModes));
    803     idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_weight));
    804     idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_mean));
    805     idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst));
    806     kernel_getBg.set(idxArg, backgroundRatio);
    807 
    808     size_t globalsize[2] = {u_bgmodelUsedModes.cols, u_bgmodelUsedModes.rows};
    809 
    810     return kernel_getBg.run(2, globalsize, NULL, false);
    811 }
    812 
    813 #endif
    814 
    815 void BackgroundSubtractorMOG2Impl::create_ocl_apply_kernel()
    816 {
    817     int nchannels = CV_MAT_CN(frameType);
    818     String opts = format("-D CN=%d -D NMIXTURES=%d%s", nchannels, nmixtures, bShadowDetection ? " -D SHADOW_DETECT" : "");
    819     kernel_apply.create("mog2_kernel", ocl::video::bgfg_mog2_oclsrc, opts);
    820 }
    821 
    822 void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate)
    823 {
    824     bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
    825 
    826     if( needToInitialize )
    827         initialize(_image.size(), _image.type());
    828 
    829     if (opencl_ON)
    830     {
    831         CV_OCL_RUN(opencl_ON, ocl_apply(_image, _fgmask, learningRate))
    832 
    833         opencl_ON = false;
    834         initialize(_image.size(), _image.type());
    835     }
    836 
    837     Mat image = _image.getMat();
    838     _fgmask.create( image.size(), CV_8U );
    839     Mat fgmask = _fgmask.getMat();
    840 
    841     ++nframes;
    842     learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
    843     CV_Assert(learningRate >= 0);
    844 
    845     parallel_for_(Range(0, image.rows),
    846                   MOG2Invoker(image, fgmask,
    847                               bgmodel.ptr<GMM>(),
    848                               (float*)(bgmodel.ptr() + sizeof(GMM)*nmixtures*image.rows*image.cols),
    849                               bgmodelUsedModes.ptr(), nmixtures, (float)learningRate,
    850                               (float)varThreshold,
    851                               backgroundRatio, varThresholdGen,
    852                               fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau,
    853                               bShadowDetection, nShadowDetection),
    854                               image.total()/(double)(1 << 16));
    855 }
    856 
    857 void BackgroundSubtractorMOG2Impl::getBackgroundImage(OutputArray backgroundImage) const
    858 {
    859     if (opencl_ON)
    860     {
    861         CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage))
    862 
    863         opencl_ON = false;
    864         return;
    865     }
    866 
    867     int nchannels = CV_MAT_CN(frameType);
    868     CV_Assert(nchannels == 1 || nchannels == 3);
    869     Mat meanBackground(frameSize, CV_MAKETYPE(CV_8U, nchannels), Scalar::all(0));
    870     int firstGaussianIdx = 0;
    871     const GMM* gmm = bgmodel.ptr<GMM>();
    872     const float* mean = reinterpret_cast<const float*>(gmm + frameSize.width*frameSize.height*nmixtures);
    873     std::vector<float> meanVal(nchannels, 0.f);
    874     for(int row=0; row<meanBackground.rows; row++)
    875     {
    876         for(int col=0; col<meanBackground.cols; col++)
    877         {
    878             int nmodes = bgmodelUsedModes.at<uchar>(row, col);
    879             float totalWeight = 0.f;
    880             for(int gaussianIdx = firstGaussianIdx; gaussianIdx < firstGaussianIdx + nmodes; gaussianIdx++)
    881             {
    882                 GMM gaussian = gmm[gaussianIdx];
    883                 size_t meanPosition = gaussianIdx*nchannels;
    884                 for(int chn = 0; chn < nchannels; chn++)
    885                 {
    886                     meanVal[chn] += gaussian.weight * mean[meanPosition + chn];
    887                 }
    888                 totalWeight += gaussian.weight;
    889 
    890                 if(totalWeight > backgroundRatio)
    891                     break;
    892             }
    893             float invWeight = 1.f/totalWeight;
    894             switch(nchannels)
    895             {
    896             case 1:
    897                 meanBackground.at<uchar>(row, col) = (uchar)(meanVal[0] * invWeight);
    898                 meanVal[0] = 0.f;
    899                 break;
    900             case 3:
    901                 Vec3f& meanVec = *reinterpret_cast<Vec3f*>(&meanVal[0]);
    902                 meanBackground.at<Vec3b>(row, col) = Vec3b(meanVec * invWeight);
    903                 meanVec = 0.f;
    904                 break;
    905             }
    906             firstGaussianIdx += nmixtures;
    907         }
    908     }
    909     meanBackground.copyTo(backgroundImage);
    910 }
    911 
    912 Ptr<BackgroundSubtractorMOG2> createBackgroundSubtractorMOG2(int _history, double _varThreshold,
    913                                                              bool _bShadowDetection)
    914 {
    915     return makePtr<BackgroundSubtractorMOG2Impl>(_history, (float)_varThreshold, _bShadowDetection);
    916 }
    917 
    918 }
    919 
    920 /* End of file. */
    921