Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/video/src/bgfg_gaussmix2.cpp
16337 views
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 Gaussian 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 Gaussian 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 CV_FINAL : 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
#ifdef HAVE_OPENCL
146
opencl_ON = true;
147
#endif
148
}
149
//! the full constructor that takes the length of the history,
150
// the number of gaussian mixtures, the background ratio parameter and the noise strength
151
BackgroundSubtractorMOG2Impl(int _history, float _varThreshold, bool _bShadowDetection=true)
152
{
153
frameSize = Size(0,0);
154
frameType = 0;
155
156
nframes = 0;
157
history = _history > 0 ? _history : defaultHistory2;
158
varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2;
159
bShadowDetection = _bShadowDetection;
160
161
nmixtures = defaultNMixtures2;
162
backgroundRatio = defaultBackgroundRatio2;
163
fVarInit = defaultVarInit2;
164
fVarMax = defaultVarMax2;
165
fVarMin = defaultVarMin2;
166
167
varThresholdGen = defaultVarThresholdGen2;
168
fCT = defaultfCT2;
169
nShadowDetection = defaultnShadowDetection2;
170
fTau = defaultfTau;
171
name_ = "BackgroundSubtractor.MOG2";
172
#ifdef HAVE_OPENCL
173
opencl_ON = true;
174
#endif
175
}
176
//! the destructor
177
~BackgroundSubtractorMOG2Impl() CV_OVERRIDE {}
178
//! the update operator
179
void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;
180
181
//! computes a background image which are the mean of all background gaussians
182
virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;
183
184
//! re-initiaization method
185
void initialize(Size _frameSize, int _frameType)
186
{
187
frameSize = _frameSize;
188
frameType = _frameType;
189
nframes = 0;
190
191
int nchannels = CV_MAT_CN(frameType);
192
CV_Assert( nchannels <= CV_CN_MAX );
193
CV_Assert( nmixtures <= 255);
194
195
#ifdef HAVE_OPENCL
196
if (ocl::isOpenCLActivated() && opencl_ON)
197
{
198
create_ocl_apply_kernel();
199
200
bool isFloat = CV_MAKETYPE(CV_32F,nchannels) == frameType;
201
kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_mog2_oclsrc, format( "-D CN=%d -D FL=%d -D NMIXTURES=%d", nchannels, isFloat, nmixtures));
202
203
if (kernel_apply.empty() || kernel_getBg.empty())
204
opencl_ON = false;
205
}
206
else opencl_ON = false;
207
208
if (opencl_ON)
209
{
210
u_weight.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);
211
u_weight.setTo(Scalar::all(0));
212
213
u_variance.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);
214
u_variance.setTo(Scalar::all(0));
215
216
if (nchannels==3)
217
nchannels=4;
218
u_mean.create(frameSize.height * nmixtures, frameSize.width, CV_32FC(nchannels)); //4 channels
219
u_mean.setTo(Scalar::all(0));
220
221
//make the array for keeping track of the used modes per pixel - all zeros at start
222
u_bgmodelUsedModes.create(frameSize, CV_8UC1);
223
u_bgmodelUsedModes.setTo(cv::Scalar::all(0));
224
}
225
else
226
#endif
227
{
228
// for each gaussian mixture of each pixel bg model we store ...
229
// the mixture weight (w),
230
// the mean (nchannels values) and
231
// the covariance
232
bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F );
233
//make the array for keeping track of the used modes per pixel - all zeros at start
234
bgmodelUsedModes.create(frameSize,CV_8U);
235
bgmodelUsedModes = Scalar::all(0);
236
}
237
}
238
239
virtual int getHistory() const CV_OVERRIDE { return history; }
240
virtual void setHistory(int _nframes) CV_OVERRIDE { history = _nframes; }
241
242
virtual int getNMixtures() const CV_OVERRIDE { return nmixtures; }
243
virtual void setNMixtures(int nmix) CV_OVERRIDE { nmixtures = nmix; }
244
245
virtual double getBackgroundRatio() const CV_OVERRIDE { return backgroundRatio; }
246
virtual void setBackgroundRatio(double _backgroundRatio) CV_OVERRIDE { backgroundRatio = (float)_backgroundRatio; }
247
248
virtual double getVarThreshold() const CV_OVERRIDE { return varThreshold; }
249
virtual void setVarThreshold(double _varThreshold) CV_OVERRIDE { varThreshold = _varThreshold; }
250
251
virtual double getVarThresholdGen() const CV_OVERRIDE { return varThresholdGen; }
252
virtual void setVarThresholdGen(double _varThresholdGen) CV_OVERRIDE { varThresholdGen = (float)_varThresholdGen; }
253
254
virtual double getVarInit() const CV_OVERRIDE { return fVarInit; }
255
virtual void setVarInit(double varInit) CV_OVERRIDE { fVarInit = (float)varInit; }
256
257
virtual double getVarMin() const CV_OVERRIDE { return fVarMin; }
258
virtual void setVarMin(double varMin) CV_OVERRIDE { fVarMin = (float)varMin; }
259
260
virtual double getVarMax() const CV_OVERRIDE { return fVarMax; }
261
virtual void setVarMax(double varMax) CV_OVERRIDE { fVarMax = (float)varMax; }
262
263
virtual double getComplexityReductionThreshold() const CV_OVERRIDE { return fCT; }
264
virtual void setComplexityReductionThreshold(double ct) CV_OVERRIDE { fCT = (float)ct; }
265
266
virtual bool getDetectShadows() const CV_OVERRIDE { return bShadowDetection; }
267
virtual void setDetectShadows(bool detectshadows) CV_OVERRIDE
268
{
269
if (bShadowDetection == detectshadows)
270
return;
271
bShadowDetection = detectshadows;
272
#ifdef HAVE_OPENCL
273
if (!kernel_apply.empty())
274
{
275
create_ocl_apply_kernel();
276
CV_Assert( !kernel_apply.empty() );
277
}
278
#endif
279
}
280
281
virtual int getShadowValue() const CV_OVERRIDE { return nShadowDetection; }
282
virtual void setShadowValue(int value) CV_OVERRIDE { nShadowDetection = (uchar)value; }
283
284
virtual double getShadowThreshold() const CV_OVERRIDE { return fTau; }
285
virtual void setShadowThreshold(double value) CV_OVERRIDE { fTau = (float)value; }
286
287
virtual void write(FileStorage& fs) const CV_OVERRIDE
288
{
289
writeFormat(fs);
290
fs << "name" << name_
291
<< "history" << history
292
<< "nmixtures" << nmixtures
293
<< "backgroundRatio" << backgroundRatio
294
<< "varThreshold" << varThreshold
295
<< "varThresholdGen" << varThresholdGen
296
<< "varInit" << fVarInit
297
<< "varMin" << fVarMin
298
<< "varMax" << fVarMax
299
<< "complexityReductionThreshold" << fCT
300
<< "detectShadows" << (int)bShadowDetection
301
<< "shadowValue" << (int)nShadowDetection
302
<< "shadowThreshold" << fTau;
303
}
304
305
virtual void read(const FileNode& fn) CV_OVERRIDE
306
{
307
CV_Assert( (String)fn["name"] == name_ );
308
history = (int)fn["history"];
309
nmixtures = (int)fn["nmixtures"];
310
backgroundRatio = (float)fn["backgroundRatio"];
311
varThreshold = (double)fn["varThreshold"];
312
varThresholdGen = (float)fn["varThresholdGen"];
313
fVarInit = (float)fn["varInit"];
314
fVarMin = (float)fn["varMin"];
315
fVarMax = (float)fn["varMax"];
316
fCT = (float)fn["complexityReductionThreshold"];
317
bShadowDetection = (int)fn["detectShadows"] != 0;
318
nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
319
fTau = (float)fn["shadowThreshold"];
320
}
321
322
protected:
323
Size frameSize;
324
int frameType;
325
Mat bgmodel;
326
Mat bgmodelUsedModes;//keep track of number of modes per pixel
327
328
#ifdef HAVE_OPENCL
329
//for OCL
330
331
mutable bool opencl_ON;
332
333
UMat u_weight;
334
UMat u_variance;
335
UMat u_mean;
336
UMat u_bgmodelUsedModes;
337
338
mutable ocl::Kernel kernel_apply;
339
mutable ocl::Kernel kernel_getBg;
340
#endif
341
342
int nframes;
343
int history;
344
int nmixtures;
345
//! here it is the maximum allowed number of mixture components.
346
//! Actual number is determined dynamically per pixel
347
double varThreshold;
348
// threshold on the squared Mahalanobis distance to decide if it is well described
349
// by the background model or not. Related to Cthr from the paper.
350
// This does not influence the update of the background. A typical value could be 4 sigma
351
// and that is varThreshold=4*4=16; Corresponds to Tb in the paper.
352
353
/////////////////////////
354
// less important parameters - things you might change but be careful
355
////////////////////////
356
float backgroundRatio;
357
// corresponds to fTB=1-cf from the paper
358
// TB - threshold when the component becomes significant enough to be included into
359
// the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
360
// For alpha=0.001 it means that the mode should exist for approximately 105 frames before
361
// it is considered foreground
362
// float noiseSigma;
363
float varThresholdGen;
364
//correspondts to Tg - threshold on the squared Mahalan. dist. to decide
365
//when a sample is close to the existing components. If it is not close
366
//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
367
//Smaller Tg leads to more generated components and higher Tg might make
368
//lead to small number of components but they can grow too large
369
float fVarInit;
370
float fVarMin;
371
float fVarMax;
372
//initial variance for the newly generated components.
373
//It will will influence the speed of adaptation. A good guess should be made.
374
//A simple way is to estimate the typical standard deviation from the images.
375
//I used here 10 as a reasonable value
376
// min and max can be used to further control the variance
377
float fCT;//CT - complexity reduction prior
378
//this is related to the number of samples needed to accept that a component
379
//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
380
//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
381
382
//shadow detection parameters
383
bool bShadowDetection;//default 1 - do shadow detection
384
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
385
float fTau;
386
// Tau - shadow threshold. The shadow is detected if the pixel is darker
387
//version of the background. Tau is a threshold on how much darker the shadow can be.
388
//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
389
//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
390
391
String name_;
392
393
template <typename T, int CN>
394
void getBackgroundImage_intern(OutputArray backgroundImage) const;
395
396
#ifdef HAVE_OPENCL
397
bool ocl_getBackgroundImage(OutputArray backgroundImage) const;
398
bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);
399
void create_ocl_apply_kernel();
400
#endif
401
};
402
403
struct GaussBGStatModel2Params
404
{
405
//image info
406
int nWidth;
407
int nHeight;
408
int nND;//number of data dimensions (image channels)
409
410
bool bPostFiltering;//default 1 - do postfiltering - will make shadow detection results also give value 255
411
double minArea; // for postfiltering
412
413
bool bInit;//default 1, faster updates at start
414
415
/////////////////////////
416
//very important parameters - things you will change
417
////////////////////////
418
float fAlphaT;
419
//alpha - speed of update - if the time interval you want to average over is T
420
//set alpha=1/T. It is also useful at start to make T slowly increase
421
//from 1 until the desired T
422
float fTb;
423
//Tb - threshold on the squared Mahalan. dist. to decide if it is well described
424
//by the background model or not. Related to Cthr from the paper.
425
//This does not influence the update of the background. A typical value could be 4 sigma
426
//and that is Tb=4*4=16;
427
428
/////////////////////////
429
//less important parameters - things you might change but be careful
430
////////////////////////
431
float fTg;
432
//Tg - threshold on the squared Mahalan. dist. to decide
433
//when a sample is close to the existing components. If it is not close
434
//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
435
//Smaller Tg leads to more generated components and higher Tg might make
436
//lead to small number of components but they can grow too large
437
float fTB;//1-cf from the paper
438
//TB - threshold when the component becomes significant enough to be included into
439
//the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
440
//For alpha=0.001 it means that the mode should exist for approximately 105 frames before
441
//it is considered foreground
442
float fVarInit;
443
float fVarMax;
444
float fVarMin;
445
//initial standard deviation for the newly generated components.
446
//It will will influence the speed of adaptation. A good guess should be made.
447
//A simple way is to estimate the typical standard deviation from the images.
448
//I used here 10 as a reasonable value
449
float fCT;//CT - complexity reduction prior
450
//this is related to the number of samples needed to accept that a component
451
//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
452
//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
453
454
//even less important parameters
455
int nM;//max number of modes - const - 4 is usually enough
456
457
//shadow detection parameters
458
bool bShadowDetection;//default 1 - do shadow detection
459
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result
460
float fTau;
461
// Tau - shadow threshold. The shadow is detected if the pixel is darker
462
//version of the background. Tau is a threshold on how much darker the shadow can be.
463
//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
464
//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
465
};
466
467
struct GMM
468
{
469
float weight;
470
float variance;
471
};
472
473
// shadow detection performed per pixel
474
// should work for rgb data, could be useful for gray scale and depth data as well
475
// See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
476
CV_INLINE bool
477
detectShadowGMM(const float* data, int nchannels, int nmodes,
478
const GMM* gmm, const float* mean,
479
float Tb, float TB, float tau)
480
{
481
float tWeight = 0;
482
483
// check all the components marked as background:
484
for( int mode = 0; mode < nmodes; mode++, mean += nchannels )
485
{
486
GMM g = gmm[mode];
487
488
float numerator = 0.0f;
489
float denominator = 0.0f;
490
for( int c = 0; c < nchannels; c++ )
491
{
492
numerator += data[c] * mean[c];
493
denominator += mean[c] * mean[c];
494
}
495
496
// no division by zero allowed
497
if( denominator == 0 )
498
return false;
499
500
// if tau < a < 1 then also check the color distortion
501
if( numerator <= denominator && numerator >= tau*denominator )
502
{
503
float a = numerator / denominator;
504
float dist2a = 0.0f;
505
506
for( int c = 0; c < nchannels; c++ )
507
{
508
float dD= a*mean[c] - data[c];
509
dist2a += dD*dD;
510
}
511
512
if (dist2a < Tb*g.variance*a*a)
513
return true;
514
};
515
516
tWeight += g.weight;
517
if( tWeight > TB )
518
return false;
519
};
520
return false;
521
}
522
523
//update GMM - the base update function performed per pixel
524
//
525
//"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"
526
//Z.Zivkovic, F. van der Heijden
527
//Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
528
//
529
//The algorithm similar to the standard Stauffer&Grimson algorithm with
530
//additional selection of the number of the Gaussian components based on:
531
//
532
//"Recursive unsupervised learning of finite mixture models "
533
//Z.Zivkovic, F.van der Heijden
534
//IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004
535
//http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf
536
537
class MOG2Invoker : public ParallelLoopBody
538
{
539
public:
540
MOG2Invoker(const Mat& _src, Mat& _dst,
541
GMM* _gmm, float* _mean,
542
uchar* _modesUsed,
543
int _nmixtures, float _alphaT,
544
float _Tb, float _TB, float _Tg,
545
float _varInit, float _varMin, float _varMax,
546
float _prune, float _tau, bool _detectShadows,
547
uchar _shadowVal)
548
{
549
src = &_src;
550
dst = &_dst;
551
gmm0 = _gmm;
552
mean0 = _mean;
553
modesUsed0 = _modesUsed;
554
nmixtures = _nmixtures;
555
alphaT = _alphaT;
556
Tb = _Tb;
557
TB = _TB;
558
Tg = _Tg;
559
varInit = _varInit;
560
varMin = MIN(_varMin, _varMax);
561
varMax = MAX(_varMin, _varMax);
562
prune = _prune;
563
tau = _tau;
564
detectShadows = _detectShadows;
565
shadowVal = _shadowVal;
566
}
567
568
void operator()(const Range& range) const CV_OVERRIDE
569
{
570
int y0 = range.start, y1 = range.end;
571
int ncols = src->cols, nchannels = src->channels();
572
AutoBuffer<float> buf(src->cols*nchannels);
573
float alpha1 = 1.f - alphaT;
574
float dData[CV_CN_MAX];
575
576
for( int y = y0; y < y1; y++ )
577
{
578
const float* data = buf.data();
579
if( src->depth() != CV_32F )
580
src->row(y).convertTo(Mat(1, ncols, CV_32FC(nchannels), (void*)data), CV_32F);
581
else
582
data = src->ptr<float>(y);
583
584
float* mean = mean0 + ncols*nmixtures*nchannels*y;
585
GMM* gmm = gmm0 + ncols*nmixtures*y;
586
uchar* modesUsed = modesUsed0 + ncols*y;
587
uchar* mask = dst->ptr(y);
588
589
for( int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels )
590
{
591
//calculate distances to the modes (+ sort)
592
//here we need to go in descending order!!!
593
bool background = false;//return value -> true - the pixel classified as background
594
595
//internal:
596
bool fitsPDF = false;//if it remains zero a new GMM mode will be added
597
int nmodes = modesUsed[x];//current number of modes in GMM
598
float totalWeight = 0.f;
599
600
float* mean_m = mean;
601
602
//////
603
//go through all modes
604
for( int mode = 0; mode < nmodes; mode++, mean_m += nchannels )
605
{
606
float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found
607
int swap_count = 0;
608
////
609
//fit not found yet
610
if( !fitsPDF )
611
{
612
//check if it belongs to some of the remaining modes
613
float var = gmm[mode].variance;
614
615
//calculate difference and distance
616
float dist2;
617
618
if( nchannels == 3 )
619
{
620
dData[0] = mean_m[0] - data[0];
621
dData[1] = mean_m[1] - data[1];
622
dData[2] = mean_m[2] - data[2];
623
dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
624
}
625
else
626
{
627
dist2 = 0.f;
628
for( int c = 0; c < nchannels; c++ )
629
{
630
dData[c] = mean_m[c] - data[c];
631
dist2 += dData[c]*dData[c];
632
}
633
}
634
635
//background? - Tb - usually larger than Tg
636
if( totalWeight < TB && dist2 < Tb*var )
637
background = true;
638
639
//check fit
640
if( dist2 < Tg*var )
641
{
642
/////
643
//belongs to the mode
644
fitsPDF = true;
645
646
//update distribution
647
648
//update weight
649
weight += alphaT;
650
float k = alphaT/weight;
651
652
//update mean
653
for( int c = 0; c < nchannels; c++ )
654
mean_m[c] -= k*dData[c];
655
656
//update variance
657
float varnew = var + k*(dist2-var);
658
//limit the variance
659
varnew = MAX(varnew, varMin);
660
varnew = MIN(varnew, varMax);
661
gmm[mode].variance = varnew;
662
663
//sort
664
//all other weights are at the same place and
665
//only the matched (iModes) is higher -> just find the new place for it
666
for( int i = mode; i > 0; i-- )
667
{
668
//check one up
669
if( weight < gmm[i-1].weight )
670
break;
671
672
swap_count++;
673
//swap one up
674
std::swap(gmm[i], gmm[i-1]);
675
for( int c = 0; c < nchannels; c++ )
676
std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
677
}
678
//belongs to the mode - bFitsPDF becomes 1
679
/////
680
}
681
}//!bFitsPDF)
682
683
//check prune
684
if( weight < -prune )
685
{
686
weight = 0.0;
687
nmodes--;
688
}
689
690
gmm[mode-swap_count].weight = weight;//update weight by the calculated value
691
totalWeight += weight;
692
}
693
//go through all modes
694
//////
695
696
// Renormalize weights. In the special case that the pixel does
697
// not agree with any modes, set weights to zero (a new mode will be added below).
698
float invWeight = 0.f;
699
if (std::abs(totalWeight) > FLT_EPSILON) {
700
invWeight = 1.f/totalWeight;
701
}
702
703
for( int mode = 0; mode < nmodes; mode++ )
704
{
705
gmm[mode].weight *= invWeight;
706
}
707
708
//make new mode if needed and exit
709
if( !fitsPDF && alphaT > 0.f )
710
{
711
// replace the weakest or add a new one
712
int mode = nmodes == nmixtures ? nmixtures-1 : nmodes++;
713
714
if (nmodes==1)
715
gmm[mode].weight = 1.f;
716
else
717
{
718
gmm[mode].weight = alphaT;
719
720
// renormalize all other weights
721
for( int i = 0; i < nmodes-1; i++ )
722
gmm[i].weight *= alpha1;
723
}
724
725
// init
726
for( int c = 0; c < nchannels; c++ )
727
mean[mode*nchannels + c] = data[c];
728
729
gmm[mode].variance = varInit;
730
731
//sort
732
//find the new place for it
733
for( int i = nmodes - 1; i > 0; i-- )
734
{
735
// check one up
736
if( alphaT < gmm[i-1].weight )
737
break;
738
739
// swap one up
740
std::swap(gmm[i], gmm[i-1]);
741
for( int c = 0; c < nchannels; c++ )
742
std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
743
}
744
}
745
746
//set the number of modes
747
modesUsed[x] = uchar(nmodes);
748
mask[x] = background ? 0 :
749
detectShadows && detectShadowGMM(data, nchannels, nmodes, gmm, mean, Tb, TB, tau) ?
750
shadowVal : 255;
751
}
752
}
753
}
754
755
const Mat* src;
756
Mat* dst;
757
GMM* gmm0;
758
float* mean0;
759
uchar* modesUsed0;
760
761
int nmixtures;
762
float alphaT, Tb, TB, Tg;
763
float varInit, varMin, varMax, prune, tau;
764
765
bool detectShadows;
766
uchar shadowVal;
767
};
768
769
#ifdef HAVE_OPENCL
770
771
bool BackgroundSubtractorMOG2Impl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate)
772
{
773
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
774
775
if( needToInitialize )
776
initialize(_image.size(), _image.type());
777
778
++nframes;
779
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
780
CV_Assert(learningRate >= 0);
781
782
_fgmask.create(_image.size(), CV_8U);
783
UMat fgmask = _fgmask.getUMat();
784
785
const double alpha1 = 1.0f - learningRate;
786
787
UMat frame = _image.getUMat();
788
789
float varMax = MAX(fVarMin, fVarMax);
790
float varMin = MIN(fVarMin, fVarMax);
791
792
int idxArg = 0;
793
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame));
794
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_bgmodelUsedModes));
795
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_weight));
796
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_mean));
797
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_variance));
798
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask));
799
800
idxArg = kernel_apply.set(idxArg, (float)learningRate); //alphaT
801
idxArg = kernel_apply.set(idxArg, (float)alpha1);
802
idxArg = kernel_apply.set(idxArg, (float)(-learningRate*fCT)); //prune
803
804
idxArg = kernel_apply.set(idxArg, (float)varThreshold); //c_Tb
805
idxArg = kernel_apply.set(idxArg, backgroundRatio); //c_TB
806
idxArg = kernel_apply.set(idxArg, varThresholdGen); //c_Tg
807
idxArg = kernel_apply.set(idxArg, varMin);
808
idxArg = kernel_apply.set(idxArg, varMax);
809
idxArg = kernel_apply.set(idxArg, fVarInit);
810
idxArg = kernel_apply.set(idxArg, fTau);
811
if (bShadowDetection)
812
kernel_apply.set(idxArg, nShadowDetection);
813
814
size_t globalsize[] = {(size_t)frame.cols, (size_t)frame.rows, 1};
815
return kernel_apply.run(2, globalsize, NULL, true);
816
}
817
818
bool BackgroundSubtractorMOG2Impl::ocl_getBackgroundImage(OutputArray _backgroundImage) const
819
{
820
_backgroundImage.create(frameSize, frameType);
821
UMat dst = _backgroundImage.getUMat();
822
823
int idxArg = 0;
824
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_bgmodelUsedModes));
825
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_weight));
826
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_mean));
827
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst));
828
kernel_getBg.set(idxArg, backgroundRatio);
829
830
size_t globalsize[2] = {(size_t)u_bgmodelUsedModes.cols, (size_t)u_bgmodelUsedModes.rows};
831
832
return kernel_getBg.run(2, globalsize, NULL, false);
833
}
834
835
void BackgroundSubtractorMOG2Impl::create_ocl_apply_kernel()
836
{
837
int nchannels = CV_MAT_CN(frameType);
838
bool isFloat = CV_MAKETYPE(CV_32F,nchannels) == frameType;
839
String opts = format("-D CN=%d -D FL=%d -D NMIXTURES=%d%s", nchannels, isFloat, nmixtures, bShadowDetection ? " -D SHADOW_DETECT" : "");
840
kernel_apply.create("mog2_kernel", ocl::video::bgfg_mog2_oclsrc, opts);
841
}
842
843
#endif
844
845
void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate)
846
{
847
CV_INSTRUMENT_REGION();
848
849
#ifdef HAVE_OPENCL
850
if (opencl_ON)
851
{
852
CV_OCL_RUN(_fgmask.isUMat(), ocl_apply(_image, _fgmask, learningRate))
853
854
opencl_ON = false;
855
nframes = 0;
856
}
857
#endif
858
859
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
860
861
if( needToInitialize )
862
initialize(_image.size(), _image.type());
863
864
Mat image = _image.getMat();
865
_fgmask.create( image.size(), CV_8U );
866
Mat fgmask = _fgmask.getMat();
867
868
++nframes;
869
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
870
CV_Assert(learningRate >= 0);
871
872
parallel_for_(Range(0, image.rows),
873
MOG2Invoker(image, fgmask,
874
bgmodel.ptr<GMM>(),
875
(float*)(bgmodel.ptr() + sizeof(GMM)*nmixtures*image.rows*image.cols),
876
bgmodelUsedModes.ptr(), nmixtures, (float)learningRate,
877
(float)varThreshold,
878
backgroundRatio, varThresholdGen,
879
fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau,
880
bShadowDetection, nShadowDetection),
881
image.total()/(double)(1 << 16));
882
}
883
884
template <typename T, int CN>
885
void BackgroundSubtractorMOG2Impl::getBackgroundImage_intern(OutputArray backgroundImage) const
886
{
887
CV_INSTRUMENT_REGION();
888
889
Mat meanBackground(frameSize, frameType, Scalar::all(0));
890
int firstGaussianIdx = 0;
891
const GMM* gmm = bgmodel.ptr<GMM>();
892
const float* mean = reinterpret_cast<const float*>(gmm + frameSize.width*frameSize.height*nmixtures);
893
Vec<float,CN> meanVal(0.f);
894
for(int row=0; row<meanBackground.rows; row++)
895
{
896
for(int col=0; col<meanBackground.cols; col++)
897
{
898
int nmodes = bgmodelUsedModes.at<uchar>(row, col);
899
float totalWeight = 0.f;
900
for(int gaussianIdx = firstGaussianIdx; gaussianIdx < firstGaussianIdx + nmodes; gaussianIdx++)
901
{
902
GMM gaussian = gmm[gaussianIdx];
903
size_t meanPosition = gaussianIdx*CN;
904
for(int chn = 0; chn < CN; chn++)
905
{
906
meanVal(chn) += gaussian.weight * mean[meanPosition + chn];
907
}
908
totalWeight += gaussian.weight;
909
910
if(totalWeight > backgroundRatio)
911
break;
912
}
913
float invWeight = 0.f;
914
if (std::abs(totalWeight) > FLT_EPSILON) {
915
invWeight = 1.f/totalWeight;
916
}
917
918
meanBackground.at<Vec<T,CN> >(row, col) = Vec<T,CN>(meanVal * invWeight);
919
meanVal = 0.f;
920
921
firstGaussianIdx += nmixtures;
922
}
923
}
924
meanBackground.copyTo(backgroundImage);
925
}
926
927
void BackgroundSubtractorMOG2Impl::getBackgroundImage(OutputArray backgroundImage) const
928
{
929
CV_Assert(frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_32FC1 || frameType == CV_32FC3);
930
931
#ifdef HAVE_OPENCL
932
if (opencl_ON)
933
{
934
CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage))
935
936
opencl_ON = false;
937
}
938
#endif
939
940
switch(frameType)
941
{
942
case CV_8UC1:
943
getBackgroundImage_intern<uchar,1>(backgroundImage);
944
break;
945
case CV_8UC3:
946
getBackgroundImage_intern<uchar,3>(backgroundImage);
947
break;
948
case CV_32FC1:
949
getBackgroundImage_intern<float,1>(backgroundImage);
950
break;
951
case CV_32FC3:
952
getBackgroundImage_intern<float,3>(backgroundImage);
953
break;
954
}
955
}
956
957
Ptr<BackgroundSubtractorMOG2> createBackgroundSubtractorMOG2(int _history, double _varThreshold,
958
bool _bShadowDetection)
959
{
960
return makePtr<BackgroundSubtractorMOG2Impl>(_history, (float)_varThreshold, _bShadowDetection);
961
}
962
963
}
964
965
/* End of file. */
966
967