VAT  3.0
Video Analysis Tool
ReliabilityMog2.h
1 #ifndef RELIABILITYMOG2_H
2 #define RELIABILITYMOG2_H
3 
4 #include <opencv2/opencv.hpp>
5 
6 using namespace cv;
7 
8 /*
9  Interface of Gaussian mixture algorithm from:
10 
11  "Improved adaptive Gausian mixture model for background subtraction"
12  Z.Zivkovic
13  International Conference Pattern Recognition, UK, August, 2004
14  http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
15 
16  Advantages:
17  -fast - number of Gausssian components is constantly adapted per pixel.
18  -performs also shadow detection (see bgfg_segm_test.cpp example)
19 
20 */
21 
22 // default parameters of gaussian background detection algorithm
23 static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2
24 static const float defaultVarThreshold2 = 4.0f*4.0f;
25 static const int defaultNMixtures2 = 5; // maximal number of Gaussians in mixture
26 static const float defaultBackgroundRatio2 = 0.9f; // threshold sum of weights for background test
27 static const float defaultVarThresholdGen2 = 3.0f*3.0f;
28 static const float defaultVarInit2 = 15.0f; // initial variance for new components
29 static const float defaultVarMax2 = 5*defaultVarInit2;
30 static const float defaultVarMin2 = 4.0f;
31 
32 // additional parameters
33 static const float defaultfCT2 = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components
34 static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
35 static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation
36 
37 class ReliabilityMog2 : public cv::BackgroundSubtractorMOG2
38 {
39 public:
42 
44  // the number of gaussian mixtures, the background ratio parameter and the noise strength
45  ReliabilityMog2(int _history, float _varThreshold, bool _bShadowDetection=true);
46 
49 
51  void apply(InputArray image, OutputArray fgmask, double learningRate=-1);
52  void apply(InputArray image, OutputArray fgmask, OutputArray featureMap, double learningRate=-1);
53 
55  virtual void getBackgroundImage(OutputArray backgroundImage) const;
56 
58  void initialize(Size _frameSize, int _frameType);
59 
60  virtual int getHistory() const { return history; }
61  virtual void setHistory(int _nframes) { history = _nframes; }
62 
63  virtual int getNMixtures() const { return nmixtures; }
64  virtual void setNMixtures(int nmix) { nmixtures = nmix; }
65 
66  virtual double getBackgroundRatio() const { return backgroundRatio; }
67  virtual void setBackgroundRatio(double _backgroundRatio) { backgroundRatio = (float)_backgroundRatio; }
68 
69  virtual double getVarThreshold() const { return varThreshold; }
70  virtual void setVarThreshold(double _varThreshold) { varThreshold = _varThreshold; }
71 
72  virtual double getVarThresholdGen() const { return varThresholdGen; }
73  virtual void setVarThresholdGen(double _varThresholdGen) { varThresholdGen = (float)_varThresholdGen; }
74 
75  virtual double getVarInit() const { return fVarInit; }
76  virtual void setVarInit(double varInit) { fVarInit = (float)varInit; }
77 
78  virtual double getVarMin() const { return fVarMin; }
79  virtual void setVarMin(double varMin) { fVarMin = (float)varMin; }
80 
81  virtual double getVarMax() const { return fVarMax; }
82  virtual void setVarMax(double varMax) { fVarMax = (float)varMax; }
83 
84  virtual double getComplexityReductionThreshold() const { return fCT; }
85  virtual void setComplexityReductionThreshold(double ct) { fCT = (float)ct; }
86 
87  virtual bool getDetectShadows() const { return bShadowDetection; }
88  virtual void setDetectShadows(bool detectshadows)
89  {
90  if ((bShadowDetection && detectshadows) || (!bShadowDetection && !detectshadows))
91  return;
92  bShadowDetection = detectshadows;
93  }
94 
95  virtual int getShadowValue() const { return nShadowDetection; }
96  virtual void setShadowValue(int value) { nShadowDetection = (uchar)value; }
97 
98  virtual double getShadowThreshold() const { return fTau; }
99  virtual void setShadowThreshold(double value) { fTau = (float)value; }
100 
101  virtual void write(FileStorage& fs) const
102  {
103  fs << "name" << name_
104  << "history" << history
105  << "nmixtures" << nmixtures
106  << "backgroundRatio" << backgroundRatio
107  << "varThreshold" << varThreshold
108  << "varThresholdGen" << varThresholdGen
109  << "varInit" << fVarInit
110  << "varMin" << fVarMin
111  << "varMax" << fVarMax
112  << "complexityReductionThreshold" << fCT
113  << "detectShadows" << (int)bShadowDetection
114  << "shadowValue" << (int)nShadowDetection
115  << "shadowThreshold" << fTau;
116  }
117 
118  virtual void read(const FileNode& fn)
119  {
120  CV_Assert( (String)fn["name"] == name_ );
121  history = (int)fn["history"];
122  nmixtures = (int)fn["nmixtures"];
123  backgroundRatio = (float)fn["backgroundRatio"];
124  varThreshold = (double)fn["varThreshold"];
125  varThresholdGen = (float)fn["varThresholdGen"];
126  fVarInit = (float)fn["varInit"];
127  fVarMin = (float)fn["varMin"];
128  fVarMax = (float)fn["varMax"];
129  fCT = (float)fn["complexityReductionThreshold"];
130  bShadowDetection = (int)fn["detectShadows"] != 0;
131  nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
132  fTau = (float)fn["shadowThreshold"];
133  }
134 
135 protected:
136  Size frameSize;
137  int frameType;
138  Mat bgmodel;
139  Mat bgmodelUsedModes;//keep track of number of modes per pixel
140 
141  //for OCL
142 
143  mutable bool opencl_ON;
144 
145  UMat u_weight;
146  UMat u_variance;
147  UMat u_mean;
148  UMat u_bgmodelUsedModes;
149 
150  int nframes;
151  int history;
152  int nmixtures;
155  double varThreshold;
156  // threshold on the squared Mahalanobis distance to decide if it is well described
157  // by the background model or not. Related to Cthr from the paper.
158  // This does not influence the update of the background. A typical value could be 4 sigma
159  // and that is varThreshold=4*4=16; Corresponds to Tb in the paper.
160 
162  // less important parameters - things you might change but be carefull
164  float backgroundRatio;
165  // corresponds to fTB=1-cf from the paper
166  // TB - threshold when the component becomes significant enough to be included into
167  // the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
168  // For alpha=0.001 it means that the mode should exist for approximately 105 frames before
169  // it is considered foreground
170  // float noiseSigma;
171  float varThresholdGen;
172  //correspondts to Tg - threshold on the squared Mahalan. dist. to decide
173  //when a sample is close to the existing components. If it is not close
174  //to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
175  //Smaller Tg leads to more generated components and higher Tg might make
176  //lead to small number of components but they can grow too large
177  float fVarInit;
178  float fVarMin;
179  float fVarMax;
180  //initial variance for the newly generated components.
181  //It will will influence the speed of adaptation. A good guess should be made.
182  //A simple way is to estimate the typical standard deviation from the images.
183  //I used here 10 as a reasonable value
184  // min and max can be used to further control the variance
185  float fCT;//CT - complexity reduction prior
186  //this is related to the number of samples needed to accept that a component
187  //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
188  //the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
189 
190  //shadow detection parameters
191  bool bShadowDetection;//default 1 - do shadow detection
192  unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
193  float fTau;
194  // Tau - shadow threshold. The shadow is detected if the pixel is darker
195  //version of the background. Tau is a threshold on how much darker the shadow can be.
196  //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
197  //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
198 
199  String name_;
200 
201  bool ocl_getBackgroundImage(OutputArray backgroundImage) const;
202  bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);
203  void create_ocl_apply_kernel();
204 };
205 
207 {
208  //image info
209  int nWidth;
210  int nHeight;
211  int nND;//number of data dimensions (image channels)
212 
213  bool bPostFiltering;//defult 1 - do postfiltering - will make shadow detection results also give value 255
214  double minArea; // for postfiltering
215 
216  bool bInit;//default 1, faster updates at start
217 
219  //very important parameters - things you will change
221  float fAlphaT;
222  //alpha - speed of update - if the time interval you want to average over is T
223  //set alpha=1/T. It is also usefull at start to make T slowly increase
224  //from 1 until the desired T
225  float fTb;
226  //Tb - threshold on the squared Mahalan. dist. to decide if it is well described
227  //by the background model or not. Related to Cthr from the paper.
228  //This does not influence the update of the background. A typical value could be 4 sigma
229  //and that is Tb=4*4=16;
230 
232  //less important parameters - things you might change but be carefull
234  float fTg;
235  //Tg - threshold on the squared Mahalan. dist. to decide
236  //when a sample is close to the existing components. If it is not close
237  //to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
238  //Smaller Tg leads to more generated components and higher Tg might make
239  //lead to small number of components but they can grow too large
240  float fTB;//1-cf from the paper
241  //TB - threshold when the component becomes significant enough to be included into
242  //the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
243  //For alpha=0.001 it means that the mode should exist for approximately 105 frames before
244  //it is considered foreground
245  float fVarInit;
246  float fVarMax;
247  float fVarMin;
248  //initial standard deviation for the newly generated components.
249  //It will will influence the speed of adaptation. A good guess should be made.
250  //A simple way is to estimate the typical standard deviation from the images.
251  //I used here 10 as a reasonable value
252  float fCT;//CT - complexity reduction prior
253  //this is related to the number of samples needed to accept that a component
254  //actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
255  //the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
256 
257  //even less important parameters
258  int nM;//max number of modes - const - 4 is usually enough
259 
260  //shadow detection parameters
261  bool bShadowDetection;//default 1 - do shadow detection
262  unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result
263  float fTau;
264  // Tau - shadow threshold. The shadow is detected if the pixel is darker
265  //version of the background. Tau is a threshold on how much darker the shadow can be.
266  //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
267  //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
268 };
269 
270 struct GMM
271 {
272  float weight;
273  float variance;
274 };
275 
276 // shadow detection performed per pixel
277 // should work for rgb data, could be usefull for gray scale and depth data as well
278 // See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003.
279 CV_INLINE bool
280 detectShadowGMM(const float* data, int nchannels, int nmodes,
281  const GMM* gmm, const float* mean,
282  float Tb, float TB, float tau)
283 {
284  float tWeight = 0;
285 
286  // check all the components marked as background:
287  for( int mode = 0; mode < nmodes; mode++, mean += nchannels )
288  {
289  GMM g = gmm[mode];
290 
291  float numerator = 0.0f;
292  float denominator = 0.0f;
293  for( int c = 0; c < nchannels; c++ )
294  {
295  numerator += data[c] * mean[c];
296  denominator += mean[c] * mean[c];
297  }
298 
299  // no division by zero allowed
300  if( denominator == 0 )
301  return false;
302 
303  // if tau < a < 1 then also check the color distortion
304  if( numerator <= denominator && numerator >= tau*denominator )
305  {
306  float a = numerator / denominator;
307  float dist2a = 0.0f;
308 
309  for( int c = 0; c < nchannels; c++ )
310  {
311  float dD= a*mean[c] - data[c];
312  dist2a += dD*dD;
313  }
314 
315  if (dist2a < Tb*g.variance*a*a)
316  return true;
317  };
318 
319  tWeight += g.weight;
320  if( tWeight > TB )
321  return false;
322  };
323  return false;
324 }
325 
326 //update GMM - the base update function performed per pixel
327 //
328 //"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"
329 //Z.Zivkovic, F. van der Heijden
330 //Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
331 //
332 //The algorithm similar to the standard Stauffer&Grimson algorithm with
333 //additional selection of the number of the Gaussian components based on:
334 //
335 //"Recursive unsupervised learning of finite mixture models "
336 //Z.Zivkovic, F.van der Heijden
337 //IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004
338 //http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf
339 
340 class MOG2Invoker : public ParallelLoopBody
341 {
342 public:
343  MOG2Invoker(const Mat& _src, Mat& _dst, Mat& _featureMap,
344  GMM* _gmm, float* _mean,
345  uchar* _modesUsed,
346  int _nmixtures, float _alphaT,
347  float _Tb, float _TB, float _Tg,
348  float _varInit, float _varMin, float _varMax,
349  float _prune, float _tau, bool _detectShadows,
350  uchar _shadowVal)
351  {
352  src = &_src;
353  dst = &_dst;
354  featureMap = &_featureMap;
355  gmm0 = _gmm;
356  mean0 = _mean;
357  modesUsed0 = _modesUsed;
358  nmixtures = _nmixtures;
359  alphaT = _alphaT;
360  Tb = _Tb;
361  TB = _TB;
362  Tg = _Tg;
363  varInit = _varInit;
364  varMin = MIN(_varMin, _varMax);
365  varMax = MAX(_varMin, _varMax);
366  prune = _prune;
367  tau = _tau;
368  detectShadows = _detectShadows;
369  shadowVal = _shadowVal;
370  }
371 
372  void operator()(const Range& range) const
373  {
374  int y0 = range.start, y1 = range.end;
375  int ncols = src->cols, nchannels = src->channels();
376  AutoBuffer<float> buf(src->cols*nchannels);
377  float alpha1 = 1.f - alphaT;
378  float dData[CV_CN_MAX];
379  float featureVal;
380 
381  for( int y = y0; y < y1; y++ )
382  {
383  const float* data = buf;
384  if( src->depth() != CV_32F )
385  src->row(y).convertTo(Mat(1, ncols, CV_32FC(nchannels), (void*)data), CV_32F);
386  else
387  data = src->ptr<float>(y);
388 
389  float* mean = mean0 + ncols*nmixtures*nchannels*y;
390  GMM* gmm = gmm0 + ncols*nmixtures*y;
391  uchar* modesUsed = modesUsed0 + ncols*y;
392  uchar* mask = dst->ptr(y);
393  uchar* ptrFeatureMap = featureMap->ptr(y);
394 
395  for( int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels )
396  {
397  //calculate distances to the modes (+ sort)
398  //here we need to go in descending order!!!
399  bool background = false;//return value -> true - the pixel classified as background
400 
401  //internal:
402  bool fitsPDF = false;//if it remains false a new GMM mode will be added
403  int nmodes = modesUsed[x], nNewModes = nmodes;//current number of modes in GMM
404  float totalWeight = 0.f;
405 
406  float* mean_m = mean;
407  featureVal = 100000;
408 
410  //go through all modes
411  for( int mode = 0; mode < nmodes; mode++, mean_m += nchannels )
412  {
413  float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found
414  int swap_count = 0;
416  //fit not found yet
417  if( !fitsPDF )
418  {
419  //check if it belongs to some of the remaining modes
420  float var = gmm[mode].variance;
421 
422  //calculate difference and distance
423  float dist2;
424 
425  if( nchannels == 3 )
426  {
427  dData[0] = mean_m[0] - data[0];
428  dData[1] = mean_m[1] - data[1];
429  dData[2] = mean_m[2] - data[2];
430  dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
431  }
432  else
433  {
434  dist2 = 0.f;
435  for( int c = 0; c < nchannels; c++ )
436  {
437  dData[c] = mean_m[c] - data[c];
438  dist2 += dData[c]*dData[c];
439  }
440  }
441 
442  if( totalWeight < TB && dist2 < featureVal)
443  {
444  featureVal = dist2;
445  }
446 
447  //background? - Tb - usually larger than Tg
448  if( totalWeight < TB && dist2 < Tb*var )
449  {
450  background = true;
451 // featureVal = dist2;
452  }
453 
454  //check fit
455  if( dist2 < Tg*var )
456  {
457 
458  //feature map of closest mode
459 // if( totalWeight < TB && dist2 < featureVal)
460 // featureVal = dist2;
461 
463  //belongs to the mode
464  fitsPDF = true;
465 
466  //update distribution
467 
468  //update weight
469  weight += alphaT;
470  float k = alphaT/weight;
471 
472  //update mean
473  for( int c = 0; c < nchannels; c++ )
474  mean_m[c] -= k*dData[c];
475 
476  //update variance
477  float varnew = var + k*(dist2-var);
478  //limit the variance
479  varnew = MAX(varnew, varMin);
480  varnew = MIN(varnew, varMax);
481  gmm[mode].variance = varnew;
482 
483  //sort
484  //all other weights are at the same place and
485  //only the matched (iModes) is higher -> just find the new place for it
486  for( int i = mode; i > 0; i-- )
487  {
488  //check one up
489  if( weight < gmm[i-1].weight )
490  break;
491 
492  swap_count++;
493  //swap one up
494  std::swap(gmm[i], gmm[i-1]);
495  for( int c = 0; c < nchannels; c++ )
496  std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
497  }
498  //belongs to the mode - bFitsPDF becomes 1
500  }
501  }
502 
503  //check prune
504  if( weight < -prune )
505  {
506  weight = 0.0;
507  nmodes--;
508  }
509 
510  gmm[mode-swap_count].weight = weight;//update weight by the calculated value
511  totalWeight += weight;
512  }
513  //go through all modes
515 
516  //renormalize weights
517  totalWeight = 1.f/totalWeight;
518  for( int mode = 0; mode < nmodes; mode++ )
519  {
520  gmm[mode].weight *= totalWeight;
521  }
522 
523  nmodes = nNewModes;
524 
525  //make new mode if needed and exit
526  if( !fitsPDF && alphaT > 0.f )
527  {
528  // replace the weakest or add a new one
529  int mode = nmodes == nmixtures ? nmixtures-1 : nmodes++;
530 
531  if (nmodes==1)
532  gmm[mode].weight = 1.f;
533  else
534  {
535  gmm[mode].weight = alphaT;
536 
537  // renormalize all other weights
538  for( int i = 0; i < nmodes-1; i++ )
539  gmm[i].weight *= alpha1;
540  }
541 
542  // init
543  for( int c = 0; c < nchannels; c++ )
544  mean[mode*nchannels + c] = data[c];
545 
546  gmm[mode].variance = varInit;
547 
548  //sort
549  //find the new place for it
550  for( int i = nmodes - 1; i > 0; i-- )
551  {
552  // check one up
553  if( alphaT < gmm[i-1].weight )
554  break;
555 
556  // swap one up
557  std::swap(gmm[i], gmm[i-1]);
558  for( int c = 0; c < nchannels; c++ )
559  std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
560  }
561  }
562 
563  //set the number of modes
564  modesUsed[x] = uchar(nmodes);
565  mask[x] = background ? 0 :
566  detectShadows && detectShadowGMM(data, nchannels, nmodes, gmm, mean, Tb, TB, tau) ?
567  shadowVal : 255;
568  if(featureVal == 100000)
569  ptrFeatureMap[x] = 0;
570  else
571  ptrFeatureMap[x] = featureVal < 255? featureVal: 255;
572  }
573  }
574  }
575 
576  const Mat* src;
577  Mat* dst;
578  Mat* featureMap;
579  GMM* gmm0;
580  float* mean0;
581  uchar* modesUsed0;
582 
583  int nmixtures;
584  float alphaT, Tb, TB, Tg;
585  float varInit, varMin, varMax, prune, tau;
586 
587  bool detectShadows;
588  uchar shadowVal;
589 };
590 
591 
592 
593 //Ptr<BackgroundSubtractorMOG2> createBackgroundSubtractorMOG2(int _history, double _varThreshold,
594 // bool _bShadowDetection)
595 //{
596 // return makePtr<ReliabilityMog2>(_history, (float)_varThreshold, _bShadowDetection);
597 //}
598 
599 #endif // RELIABILITYMOG2_H
double varThreshold
Definition: ReliabilityMog2.h:155
Definition: ReliabilityMog2.h:206
void operator()(const Range &range) const
Definition: ReliabilityMog2.h:372
Definition: QtOpencvConversion.h:19
~ReliabilityMog2()
the destructor
Definition: ReliabilityMog2.h:48
Definition: ReliabilityMog2.h:270
Definition: ReliabilityMog2.h:340
Definition: ReliabilityMog2.h:37
Definition: BackgroundRecLigth.h:20