Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/video/src/bgfg_KNN.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
//#include <math.h>
43
44
#include "precomp.hpp"
45
#include "opencl_kernels_video.hpp"
46
47
namespace cv
48
{
49
50
/*!
51
The class implements the following algorithm:
52
"Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction"
53
Z.Zivkovic, F. van der Heijden
54
Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006
55
http://www.zoranz.net/Publications/zivkovicPRL2006.pdf
56
*/
57
58
// default parameters of gaussian background detection algorithm
59
static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2
60
static const int defaultNsamples = 7; // number of samples saved in memory
61
static const float defaultDist2Threshold = 20.0f*20.0f;//threshold on distance from the sample
62
63
// additional parameters
64
static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
65
static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation
66
67
class BackgroundSubtractorKNNImpl CV_FINAL : public BackgroundSubtractorKNN
68
{
69
public:
70
//! the default constructor
71
BackgroundSubtractorKNNImpl()
72
{
73
frameSize = Size(0,0);
74
frameType = 0;
75
nframes = 0;
76
history = defaultHistory2;
77
78
//set parameters
79
// N - the number of samples stored in memory per model
80
nN = defaultNsamples;
81
82
//kNN - k nearest neighbour - number on NN for detecting background - default K=[0.1*nN]
83
nkNN=MAX(1,cvRound(0.1*nN*3+0.40));
84
85
//Tb - Threshold Tb*kernelwidth
86
fTb = defaultDist2Threshold;
87
88
// Shadow detection
89
bShadowDetection = 1;//turn on
90
nShadowDetection = defaultnShadowDetection2;
91
fTau = defaultfTau;// Tau - shadow threshold
92
name_ = "BackgroundSubtractor.KNN";
93
nLongCounter = 0;
94
nMidCounter = 0;
95
nShortCounter = 0;
96
#ifdef HAVE_OPENCL
97
opencl_ON = true;
98
#endif
99
}
100
//! the full constructor that takes the length of the history,
101
// the number of gaussian mixtures, the background ratio parameter and the noise strength
102
BackgroundSubtractorKNNImpl(int _history, float _dist2Threshold, bool _bShadowDetection=true)
103
{
104
frameSize = Size(0,0);
105
frameType = 0;
106
107
nframes = 0;
108
history = _history > 0 ? _history : defaultHistory2;
109
110
//set parameters
111
// N - the number of samples stored in memory per model
112
nN = defaultNsamples;
113
//kNN - k nearest neighbour - number on NN for detcting background - default K=[0.1*nN]
114
nkNN=MAX(1,cvRound(0.1*nN*3+0.40));
115
116
//Tb - Threshold Tb*kernelwidth
117
fTb = _dist2Threshold>0? _dist2Threshold : defaultDist2Threshold;
118
119
bShadowDetection = _bShadowDetection;
120
nShadowDetection = defaultnShadowDetection2;
121
fTau = defaultfTau;
122
name_ = "BackgroundSubtractor.KNN";
123
nLongCounter = 0;
124
nMidCounter = 0;
125
nShortCounter = 0;
126
#ifdef HAVE_OPENCL
127
opencl_ON = true;
128
#endif
129
}
130
//! the destructor
131
~BackgroundSubtractorKNNImpl() CV_OVERRIDE {}
132
//! the update operator
133
void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;
134
135
//! computes a background image which are the mean of all background gaussians
136
virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;
137
138
//! re-initialization method
139
void initialize(Size _frameSize, int _frameType)
140
{
141
frameSize = _frameSize;
142
frameType = _frameType;
143
nframes = 0;
144
145
int nchannels = CV_MAT_CN(frameType);
146
CV_Assert( nchannels <= CV_CN_MAX );
147
148
// Reserve memory for the model
149
int size=frameSize.height*frameSize.width;
150
//Reset counters
151
nShortCounter = 0;
152
nMidCounter = 0;
153
nLongCounter = 0;
154
155
#ifdef HAVE_OPENCL
156
if (ocl::isOpenCLActivated() && opencl_ON)
157
{
158
create_ocl_apply_kernel();
159
160
kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_knn_oclsrc, format( "-D CN=%d -D NSAMPLES=%d", nchannels, nN));
161
162
if (kernel_apply.empty() || kernel_getBg.empty())
163
opencl_ON = false;
164
}
165
else opencl_ON = false;
166
167
if (opencl_ON)
168
{
169
u_flag.create(frameSize.height * nN * 3, frameSize.width, CV_8UC1);
170
u_flag.setTo(Scalar::all(0));
171
172
if (nchannels==3)
173
nchannels=4;
174
u_sample.create(frameSize.height * nN * 3, frameSize.width, CV_32FC(nchannels));
175
u_sample.setTo(Scalar::all(0));
176
177
u_aModelIndexShort.create(frameSize.height, frameSize.width, CV_8UC1);
178
u_aModelIndexShort.setTo(Scalar::all(0));
179
u_aModelIndexMid.create(frameSize.height, frameSize.width, CV_8UC1);
180
u_aModelIndexMid.setTo(Scalar::all(0));
181
u_aModelIndexLong.create(frameSize.height, frameSize.width, CV_8UC1);
182
u_aModelIndexLong.setTo(Scalar::all(0));
183
184
u_nNextShortUpdate.create(frameSize.height, frameSize.width, CV_8UC1);
185
u_nNextShortUpdate.setTo(Scalar::all(0));
186
u_nNextMidUpdate.create(frameSize.height, frameSize.width, CV_8UC1);
187
u_nNextMidUpdate.setTo(Scalar::all(0));
188
u_nNextLongUpdate.create(frameSize.height, frameSize.width, CV_8UC1);
189
u_nNextLongUpdate.setTo(Scalar::all(0));
190
}
191
else
192
#endif
193
{
194
// for each sample of 3 speed pixel models each pixel bg model we store ...
195
// values + flag (nchannels+1 values)
196
bgmodel.create( 1,(nN * 3) * (nchannels+1)* size,CV_8U);
197
bgmodel = Scalar::all(0);
198
199
//index through the three circular lists
200
aModelIndexShort.create(1,size,CV_8U);
201
aModelIndexMid.create(1,size,CV_8U);
202
aModelIndexLong.create(1,size,CV_8U);
203
//when to update next
204
nNextShortUpdate.create(1,size,CV_8U);
205
nNextMidUpdate.create(1,size,CV_8U);
206
nNextLongUpdate.create(1,size,CV_8U);
207
208
aModelIndexShort = Scalar::all(0);//random? //((m_nN)*rand())/(RAND_MAX+1);//0...m_nN-1
209
aModelIndexMid = Scalar::all(0);
210
aModelIndexLong = Scalar::all(0);
211
nNextShortUpdate = Scalar::all(0);
212
nNextMidUpdate = Scalar::all(0);
213
nNextLongUpdate = Scalar::all(0);
214
}
215
}
216
217
virtual int getHistory() const CV_OVERRIDE { return history; }
218
virtual void setHistory(int _nframes) CV_OVERRIDE { history = _nframes; }
219
220
virtual int getNSamples() const CV_OVERRIDE { return nN; }
221
virtual void setNSamples(int _nN) CV_OVERRIDE { nN = _nN; }//needs reinitialization!
222
223
virtual int getkNNSamples() const CV_OVERRIDE { return nkNN; }
224
virtual void setkNNSamples(int _nkNN) CV_OVERRIDE { nkNN = _nkNN; }
225
226
virtual double getDist2Threshold() const CV_OVERRIDE { return fTb; }
227
virtual void setDist2Threshold(double _dist2Threshold) CV_OVERRIDE { fTb = (float)_dist2Threshold; }
228
229
virtual bool getDetectShadows() const CV_OVERRIDE { return bShadowDetection; }
230
virtual void setDetectShadows(bool detectshadows) CV_OVERRIDE
231
{
232
if (bShadowDetection == detectshadows)
233
return;
234
bShadowDetection = detectshadows;
235
#ifdef HAVE_OPENCL
236
if (!kernel_apply.empty())
237
{
238
create_ocl_apply_kernel();
239
CV_Assert( !kernel_apply.empty() );
240
}
241
#endif
242
}
243
244
virtual int getShadowValue() const CV_OVERRIDE { return nShadowDetection; }
245
virtual void setShadowValue(int value) CV_OVERRIDE { nShadowDetection = (uchar)value; }
246
247
virtual double getShadowThreshold() const CV_OVERRIDE { return fTau; }
248
virtual void setShadowThreshold(double value) CV_OVERRIDE { fTau = (float)value; }
249
250
virtual void write(FileStorage& fs) const CV_OVERRIDE
251
{
252
writeFormat(fs);
253
fs << "name" << name_
254
<< "history" << history
255
<< "nsamples" << nN
256
<< "nKNN" << nkNN
257
<< "dist2Threshold" << fTb
258
<< "detectShadows" << (int)bShadowDetection
259
<< "shadowValue" << (int)nShadowDetection
260
<< "shadowThreshold" << fTau;
261
}
262
263
virtual void read(const FileNode& fn) CV_OVERRIDE
264
{
265
CV_Assert( (String)fn["name"] == name_ );
266
history = (int)fn["history"];
267
nN = (int)fn["nsamples"];
268
nkNN = (int)fn["nKNN"];
269
fTb = (float)fn["dist2Threshold"];
270
bShadowDetection = (int)fn["detectShadows"] != 0;
271
nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
272
fTau = (float)fn["shadowThreshold"];
273
}
274
275
protected:
276
Size frameSize;
277
int frameType;
278
int nframes;
279
/////////////////////////
280
//very important parameters - things you will change
281
////////////////////////
282
int history;
283
//alpha=1/history - speed of update - if the time interval you want to average over is T
284
//set alpha=1/history. It is also useful at start to make T slowly increase
285
//from 1 until the desired T
286
float fTb;
287
//Tb - threshold on the squared distance from the sample used to decide if it is well described
288
//by the background model or not. A typical value could be 2 sigma
289
//and that is Tb=2*2*10*10 =400; where we take typical pixel level sigma=10
290
291
/////////////////////////
292
//less important parameters - things you might change but be careful
293
////////////////////////
294
int nN;//totlal number of samples
295
int nkNN;//number on NN for detcting background - default K=[0.1*nN]
296
297
//shadow detection parameters
298
bool bShadowDetection;//default 1 - do shadow detection
299
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
300
float fTau;
301
// Tau - shadow threshold. The shadow is detected if the pixel is darker
302
//version of the background. Tau is a threshold on how much darker the shadow can be.
303
//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
304
//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
305
306
//model data
307
int nLongCounter;//circular counter
308
int nMidCounter;
309
int nShortCounter;
310
Mat bgmodel; // model data pixel values
311
Mat aModelIndexShort;// index into the models
312
Mat aModelIndexMid;
313
Mat aModelIndexLong;
314
Mat nNextShortUpdate;//random update points per model
315
Mat nNextMidUpdate;
316
Mat nNextLongUpdate;
317
318
#ifdef HAVE_OPENCL
319
mutable bool opencl_ON;
320
321
UMat u_flag;
322
UMat u_sample;
323
UMat u_aModelIndexShort;
324
UMat u_aModelIndexMid;
325
UMat u_aModelIndexLong;
326
UMat u_nNextShortUpdate;
327
UMat u_nNextMidUpdate;
328
UMat u_nNextLongUpdate;
329
330
mutable ocl::Kernel kernel_apply;
331
mutable ocl::Kernel kernel_getBg;
332
#endif
333
334
String name_;
335
336
#ifdef HAVE_OPENCL
337
bool ocl_getBackgroundImage(OutputArray backgroundImage) const;
338
bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);
339
void create_ocl_apply_kernel();
340
#endif
341
};
342
343
CV_INLINE void
344
_cvUpdatePixelBackgroundNP(int x_idx, const uchar* data, int nchannels, int m_nN,
345
uchar* m_aModel,
346
uchar* m_nNextLongUpdate,
347
uchar* m_nNextMidUpdate,
348
uchar* m_nNextShortUpdate,
349
uchar* m_aModelIndexLong,
350
uchar* m_aModelIndexMid,
351
uchar* m_aModelIndexShort,
352
int m_nLongCounter,
353
int m_nMidCounter,
354
int m_nShortCounter,
355
uchar include
356
)
357
{
358
// hold the offset
359
int ndata=1+nchannels;
360
long offsetLong = ndata * (m_aModelIndexLong[x_idx] + m_nN * 2);
361
long offsetMid = ndata * (m_aModelIndexMid[x_idx] + m_nN * 1);
362
long offsetShort = ndata * (m_aModelIndexShort[x_idx]);
363
364
// Long update?
365
if (m_nNextLongUpdate[x_idx] == m_nLongCounter)
366
{
367
// add the oldest pixel from Mid to the list of values (for each color)
368
memcpy(&m_aModel[offsetLong],&m_aModel[offsetMid],ndata*sizeof(unsigned char));
369
// increase the index
370
m_aModelIndexLong[x_idx] = (m_aModelIndexLong[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexLong[x_idx] + 1);
371
};
372
373
// Mid update?
374
if (m_nNextMidUpdate[x_idx] == m_nMidCounter)
375
{
376
// add this pixel to the list of values (for each color)
377
memcpy(&m_aModel[offsetMid],&m_aModel[offsetShort],ndata*sizeof(unsigned char));
378
// increase the index
379
m_aModelIndexMid[x_idx] = (m_aModelIndexMid[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexMid[x_idx] + 1);
380
};
381
382
// Short update?
383
if (m_nNextShortUpdate[x_idx] == m_nShortCounter)
384
{
385
// add this pixel to the list of values (for each color)
386
memcpy(&m_aModel[offsetShort],data,nchannels*sizeof(unsigned char));
387
//set the include flag
388
m_aModel[offsetShort+nchannels]=include;
389
// increase the index
390
m_aModelIndexShort[x_idx] = (m_aModelIndexShort[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexShort[x_idx] + 1);
391
};
392
}
393
394
CV_INLINE int
395
_cvCheckPixelBackgroundNP(const uchar* data, int nchannels,
396
int m_nN,
397
uchar* m_aModel,
398
float m_fTb,
399
int m_nkNN,
400
float tau,
401
bool m_bShadowDetection,
402
uchar& include)
403
{
404
int Pbf = 0; // the total probability that this pixel is background
405
int Pb = 0; //background model probability
406
float dData[CV_CN_MAX];
407
408
//uchar& include=data[nchannels];
409
include=0;//do we include this pixel into background model?
410
411
int ndata=nchannels+1;
412
// now increase the probability for each pixel
413
for (int n = 0; n < m_nN*3; n++)
414
{
415
uchar* mean_m = &m_aModel[n*ndata];
416
417
//calculate difference and distance
418
float dist2;
419
420
if( nchannels == 3 )
421
{
422
dData[0] = (float)mean_m[0] - data[0];
423
dData[1] = (float)mean_m[1] - data[1];
424
dData[2] = (float)mean_m[2] - data[2];
425
dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
426
}
427
else
428
{
429
dist2 = 0.f;
430
for( int c = 0; c < nchannels; c++ )
431
{
432
dData[c] = (float)mean_m[c] - data[c];
433
dist2 += dData[c]*dData[c];
434
}
435
}
436
437
if (dist2<m_fTb)
438
{
439
Pbf++;//all
440
//background only
441
//if(m_aModel[subPosPixel + nchannels])//indicator
442
if(mean_m[nchannels])//indicator
443
{
444
Pb++;
445
if (Pb >= m_nkNN)//Tb
446
{
447
include=1;//include
448
return 1;//background ->exit
449
};
450
}
451
};
452
};
453
454
//include?
455
if (Pbf>=m_nkNN)//m_nTbf)
456
{
457
include=1;
458
}
459
460
int Ps = 0; // the total probability that this pixel is background shadow
461
// Detected as moving object, perform shadow detection
462
if (m_bShadowDetection)
463
{
464
for (int n = 0; n < m_nN*3; n++)
465
{
466
//long subPosPixel = posPixel + n*ndata;
467
uchar* mean_m = &m_aModel[n*ndata];
468
469
if(mean_m[nchannels])//check only background
470
{
471
float numerator = 0.0f;
472
float denominator = 0.0f;
473
for( int c = 0; c < nchannels; c++ )
474
{
475
numerator += (float)data[c] * mean_m[c];
476
denominator += (float)mean_m[c] * mean_m[c];
477
}
478
479
// no division by zero allowed
480
if( denominator == 0 )
481
return 0;
482
483
// if tau < a < 1 then also check the color distortion
484
if( numerator <= denominator && numerator >= tau*denominator )
485
{
486
float a = numerator / denominator;
487
float dist2a = 0.0f;
488
489
for( int c = 0; c < nchannels; c++ )
490
{
491
float dD= a*mean_m[c] - data[c];
492
dist2a += dD*dD;
493
}
494
495
if (dist2a<m_fTb*a*a)
496
{
497
Ps++;
498
if (Ps >= m_nkNN)//shadow
499
return 2;
500
};
501
};
502
};
503
};
504
}
505
return 0;
506
}
507
508
class KNNInvoker : public ParallelLoopBody
509
{
510
public:
511
KNNInvoker(const Mat& _src, Mat& _dst,
512
uchar* _bgmodel,
513
uchar* _nNextLongUpdate,
514
uchar* _nNextMidUpdate,
515
uchar* _nNextShortUpdate,
516
uchar* _aModelIndexLong,
517
uchar* _aModelIndexMid,
518
uchar* _aModelIndexShort,
519
int _nLongCounter,
520
int _nMidCounter,
521
int _nShortCounter,
522
int _nN,
523
float _fTb,
524
int _nkNN,
525
float _fTau,
526
bool _bShadowDetection,
527
uchar _nShadowDetection)
528
{
529
src = &_src;
530
dst = &_dst;
531
m_aModel0 = _bgmodel;
532
m_nNextLongUpdate0 = _nNextLongUpdate;
533
m_nNextMidUpdate0 = _nNextMidUpdate;
534
m_nNextShortUpdate0 = _nNextShortUpdate;
535
m_aModelIndexLong0 = _aModelIndexLong;
536
m_aModelIndexMid0 = _aModelIndexMid;
537
m_aModelIndexShort0 = _aModelIndexShort;
538
m_nLongCounter = _nLongCounter;
539
m_nMidCounter = _nMidCounter;
540
m_nShortCounter = _nShortCounter;
541
m_nN = _nN;
542
m_fTb = _fTb;
543
m_fTau = _fTau;
544
m_nkNN = _nkNN;
545
m_bShadowDetection = _bShadowDetection;
546
m_nShadowDetection = _nShadowDetection;
547
}
548
549
void operator()(const Range& range) const CV_OVERRIDE
550
{
551
int y0 = range.start, y1 = range.end;
552
int ncols = src->cols, nchannels = src->channels();
553
int ndata=nchannels+1;
554
555
for ( int y = y0; y < y1; y++ )
556
{
557
const uchar* data = src->ptr(y);
558
uchar* m_aModel = m_aModel0 + ncols*m_nN*3*ndata*y;
559
uchar* m_nNextLongUpdate = m_nNextLongUpdate0 + ncols*y;
560
uchar* m_nNextMidUpdate = m_nNextMidUpdate0 + ncols*y;
561
uchar* m_nNextShortUpdate = m_nNextShortUpdate0 + ncols*y;
562
uchar* m_aModelIndexLong = m_aModelIndexLong0 + ncols*y;
563
uchar* m_aModelIndexMid = m_aModelIndexMid0 + ncols*y;
564
uchar* m_aModelIndexShort = m_aModelIndexShort0 + ncols*y;
565
uchar* mask = dst->ptr(y);
566
567
for ( int x = 0; x < ncols; x++ )
568
{
569
570
//update model+ background subtract
571
uchar include=0;
572
int result= _cvCheckPixelBackgroundNP(data, nchannels,
573
m_nN, m_aModel, m_fTb,m_nkNN, m_fTau,m_bShadowDetection,include);
574
575
_cvUpdatePixelBackgroundNP(x,data,nchannels,
576
m_nN, m_aModel,
577
m_nNextLongUpdate,
578
m_nNextMidUpdate,
579
m_nNextShortUpdate,
580
m_aModelIndexLong,
581
m_aModelIndexMid,
582
m_aModelIndexShort,
583
m_nLongCounter,
584
m_nMidCounter,
585
m_nShortCounter,
586
include
587
);
588
switch (result)
589
{
590
case 0:
591
//foreground
592
mask[x] = 255;
593
break;
594
case 1:
595
//background
596
mask[x] = 0;
597
break;
598
case 2:
599
//shadow
600
mask[x] = m_nShadowDetection;
601
break;
602
}
603
data += nchannels;
604
m_aModel += m_nN*3*ndata;
605
}
606
}
607
}
608
609
const Mat* src;
610
Mat* dst;
611
uchar* m_aModel0;
612
uchar* m_nNextLongUpdate0;
613
uchar* m_nNextMidUpdate0;
614
uchar* m_nNextShortUpdate0;
615
uchar* m_aModelIndexLong0;
616
uchar* m_aModelIndexMid0;
617
uchar* m_aModelIndexShort0;
618
int m_nLongCounter;
619
int m_nMidCounter;
620
int m_nShortCounter;
621
int m_nN;
622
float m_fTb;
623
float m_fTau;
624
int m_nkNN;
625
bool m_bShadowDetection;
626
uchar m_nShadowDetection;
627
};
628
629
#ifdef HAVE_OPENCL
630
bool BackgroundSubtractorKNNImpl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate)
631
{
632
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
633
634
if( needToInitialize )
635
initialize(_image.size(), _image.type());
636
637
++nframes;
638
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
639
CV_Assert(learningRate >= 0);
640
641
_fgmask.create(_image.size(), CV_8U);
642
UMat fgmask = _fgmask.getUMat();
643
644
UMat frame = _image.getUMat();
645
646
//recalculate update rates - in case alpha is changed
647
// calculate update parameters (using alpha)
648
int Kshort,Kmid,Klong;
649
//approximate exponential learning curve
650
Kshort=(int)(log(0.7)/log(1-learningRate))+1;//Kshort
651
Kmid=(int)(log(0.4)/log(1-learningRate))-Kshort+1;//Kmid
652
Klong=(int)(log(0.1)/log(1-learningRate))-Kshort-Kmid+1;//Klong
653
654
//refresh rates
655
int nShortUpdate = (Kshort/nN)+1;
656
int nMidUpdate = (Kmid/nN)+1;
657
int nLongUpdate = (Klong/nN)+1;
658
659
int idxArg = 0;
660
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame));
661
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextLongUpdate));
662
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextMidUpdate));
663
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextShortUpdate));
664
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexLong));
665
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexMid));
666
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexShort));
667
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_flag));
668
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_sample));
669
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask));
670
671
idxArg = kernel_apply.set(idxArg, nLongCounter);
672
idxArg = kernel_apply.set(idxArg, nMidCounter);
673
idxArg = kernel_apply.set(idxArg, nShortCounter);
674
idxArg = kernel_apply.set(idxArg, fTb);
675
idxArg = kernel_apply.set(idxArg, nkNN);
676
idxArg = kernel_apply.set(idxArg, fTau);
677
if (bShadowDetection)
678
kernel_apply.set(idxArg, nShadowDetection);
679
680
size_t globalsize[2] = {(size_t)frame.cols, (size_t)frame.rows};
681
if(!kernel_apply.run(2, globalsize, NULL, true))
682
return false;
683
684
nShortCounter++;//0,1,...,nShortUpdate-1
685
nMidCounter++;
686
nLongCounter++;
687
if (nShortCounter >= nShortUpdate)
688
{
689
nShortCounter = 0;
690
randu(u_nNextShortUpdate, Scalar::all(0), Scalar::all(nShortUpdate));
691
}
692
if (nMidCounter >= nMidUpdate)
693
{
694
nMidCounter = 0;
695
randu(u_nNextMidUpdate, Scalar::all(0), Scalar::all(nMidUpdate));
696
}
697
if (nLongCounter >= nLongUpdate)
698
{
699
nLongCounter = 0;
700
randu(u_nNextLongUpdate, Scalar::all(0), Scalar::all(nLongUpdate));
701
}
702
return true;
703
}
704
705
bool BackgroundSubtractorKNNImpl::ocl_getBackgroundImage(OutputArray _backgroundImage) const
706
{
707
_backgroundImage.create(frameSize, frameType);
708
UMat dst = _backgroundImage.getUMat();
709
710
int idxArg = 0;
711
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_flag));
712
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_sample));
713
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst));
714
715
size_t globalsize[2] = {(size_t)dst.cols, (size_t)dst.rows};
716
717
return kernel_getBg.run(2, globalsize, NULL, false);
718
}
719
720
void BackgroundSubtractorKNNImpl::create_ocl_apply_kernel()
721
{
722
int nchannels = CV_MAT_CN(frameType);
723
String opts = format("-D CN=%d -D NSAMPLES=%d%s", nchannels, nN, bShadowDetection ? " -D SHADOW_DETECT" : "");
724
kernel_apply.create("knn_kernel", ocl::video::bgfg_knn_oclsrc, opts);
725
}
726
727
#endif
728
729
void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate)
730
{
731
CV_INSTRUMENT_REGION();
732
733
#ifdef HAVE_OPENCL
734
if (opencl_ON)
735
{
736
#ifndef __APPLE__
737
CV_OCL_RUN(_fgmask.isUMat() && OCL_PERFORMANCE_CHECK(!ocl::Device::getDefault().isIntel() || _image.channels() == 1),
738
ocl_apply(_image, _fgmask, learningRate))
739
#else
740
CV_OCL_RUN(_fgmask.isUMat() && OCL_PERFORMANCE_CHECK(!ocl::Device::getDefault().isIntel()),
741
ocl_apply(_image, _fgmask, learningRate))
742
#endif
743
744
opencl_ON = false;
745
nframes = 0;
746
}
747
#endif
748
749
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
750
751
if( needToInitialize )
752
initialize(_image.size(), _image.type());
753
754
Mat image = _image.getMat();
755
_fgmask.create( image.size(), CV_8U );
756
Mat fgmask = _fgmask.getMat();
757
758
++nframes;
759
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
760
CV_Assert(learningRate >= 0);
761
762
//recalculate update rates - in case alpha is changed
763
// calculate update parameters (using alpha)
764
int Kshort,Kmid,Klong;
765
//approximate exponential learning curve
766
Kshort=(int)(log(0.7)/log(1-learningRate))+1;//Kshort
767
Kmid=(int)(log(0.4)/log(1-learningRate))-Kshort+1;//Kmid
768
Klong=(int)(log(0.1)/log(1-learningRate))-Kshort-Kmid+1;//Klong
769
770
//refresh rates
771
int nShortUpdate = (Kshort/nN)+1;
772
int nMidUpdate = (Kmid/nN)+1;
773
int nLongUpdate = (Klong/nN)+1;
774
775
parallel_for_(Range(0, image.rows),
776
KNNInvoker(image, fgmask,
777
bgmodel.ptr(),
778
nNextLongUpdate.ptr(),
779
nNextMidUpdate.ptr(),
780
nNextShortUpdate.ptr(),
781
aModelIndexLong.ptr(),
782
aModelIndexMid.ptr(),
783
aModelIndexShort.ptr(),
784
nLongCounter,
785
nMidCounter,
786
nShortCounter,
787
nN,
788
fTb,
789
nkNN,
790
fTau,
791
bShadowDetection,
792
nShadowDetection),
793
image.total()/(double)(1 << 16));
794
795
nShortCounter++;//0,1,...,nShortUpdate-1
796
nMidCounter++;
797
nLongCounter++;
798
if (nShortCounter >= nShortUpdate)
799
{
800
nShortCounter = 0;
801
randu(nNextShortUpdate, Scalar::all(0), Scalar::all(nShortUpdate));
802
}
803
if (nMidCounter >= nMidUpdate)
804
{
805
nMidCounter = 0;
806
randu(nNextMidUpdate, Scalar::all(0), Scalar::all(nMidUpdate));
807
}
808
if (nLongCounter >= nLongUpdate)
809
{
810
nLongCounter = 0;
811
randu(nNextLongUpdate, Scalar::all(0), Scalar::all(nLongUpdate));
812
}
813
}
814
815
void BackgroundSubtractorKNNImpl::getBackgroundImage(OutputArray backgroundImage) const
816
{
817
CV_INSTRUMENT_REGION();
818
819
#ifdef HAVE_OPENCL
820
if (opencl_ON)
821
{
822
CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage))
823
824
opencl_ON = false;
825
}
826
#endif
827
828
int nchannels = CV_MAT_CN(frameType);
829
//CV_Assert( nchannels == 3 );
830
Mat meanBackground(frameSize, CV_8UC3, Scalar::all(0));
831
832
int ndata=nchannels+1;
833
int modelstep=(ndata * nN * 3);
834
835
const uchar* pbgmodel=bgmodel.ptr(0);
836
for(int row=0; row<meanBackground.rows; row++)
837
{
838
for(int col=0; col<meanBackground.cols; col++)
839
{
840
for (int n = 0; n < nN*3; n++)
841
{
842
const uchar* mean_m = &pbgmodel[n*ndata];
843
if (mean_m[nchannels])
844
{
845
meanBackground.at<Vec3b>(row, col) = Vec3b(mean_m);
846
break;
847
}
848
}
849
pbgmodel=pbgmodel+modelstep;
850
}
851
}
852
853
switch(CV_MAT_CN(frameType))
854
{
855
case 1:
856
{
857
std::vector<Mat> channels;
858
split(meanBackground, channels);
859
channels[0].copyTo(backgroundImage);
860
break;
861
}
862
case 3:
863
{
864
meanBackground.copyTo(backgroundImage);
865
break;
866
}
867
default:
868
CV_Error(Error::StsUnsupportedFormat, "");
869
}
870
}
871
872
873
Ptr<BackgroundSubtractorKNN> createBackgroundSubtractorKNN(int _history, double _threshold2,
874
bool _bShadowDetection)
875
{
876
return makePtr<BackgroundSubtractorKNNImpl>(_history, (float)_threshold2, _bShadowDetection);
877
}
878
879
}
880
881
/* End of file. */
882
883