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 // Intel License Agreement
11 //
12 // Copyright (C) 2000, Intel Corporation, all rights reserved.
13 // Third party copyrights are property of their respective owners.
14 //
15 // Redistribution and use in source and binary forms, with or without modification,
16 // are permitted provided that the following conditions are met:
17 //
18 // * Redistribution's of source code must retain the above copyright notice,
19 // this list of conditions and the following disclaimer.
20 //
21 // * Redistribution's in binary form must reproduce the above copyright notice,
22 // this list of conditions and the following disclaimer in the documentation
23 // and/or other materials provided with the distribution.
24 //
25 // * The name of Intel Corporation may not be used to endorse or promote products
26 // derived from this software without specific prior written permission.
27 //
28 // This software is provided by the copyright holders and contributors "as is" and
29 // any express or implied warranties, including, but not limited to, the implied
30 // warranties of merchantability and fitness for a particular purpose are disclaimed.
31 // In no event shall the Intel Corporation or contributors be liable for any direct,
32 // indirect, incidental, special, exemplary, or consequential damages
33 // (including, but not limited to, procurement of substitute goods or services;
34 // loss of use, data, or profits; or business interruption) however caused
35 // and on any theory of liability, whether in contract, strict liability,
36 // or tort (including negligence or otherwise) arising in any way out of
37 // the use of this software, even if advised of the possibility of such damage.
38 //
39 //M*/
40
41
42 // This file implements the foreground/background pixel
43 // discrimination algorithm described in
44 //
45 // Foreground Object Detection from Videos Containing Complex Background
46 // Li, Huan, Gu, Tian 2003 9p
47 // http://muq.org/~cynbe/bib/foreground-object-detection-from-videos-containing-complex-background.pdf
48
49
50 #include "_cvaux.h"
51
52 #include <math.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 //#include <algorithm>
56
_cv_max_element(double * start,double * end)57 static double* _cv_max_element( double* start, double* end )
58 {
59 double* p = start++;
60
61 for( ; start != end; ++start) {
62
63 if (*p < *start) p = start;
64 }
65
66 return p;
67 }
68
69 static void CV_CDECL icvReleaseFGDStatModel( CvFGDStatModel** model );
70 static int CV_CDECL icvUpdateFGDStatModel( IplImage* curr_frame,
71 CvFGDStatModel* model );
72
73 // Function cvCreateFGDStatModel initializes foreground detection process
74 // parameters:
75 // first_frame - frame from video sequence
76 // parameters - (optional) if NULL default parameters of the algorithm will be used
77 // p_model - pointer to CvFGDStatModel structure
78 CV_IMPL CvBGStatModel*
cvCreateFGDStatModel(IplImage * first_frame,CvFGDStatModelParams * parameters)79 cvCreateFGDStatModel( IplImage* first_frame, CvFGDStatModelParams* parameters )
80 {
81 CvFGDStatModel* p_model = 0;
82
83 CV_FUNCNAME( "cvCreateFGDStatModel" );
84
85 __BEGIN__;
86
87 int i, j, k, pixel_count, buf_size;
88 CvFGDStatModelParams params;
89
90 if( !CV_IS_IMAGE(first_frame) )
91 CV_ERROR( CV_StsBadArg, "Invalid or NULL first_frame parameter" );
92
93 if (first_frame->nChannels != 3)
94 CV_ERROR( CV_StsBadArg, "first_frame must have 3 color channels" );
95
96 // Initialize parameters:
97 if( parameters == NULL )
98 {
99 params.Lc = CV_BGFG_FGD_LC;
100 params.N1c = CV_BGFG_FGD_N1C;
101 params.N2c = CV_BGFG_FGD_N2C;
102
103 params.Lcc = CV_BGFG_FGD_LCC;
104 params.N1cc = CV_BGFG_FGD_N1CC;
105 params.N2cc = CV_BGFG_FGD_N2CC;
106
107 params.delta = CV_BGFG_FGD_DELTA;
108
109 params.alpha1 = CV_BGFG_FGD_ALPHA_1;
110 params.alpha2 = CV_BGFG_FGD_ALPHA_2;
111 params.alpha3 = CV_BGFG_FGD_ALPHA_3;
112
113 params.T = CV_BGFG_FGD_T;
114 params.minArea = CV_BGFG_FGD_MINAREA;
115
116 params.is_obj_without_holes = 1;
117 params.perform_morphing = 1;
118 }
119 else
120 {
121 params = *parameters;
122 }
123
124 CV_CALL( p_model = (CvFGDStatModel*)cvAlloc( sizeof(*p_model) ));
125 memset( p_model, 0, sizeof(*p_model) );
126 p_model->type = CV_BG_MODEL_FGD;
127 p_model->release = (CvReleaseBGStatModel)icvReleaseFGDStatModel;
128 p_model->update = (CvUpdateBGStatModel)icvUpdateFGDStatModel;;
129 p_model->params = params;
130
131 // Initialize storage pools:
132 pixel_count = first_frame->width * first_frame->height;
133
134 buf_size = pixel_count*sizeof(p_model->pixel_stat[0]);
135 CV_CALL( p_model->pixel_stat = (CvBGPixelStat*)cvAlloc(buf_size) );
136 memset( p_model->pixel_stat, 0, buf_size );
137
138 buf_size = pixel_count*params.N2c*sizeof(p_model->pixel_stat[0].ctable[0]);
139 CV_CALL( p_model->pixel_stat[0].ctable = (CvBGPixelCStatTable*)cvAlloc(buf_size) );
140 memset( p_model->pixel_stat[0].ctable, 0, buf_size );
141
142 buf_size = pixel_count*params.N2cc*sizeof(p_model->pixel_stat[0].cctable[0]);
143 CV_CALL( p_model->pixel_stat[0].cctable = (CvBGPixelCCStatTable*)cvAlloc(buf_size) );
144 memset( p_model->pixel_stat[0].cctable, 0, buf_size );
145
146 for( i = 0, k = 0; i < first_frame->height; i++ ) {
147 for( j = 0; j < first_frame->width; j++, k++ )
148 {
149 p_model->pixel_stat[k].ctable = p_model->pixel_stat[0].ctable + k*params.N2c;
150 p_model->pixel_stat[k].cctable = p_model->pixel_stat[0].cctable + k*params.N2cc;
151 }
152 }
153
154 // Init temporary images:
155 CV_CALL( p_model->Ftd = cvCreateImage(cvSize(first_frame->width, first_frame->height), IPL_DEPTH_8U, 1));
156 CV_CALL( p_model->Fbd = cvCreateImage(cvSize(first_frame->width, first_frame->height), IPL_DEPTH_8U, 1));
157 CV_CALL( p_model->foreground = cvCreateImage(cvSize(first_frame->width, first_frame->height), IPL_DEPTH_8U, 1));
158
159 CV_CALL( p_model->background = cvCloneImage(first_frame));
160 CV_CALL( p_model->prev_frame = cvCloneImage(first_frame));
161 CV_CALL( p_model->storage = cvCreateMemStorage());
162
163 __END__;
164
165 if( cvGetErrStatus() < 0 )
166 {
167 CvBGStatModel* base_ptr = (CvBGStatModel*)p_model;
168
169 if( p_model && p_model->release )
170 p_model->release( &base_ptr );
171 else
172 cvFree( &p_model );
173 p_model = 0;
174 }
175
176 return (CvBGStatModel*)p_model;
177 }
178
179
180 static void CV_CDECL
icvReleaseFGDStatModel(CvFGDStatModel ** _model)181 icvReleaseFGDStatModel( CvFGDStatModel** _model )
182 {
183 CV_FUNCNAME( "icvReleaseFGDStatModel" );
184
185 __BEGIN__;
186
187 if( !_model )
188 CV_ERROR( CV_StsNullPtr, "" );
189
190 if( *_model )
191 {
192 CvFGDStatModel* model = *_model;
193 if( model->pixel_stat )
194 {
195 cvFree( &model->pixel_stat[0].ctable );
196 cvFree( &model->pixel_stat[0].cctable );
197 cvFree( &model->pixel_stat );
198 }
199
200 cvReleaseImage( &model->Ftd );
201 cvReleaseImage( &model->Fbd );
202 cvReleaseImage( &model->foreground );
203 cvReleaseImage( &model->background );
204 cvReleaseImage( &model->prev_frame );
205 cvReleaseMemStorage(&model->storage);
206
207 cvFree( _model );
208 }
209
210 __END__;
211 }
212
213 // Function cvChangeDetection performs change detection for Foreground detection algorithm
214 // parameters:
215 // prev_frame -
216 // curr_frame -
217 // change_mask -
218 CV_IMPL int
cvChangeDetection(IplImage * prev_frame,IplImage * curr_frame,IplImage * change_mask)219 cvChangeDetection( IplImage* prev_frame,
220 IplImage* curr_frame,
221 IplImage* change_mask )
222 {
223 int i, j, b, x, y, thres;
224 const int PIXELRANGE=256;
225
226 if( !prev_frame
227 || !curr_frame
228 || !change_mask
229 || prev_frame->nChannels != 3
230 || curr_frame->nChannels != 3
231 || change_mask->nChannels != 1
232 || prev_frame->depth != IPL_DEPTH_8U
233 || curr_frame->depth != IPL_DEPTH_8U
234 || change_mask->depth != IPL_DEPTH_8U
235 || prev_frame->width != curr_frame->width
236 || prev_frame->height != curr_frame->height
237 || prev_frame->width != change_mask->width
238 || prev_frame->height != change_mask->height
239 ){
240 return 0;
241 }
242
243 cvZero ( change_mask );
244
245 // All operations per colour
246 for (b=0 ; b<prev_frame->nChannels ; b++) {
247
248 // Create histogram:
249
250 long HISTOGRAM[PIXELRANGE];
251 for (i=0 ; i<PIXELRANGE; i++) HISTOGRAM[i]=0;
252
253 for (y=0 ; y<curr_frame->height ; y++)
254 {
255 uchar* rowStart1 = (uchar*)curr_frame->imageData + y * curr_frame->widthStep + b;
256 uchar* rowStart2 = (uchar*)prev_frame->imageData + y * prev_frame->widthStep + b;
257 for (x=0 ; x<curr_frame->width ; x++, rowStart1+=curr_frame->nChannels, rowStart2+=prev_frame->nChannels) {
258 int diff = abs( int(*rowStart1) - int(*rowStart2) );
259 HISTOGRAM[diff]++;
260 }
261 }
262
263 double relativeVariance[PIXELRANGE];
264 for (i=0 ; i<PIXELRANGE; i++) relativeVariance[i]=0;
265
266 for (thres=PIXELRANGE-2; thres>=0 ; thres--)
267 {
268 // fprintf(stderr, "Iter %d\n", thres);
269 double sum=0;
270 double sqsum=0;
271 int count=0;
272 // fprintf(stderr, "Iter %d entering loop\n", thres);
273 for (j=thres ; j<PIXELRANGE ; j++) {
274 sum += double(j)*double(HISTOGRAM[j]);
275 sqsum += double(j*j)*double(HISTOGRAM[j]);
276 count += HISTOGRAM[j];
277 }
278 count = count == 0 ? 1 : count;
279 // fprintf(stderr, "Iter %d finishing loop\n", thres);
280 double my = sum / count;
281 double sigma = sqrt( sqsum/count - my*my);
282 // fprintf(stderr, "Iter %d sum=%g sqsum=%g count=%d sigma = %g\n", thres, sum, sqsum, count, sigma);
283 // fprintf(stderr, "Writing to %x\n", &(relativeVariance[thres]));
284 relativeVariance[thres] = sigma;
285 // fprintf(stderr, "Iter %d finished\n", thres);
286 }
287
288 // Find maximum:
289 uchar bestThres = 0;
290
291 double* pBestThres = _cv_max_element(relativeVariance, relativeVariance+PIXELRANGE);
292 bestThres = (uchar)(*pBestThres); if (bestThres <10) bestThres=10;
293
294 for (y=0 ; y<prev_frame->height ; y++)
295 {
296 uchar* rowStart1 = (uchar*)(curr_frame->imageData) + y * curr_frame->widthStep + b;
297 uchar* rowStart2 = (uchar*)(prev_frame->imageData) + y * prev_frame->widthStep + b;
298 uchar* rowStart3 = (uchar*)(change_mask->imageData) + y * change_mask->widthStep;
299 for (x = 0; x < curr_frame->width; x++, rowStart1+=curr_frame->nChannels,
300 rowStart2+=prev_frame->nChannels, rowStart3+=change_mask->nChannels) {
301 // OR between different color channels
302 int diff = abs( int(*rowStart1) - int(*rowStart2) );
303 if ( diff > bestThres)
304 *rowStart3 |=255;
305 }
306 }
307 }
308
309 return 1;
310 }
311
312
313 #define MIN_PV 1E-10
314
315
316 #define V_C(k,l) ctable[k].v[l]
317 #define PV_C(k) ctable[k].Pv
318 #define PVB_C(k) ctable[k].Pvb
319 #define V_CC(k,l) cctable[k].v[l]
320 #define PV_CC(k) cctable[k].Pv
321 #define PVB_CC(k) cctable[k].Pvb
322
323
324 // Function cvUpdateFGDStatModel updates statistical model and returns number of foreground regions
325 // parameters:
326 // curr_frame - current frame from video sequence
327 // p_model - pointer to CvFGDStatModel structure
328 static int CV_CDECL
icvUpdateFGDStatModel(IplImage * curr_frame,CvFGDStatModel * model)329 icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model )
330 {
331 int mask_step = model->Ftd->widthStep;
332 CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL;
333 IplImage* prev_frame = model->prev_frame;
334 int region_count = 0;
335 int FG_pixels_count = 0;
336 int deltaC = cvRound(model->params.delta * 256 / model->params.Lc);
337 int deltaCC = cvRound(model->params.delta * 256 / model->params.Lcc);
338 int i, j, k, l;
339
340 //clear storages
341 cvClearMemStorage(model->storage);
342 cvZero(model->foreground);
343
344 // From foreground pixel candidates using image differencing
345 // with adaptive thresholding. The algorithm is from:
346 //
347 // Thresholding for Change Detection
348 // Paul L. Rosin 1998 6p
349 // http://www.cis.temple.edu/~latecki/Courses/CIS750-03/Papers/thresh-iccv.pdf
350 //
351 cvChangeDetection( prev_frame, curr_frame, model->Ftd );
352 cvChangeDetection( model->background, curr_frame, model->Fbd );
353
354 for( i = 0; i < model->Ftd->height; i++ )
355 {
356 for( j = 0; j < model->Ftd->width; j++ )
357 {
358 if( ((uchar*)model->Fbd->imageData)[i*mask_step+j] || ((uchar*)model->Ftd->imageData)[i*mask_step+j] )
359 {
360 float Pb = 0;
361 float Pv = 0;
362 float Pvb = 0;
363
364 CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
365
366 CvBGPixelCStatTable* ctable = stat->ctable;
367 CvBGPixelCCStatTable* cctable = stat->cctable;
368
369 uchar* curr_data = (uchar*)(curr_frame->imageData) + i*curr_frame->widthStep + j*3;
370 uchar* prev_data = (uchar*)(prev_frame->imageData) + i*prev_frame->widthStep + j*3;
371
372 int val = 0;
373
374 // Is it a motion pixel?
375 if( ((uchar*)model->Ftd->imageData)[i*mask_step+j] )
376 {
377 if( !stat->is_trained_dyn_model ) {
378
379 val = 1;
380
381 } else {
382
383 // Compare with stored CCt vectors:
384 for( k = 0; PV_CC(k) > model->params.alpha2 && k < model->params.N1cc; k++ )
385 {
386 if ( abs( V_CC(k,0) - prev_data[0]) <= deltaCC &&
387 abs( V_CC(k,1) - prev_data[1]) <= deltaCC &&
388 abs( V_CC(k,2) - prev_data[2]) <= deltaCC &&
389 abs( V_CC(k,3) - curr_data[0]) <= deltaCC &&
390 abs( V_CC(k,4) - curr_data[1]) <= deltaCC &&
391 abs( V_CC(k,5) - curr_data[2]) <= deltaCC)
392 {
393 Pv += PV_CC(k);
394 Pvb += PVB_CC(k);
395 }
396 }
397 Pb = stat->Pbcc;
398 if( 2 * Pvb * Pb <= Pv ) val = 1;
399 }
400 }
401 else if( stat->is_trained_st_model )
402 {
403 // Compare with stored Ct vectors:
404 for( k = 0; PV_C(k) > model->params.alpha2 && k < model->params.N1c; k++ )
405 {
406 if ( abs( V_C(k,0) - curr_data[0]) <= deltaC &&
407 abs( V_C(k,1) - curr_data[1]) <= deltaC &&
408 abs( V_C(k,2) - curr_data[2]) <= deltaC )
409 {
410 Pv += PV_C(k);
411 Pvb += PVB_C(k);
412 }
413 }
414 Pb = stat->Pbc;
415 if( 2 * Pvb * Pb <= Pv ) val = 1;
416 }
417
418 // Update foreground:
419 ((uchar*)model->foreground->imageData)[i*mask_step+j] = (uchar)(val*255);
420 FG_pixels_count += val;
421
422 } // end if( change detection...
423 } // for j...
424 } // for i...
425 //end BG/FG classification
426
427 // Foreground segmentation.
428 // Smooth foreground map:
429 if( model->params.perform_morphing ){
430 cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_OPEN, model->params.perform_morphing );
431 cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_CLOSE, model->params.perform_morphing );
432 }
433
434
435 if( model->params.minArea > 0 || model->params.is_obj_without_holes ){
436
437 // Discard under-size foreground regions:
438 //
439 cvFindContours( model->foreground, model->storage, &first_seq, sizeof(CvContour), CV_RETR_LIST );
440 for( seq = first_seq; seq; seq = seq->h_next )
441 {
442 CvContour* cnt = (CvContour*)seq;
443 if( cnt->rect.width * cnt->rect.height < model->params.minArea ||
444 (model->params.is_obj_without_holes && CV_IS_SEQ_HOLE(seq)) )
445 {
446 // Delete under-size contour:
447 prev_seq = seq->h_prev;
448 if( prev_seq )
449 {
450 prev_seq->h_next = seq->h_next;
451 if( seq->h_next ) seq->h_next->h_prev = prev_seq;
452 }
453 else
454 {
455 first_seq = seq->h_next;
456 if( seq->h_next ) seq->h_next->h_prev = NULL;
457 }
458 }
459 else
460 {
461 region_count++;
462 }
463 }
464 model->foreground_regions = first_seq;
465 cvZero(model->foreground);
466 cvDrawContours(model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);
467
468 } else {
469
470 model->foreground_regions = NULL;
471 }
472
473 // Check ALL BG update condition:
474 if( ((float)FG_pixels_count/(model->Ftd->width*model->Ftd->height)) > CV_BGFG_FGD_BG_UPDATE_TRESH )
475 {
476 for( i = 0; i < model->Ftd->height; i++ )
477 for( j = 0; j < model->Ftd->width; j++ )
478 {
479 CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
480 stat->is_trained_st_model = stat->is_trained_dyn_model = 1;
481 }
482 }
483
484
485 // Update background model:
486 for( i = 0; i < model->Ftd->height; i++ )
487 {
488 for( j = 0; j < model->Ftd->width; j++ )
489 {
490 CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
491 CvBGPixelCStatTable* ctable = stat->ctable;
492 CvBGPixelCCStatTable* cctable = stat->cctable;
493
494 uchar *curr_data = (uchar*)(curr_frame->imageData)+i*curr_frame->widthStep+j*3;
495 uchar *prev_data = (uchar*)(prev_frame->imageData)+i*prev_frame->widthStep+j*3;
496
497 if( ((uchar*)model->Ftd->imageData)[i*mask_step+j] || !stat->is_trained_dyn_model )
498 {
499 float alpha = stat->is_trained_dyn_model ? model->params.alpha2 : model->params.alpha3;
500 float diff = 0;
501 int dist, min_dist = 2147483647, indx = -1;
502
503 //update Pb
504 stat->Pbcc *= (1.f-alpha);
505 if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
506 {
507 stat->Pbcc += alpha;
508 }
509
510 // Find best Vi match:
511 for(k = 0; PV_CC(k) && k < model->params.N2cc; k++ )
512 {
513 // Exponential decay of memory
514 PV_CC(k) *= (1-alpha);
515 PVB_CC(k) *= (1-alpha);
516 if( PV_CC(k) < MIN_PV )
517 {
518 PV_CC(k) = 0;
519 PVB_CC(k) = 0;
520 continue;
521 }
522
523 dist = 0;
524 for( l = 0; l < 3; l++ )
525 {
526 int val = abs( V_CC(k,l) - prev_data[l] );
527 if( val > deltaCC ) break;
528 dist += val;
529 val = abs( V_CC(k,l+3) - curr_data[l] );
530 if( val > deltaCC) break;
531 dist += val;
532 }
533 if( l == 3 && dist < min_dist )
534 {
535 min_dist = dist;
536 indx = k;
537 }
538 }
539
540
541 if( indx < 0 )
542 { // Replace N2th elem in the table by new feature:
543 indx = model->params.N2cc - 1;
544 PV_CC(indx) = alpha;
545 PVB_CC(indx) = alpha;
546 //udate Vt
547 for( l = 0; l < 3; l++ )
548 {
549 V_CC(indx,l) = prev_data[l];
550 V_CC(indx,l+3) = curr_data[l];
551 }
552 }
553 else
554 { // Update:
555 PV_CC(indx) += alpha;
556 if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
557 {
558 PVB_CC(indx) += alpha;
559 }
560 }
561
562 //re-sort CCt table by Pv
563 for( k = 0; k < indx; k++ )
564 {
565 if( PV_CC(k) <= PV_CC(indx) )
566 {
567 //shift elements
568 CvBGPixelCCStatTable tmp1, tmp2 = cctable[indx];
569 for( l = k; l <= indx; l++ )
570 {
571 tmp1 = cctable[l];
572 cctable[l] = tmp2;
573 tmp2 = tmp1;
574 }
575 break;
576 }
577 }
578
579
580 float sum1=0, sum2=0;
581 //check "once-off" changes
582 for(k = 0; PV_CC(k) && k < model->params.N1cc; k++ )
583 {
584 sum1 += PV_CC(k);
585 sum2 += PVB_CC(k);
586 }
587 if( sum1 > model->params.T ) stat->is_trained_dyn_model = 1;
588
589 diff = sum1 - stat->Pbcc * sum2;
590 // Update stat table:
591 if( diff > model->params.T )
592 {
593 //printf("once off change at motion mode\n");
594 //new BG features are discovered
595 for( k = 0; PV_CC(k) && k < model->params.N1cc; k++ )
596 {
597 PVB_CC(k) =
598 (PV_CC(k)-stat->Pbcc*PVB_CC(k))/(1-stat->Pbcc);
599 }
600 assert(stat->Pbcc<=1 && stat->Pbcc>=0);
601 }
602 }
603
604 // Handle "stationary" pixel:
605 if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] )
606 {
607 float alpha = stat->is_trained_st_model ? model->params.alpha2 : model->params.alpha3;
608 float diff = 0;
609 int dist, min_dist = 2147483647, indx = -1;
610
611 //update Pb
612 stat->Pbc *= (1.f-alpha);
613 if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
614 {
615 stat->Pbc += alpha;
616 }
617
618 //find best Vi match
619 for( k = 0; k < model->params.N2c; k++ )
620 {
621 // Exponential decay of memory
622 PV_C(k) *= (1-alpha);
623 PVB_C(k) *= (1-alpha);
624 if( PV_C(k) < MIN_PV )
625 {
626 PV_C(k) = 0;
627 PVB_C(k) = 0;
628 continue;
629 }
630
631 dist = 0;
632 for( l = 0; l < 3; l++ )
633 {
634 int val = abs( V_C(k,l) - curr_data[l] );
635 if( val > deltaC ) break;
636 dist += val;
637 }
638 if( l == 3 && dist < min_dist )
639 {
640 min_dist = dist;
641 indx = k;
642 }
643 }
644
645 if( indx < 0 )
646 {//N2th elem in the table is replaced by a new features
647 indx = model->params.N2c - 1;
648 PV_C(indx) = alpha;
649 PVB_C(indx) = alpha;
650 //udate Vt
651 for( l = 0; l < 3; l++ )
652 {
653 V_C(indx,l) = curr_data[l];
654 }
655 } else
656 {//update
657 PV_C(indx) += alpha;
658 if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
659 {
660 PVB_C(indx) += alpha;
661 }
662 }
663
664 //re-sort Ct table by Pv
665 for( k = 0; k < indx; k++ )
666 {
667 if( PV_C(k) <= PV_C(indx) )
668 {
669 //shift elements
670 CvBGPixelCStatTable tmp1, tmp2 = ctable[indx];
671 for( l = k; l <= indx; l++ )
672 {
673 tmp1 = ctable[l];
674 ctable[l] = tmp2;
675 tmp2 = tmp1;
676 }
677 break;
678 }
679 }
680
681 // Check "once-off" changes:
682 float sum1=0, sum2=0;
683 for( k = 0; PV_C(k) && k < model->params.N1c; k++ )
684 {
685 sum1 += PV_C(k);
686 sum2 += PVB_C(k);
687 }
688 diff = sum1 - stat->Pbc * sum2;
689 if( sum1 > model->params.T ) stat->is_trained_st_model = 1;
690
691 // Update stat table:
692 if( diff > model->params.T )
693 {
694 //printf("once off change at stat mode\n");
695 //new BG features are discovered
696 for( k = 0; PV_C(k) && k < model->params.N1c; k++ )
697 {
698 PVB_C(k) = (PV_C(k)-stat->Pbc*PVB_C(k))/(1-stat->Pbc);
699 }
700 stat->Pbc = 1 - stat->Pbc;
701 }
702 } // if !(change detection) at pixel (i,j)
703
704 // Update the reference BG image:
705 if( !((uchar*)model->foreground->imageData)[i*mask_step+j])
706 {
707 uchar* ptr = ((uchar*)model->background->imageData) + i*model->background->widthStep+j*3;
708
709 if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] &&
710 !((uchar*)model->Fbd->imageData)[i*mask_step+j] )
711 {
712 // Apply IIR filter:
713 for( l = 0; l < 3; l++ )
714 {
715 int a = cvRound(ptr[l]*(1 - model->params.alpha1) + model->params.alpha1*curr_data[l]);
716 ptr[l] = (uchar)a;
717 //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l]*=(1 - model->params.alpha1);
718 //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] += model->params.alpha1*curr_data[l];
719 }
720 }
721 else
722 {
723 // Background change detected:
724 for( l = 0; l < 3; l++ )
725 {
726 //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] = curr_data[l];
727 ptr[l] = curr_data[l];
728 }
729 }
730 }
731 } // j
732 } // i
733
734 // Keep previous frame:
735 cvCopy( curr_frame, model->prev_frame );
736
737 return region_count;
738 }
739
740 /* End of file. */
741