summaryrefslogtreecommitdiff
path: root/cvaux/src/cvbgfg_acmmm2003.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cvaux/src/cvbgfg_acmmm2003.cpp')
-rw-r--r--cvaux/src/cvbgfg_acmmm2003.cpp740
1 files changed, 740 insertions, 0 deletions
diff --git a/cvaux/src/cvbgfg_acmmm2003.cpp b/cvaux/src/cvbgfg_acmmm2003.cpp
new file mode 100644
index 0000000..bcb03b3
--- /dev/null
+++ b/cvaux/src/cvbgfg_acmmm2003.cpp
@@ -0,0 +1,740 @@
+/*M///////////////////////////////////////////////////////////////////////////////////////
+//
+// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+//
+// By downloading, copying, installing or using the software you agree to this license.
+// If you do not agree to this license, do not download, install,
+// copy or use the software.
+//
+//
+// Intel License Agreement
+//
+// Copyright (C) 2000, Intel Corporation, all rights reserved.
+// Third party copyrights are property of their respective owners.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// * Redistribution's of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistribution's in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * The name of Intel Corporation may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// This software is provided by the copyright holders and contributors "as is" and
+// any express or implied warranties, including, but not limited to, the implied
+// warranties of merchantability and fitness for a particular purpose are disclaimed.
+// In no event shall the Intel Corporation or contributors be liable for any direct,
+// indirect, incidental, special, exemplary, or consequential damages
+// (including, but not limited to, procurement of substitute goods or services;
+// loss of use, data, or profits; or business interruption) however caused
+// and on any theory of liability, whether in contract, strict liability,
+// or tort (including negligence or otherwise) arising in any way out of
+// the use of this software, even if advised of the possibility of such damage.
+//
+//M*/
+
+
+// This file implements the foreground/background pixel
+// discrimination algorithm described in
+//
+// Foreground Object Detection from Videos Containing Complex Background
+// Li, Huan, Gu, Tian 2003 9p
+// http://muq.org/~cynbe/bib/foreground-object-detection-from-videos-containing-complex-background.pdf
+
+
+#include "_cvaux.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+//#include <algorithm>
+
+static double* _cv_max_element( double* start, double* end )
+{
+ double* p = start++;
+
+ for( ; start != end; ++start) {
+
+ if (*p < *start) p = start;
+ }
+
+ return p;
+}
+
+static void CV_CDECL icvReleaseFGDStatModel( CvFGDStatModel** model );
+static int CV_CDECL icvUpdateFGDStatModel( IplImage* curr_frame,
+ CvFGDStatModel* model );
+
+// Function cvCreateFGDStatModel initializes foreground detection process
+// parameters:
+// first_frame - frame from video sequence
+// parameters - (optional) if NULL default parameters of the algorithm will be used
+// p_model - pointer to CvFGDStatModel structure
+CV_IMPL CvBGStatModel*
+cvCreateFGDStatModel( IplImage* first_frame, CvFGDStatModelParams* parameters )
+{
+ CvFGDStatModel* p_model = 0;
+
+ CV_FUNCNAME( "cvCreateFGDStatModel" );
+
+ __BEGIN__;
+
+ int i, j, k, pixel_count, buf_size;
+ CvFGDStatModelParams params;
+
+ if( !CV_IS_IMAGE(first_frame) )
+ CV_ERROR( CV_StsBadArg, "Invalid or NULL first_frame parameter" );
+
+ if (first_frame->nChannels != 3)
+ CV_ERROR( CV_StsBadArg, "first_frame must have 3 color channels" );
+
+ // Initialize parameters:
+ if( parameters == NULL )
+ {
+ params.Lc = CV_BGFG_FGD_LC;
+ params.N1c = CV_BGFG_FGD_N1C;
+ params.N2c = CV_BGFG_FGD_N2C;
+
+ params.Lcc = CV_BGFG_FGD_LCC;
+ params.N1cc = CV_BGFG_FGD_N1CC;
+ params.N2cc = CV_BGFG_FGD_N2CC;
+
+ params.delta = CV_BGFG_FGD_DELTA;
+
+ params.alpha1 = CV_BGFG_FGD_ALPHA_1;
+ params.alpha2 = CV_BGFG_FGD_ALPHA_2;
+ params.alpha3 = CV_BGFG_FGD_ALPHA_3;
+
+ params.T = CV_BGFG_FGD_T;
+ params.minArea = CV_BGFG_FGD_MINAREA;
+
+ params.is_obj_without_holes = 1;
+ params.perform_morphing = 1;
+ }
+ else
+ {
+ params = *parameters;
+ }
+
+ CV_CALL( p_model = (CvFGDStatModel*)cvAlloc( sizeof(*p_model) ));
+ memset( p_model, 0, sizeof(*p_model) );
+ p_model->type = CV_BG_MODEL_FGD;
+ p_model->release = (CvReleaseBGStatModel)icvReleaseFGDStatModel;
+ p_model->update = (CvUpdateBGStatModel)icvUpdateFGDStatModel;;
+ p_model->params = params;
+
+ // Initialize storage pools:
+ pixel_count = first_frame->width * first_frame->height;
+
+ buf_size = pixel_count*sizeof(p_model->pixel_stat[0]);
+ CV_CALL( p_model->pixel_stat = (CvBGPixelStat*)cvAlloc(buf_size) );
+ memset( p_model->pixel_stat, 0, buf_size );
+
+ buf_size = pixel_count*params.N2c*sizeof(p_model->pixel_stat[0].ctable[0]);
+ CV_CALL( p_model->pixel_stat[0].ctable = (CvBGPixelCStatTable*)cvAlloc(buf_size) );
+ memset( p_model->pixel_stat[0].ctable, 0, buf_size );
+
+ buf_size = pixel_count*params.N2cc*sizeof(p_model->pixel_stat[0].cctable[0]);
+ CV_CALL( p_model->pixel_stat[0].cctable = (CvBGPixelCCStatTable*)cvAlloc(buf_size) );
+ memset( p_model->pixel_stat[0].cctable, 0, buf_size );
+
+ for( i = 0, k = 0; i < first_frame->height; i++ ) {
+ for( j = 0; j < first_frame->width; j++, k++ )
+ {
+ p_model->pixel_stat[k].ctable = p_model->pixel_stat[0].ctable + k*params.N2c;
+ p_model->pixel_stat[k].cctable = p_model->pixel_stat[0].cctable + k*params.N2cc;
+ }
+ }
+
+ // Init temporary images:
+ CV_CALL( p_model->Ftd = cvCreateImage(cvSize(first_frame->width, first_frame->height), IPL_DEPTH_8U, 1));
+ CV_CALL( p_model->Fbd = cvCreateImage(cvSize(first_frame->width, first_frame->height), IPL_DEPTH_8U, 1));
+ CV_CALL( p_model->foreground = cvCreateImage(cvSize(first_frame->width, first_frame->height), IPL_DEPTH_8U, 1));
+
+ CV_CALL( p_model->background = cvCloneImage(first_frame));
+ CV_CALL( p_model->prev_frame = cvCloneImage(first_frame));
+ CV_CALL( p_model->storage = cvCreateMemStorage());
+
+ __END__;
+
+ if( cvGetErrStatus() < 0 )
+ {
+ CvBGStatModel* base_ptr = (CvBGStatModel*)p_model;
+
+ if( p_model && p_model->release )
+ p_model->release( &base_ptr );
+ else
+ cvFree( &p_model );
+ p_model = 0;
+ }
+
+ return (CvBGStatModel*)p_model;
+}
+
+
+static void CV_CDECL
+icvReleaseFGDStatModel( CvFGDStatModel** _model )
+{
+ CV_FUNCNAME( "icvReleaseFGDStatModel" );
+
+ __BEGIN__;
+
+ if( !_model )
+ CV_ERROR( CV_StsNullPtr, "" );
+
+ if( *_model )
+ {
+ CvFGDStatModel* model = *_model;
+ if( model->pixel_stat )
+ {
+ cvFree( &model->pixel_stat[0].ctable );
+ cvFree( &model->pixel_stat[0].cctable );
+ cvFree( &model->pixel_stat );
+ }
+
+ cvReleaseImage( &model->Ftd );
+ cvReleaseImage( &model->Fbd );
+ cvReleaseImage( &model->foreground );
+ cvReleaseImage( &model->background );
+ cvReleaseImage( &model->prev_frame );
+ cvReleaseMemStorage(&model->storage);
+
+ cvFree( _model );
+ }
+
+ __END__;
+}
+
+// Function cvChangeDetection performs change detection for Foreground detection algorithm
+// parameters:
+// prev_frame -
+// curr_frame -
+// change_mask -
+CV_IMPL int
+cvChangeDetection( IplImage* prev_frame,
+ IplImage* curr_frame,
+ IplImage* change_mask )
+{
+ int i, j, b, x, y, thres;
+ const int PIXELRANGE=256;
+
+ if( !prev_frame
+ || !curr_frame
+ || !change_mask
+ || prev_frame->nChannels != 3
+ || curr_frame->nChannels != 3
+ || change_mask->nChannels != 1
+ || prev_frame->depth != IPL_DEPTH_8U
+ || curr_frame->depth != IPL_DEPTH_8U
+ || change_mask->depth != IPL_DEPTH_8U
+ || prev_frame->width != curr_frame->width
+ || prev_frame->height != curr_frame->height
+ || prev_frame->width != change_mask->width
+ || prev_frame->height != change_mask->height
+ ){
+ return 0;
+ }
+
+ cvZero ( change_mask );
+
+ // All operations per colour
+ for (b=0 ; b<prev_frame->nChannels ; b++) {
+
+ // Create histogram:
+
+ long HISTOGRAM[PIXELRANGE];
+ for (i=0 ; i<PIXELRANGE; i++) HISTOGRAM[i]=0;
+
+ for (y=0 ; y<curr_frame->height ; y++)
+ {
+ uchar* rowStart1 = (uchar*)curr_frame->imageData + y * curr_frame->widthStep + b;
+ uchar* rowStart2 = (uchar*)prev_frame->imageData + y * prev_frame->widthStep + b;
+ for (x=0 ; x<curr_frame->width ; x++, rowStart1+=curr_frame->nChannels, rowStart2+=prev_frame->nChannels) {
+ int diff = abs( int(*rowStart1) - int(*rowStart2) );
+ HISTOGRAM[diff]++;
+ }
+ }
+
+ double relativeVariance[PIXELRANGE];
+ for (i=0 ; i<PIXELRANGE; i++) relativeVariance[i]=0;
+
+ for (thres=PIXELRANGE-2; thres>=0 ; thres--)
+ {
+ // fprintf(stderr, "Iter %d\n", thres);
+ double sum=0;
+ double sqsum=0;
+ int count=0;
+ // fprintf(stderr, "Iter %d entering loop\n", thres);
+ for (j=thres ; j<PIXELRANGE ; j++) {
+ sum += double(j)*double(HISTOGRAM[j]);
+ sqsum += double(j*j)*double(HISTOGRAM[j]);
+ count += HISTOGRAM[j];
+ }
+ count = count == 0 ? 1 : count;
+ // fprintf(stderr, "Iter %d finishing loop\n", thres);
+ double my = sum / count;
+ double sigma = sqrt( sqsum/count - my*my);
+ // fprintf(stderr, "Iter %d sum=%g sqsum=%g count=%d sigma = %g\n", thres, sum, sqsum, count, sigma);
+ // fprintf(stderr, "Writing to %x\n", &(relativeVariance[thres]));
+ relativeVariance[thres] = sigma;
+ // fprintf(stderr, "Iter %d finished\n", thres);
+ }
+
+ // Find maximum:
+ uchar bestThres = 0;
+
+ double* pBestThres = _cv_max_element(relativeVariance, relativeVariance+PIXELRANGE);
+ bestThres = (uchar)(*pBestThres); if (bestThres <10) bestThres=10;
+
+ for (y=0 ; y<prev_frame->height ; y++)
+ {
+ uchar* rowStart1 = (uchar*)(curr_frame->imageData) + y * curr_frame->widthStep + b;
+ uchar* rowStart2 = (uchar*)(prev_frame->imageData) + y * prev_frame->widthStep + b;
+ uchar* rowStart3 = (uchar*)(change_mask->imageData) + y * change_mask->widthStep;
+ for (x = 0; x < curr_frame->width; x++, rowStart1+=curr_frame->nChannels,
+ rowStart2+=prev_frame->nChannels, rowStart3+=change_mask->nChannels) {
+ // OR between different color channels
+ int diff = abs( int(*rowStart1) - int(*rowStart2) );
+ if ( diff > bestThres)
+ *rowStart3 |=255;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+#define MIN_PV 1E-10
+
+
+#define V_C(k,l) ctable[k].v[l]
+#define PV_C(k) ctable[k].Pv
+#define PVB_C(k) ctable[k].Pvb
+#define V_CC(k,l) cctable[k].v[l]
+#define PV_CC(k) cctable[k].Pv
+#define PVB_CC(k) cctable[k].Pvb
+
+
+// Function cvUpdateFGDStatModel updates statistical model and returns number of foreground regions
+// parameters:
+// curr_frame - current frame from video sequence
+// p_model - pointer to CvFGDStatModel structure
+static int CV_CDECL
+icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model )
+{
+ int mask_step = model->Ftd->widthStep;
+ CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL;
+ IplImage* prev_frame = model->prev_frame;
+ int region_count = 0;
+ int FG_pixels_count = 0;
+ int deltaC = cvRound(model->params.delta * 256 / model->params.Lc);
+ int deltaCC = cvRound(model->params.delta * 256 / model->params.Lcc);
+ int i, j, k, l;
+
+ //clear storages
+ cvClearMemStorage(model->storage);
+ cvZero(model->foreground);
+
+ // From foreground pixel candidates using image differencing
+ // with adaptive thresholding. The algorithm is from:
+ //
+ // Thresholding for Change Detection
+ // Paul L. Rosin 1998 6p
+ // http://www.cis.temple.edu/~latecki/Courses/CIS750-03/Papers/thresh-iccv.pdf
+ //
+ cvChangeDetection( prev_frame, curr_frame, model->Ftd );
+ cvChangeDetection( model->background, curr_frame, model->Fbd );
+
+ for( i = 0; i < model->Ftd->height; i++ )
+ {
+ for( j = 0; j < model->Ftd->width; j++ )
+ {
+ if( ((uchar*)model->Fbd->imageData)[i*mask_step+j] || ((uchar*)model->Ftd->imageData)[i*mask_step+j] )
+ {
+ float Pb = 0;
+ float Pv = 0;
+ float Pvb = 0;
+
+ CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
+
+ CvBGPixelCStatTable* ctable = stat->ctable;
+ CvBGPixelCCStatTable* cctable = stat->cctable;
+
+ uchar* curr_data = (uchar*)(curr_frame->imageData) + i*curr_frame->widthStep + j*3;
+ uchar* prev_data = (uchar*)(prev_frame->imageData) + i*prev_frame->widthStep + j*3;
+
+ int val = 0;
+
+ // Is it a motion pixel?
+ if( ((uchar*)model->Ftd->imageData)[i*mask_step+j] )
+ {
+ if( !stat->is_trained_dyn_model ) {
+
+ val = 1;
+
+ } else {
+
+ // Compare with stored CCt vectors:
+ for( k = 0; PV_CC(k) > model->params.alpha2 && k < model->params.N1cc; k++ )
+ {
+ if ( abs( V_CC(k,0) - prev_data[0]) <= deltaCC &&
+ abs( V_CC(k,1) - prev_data[1]) <= deltaCC &&
+ abs( V_CC(k,2) - prev_data[2]) <= deltaCC &&
+ abs( V_CC(k,3) - curr_data[0]) <= deltaCC &&
+ abs( V_CC(k,4) - curr_data[1]) <= deltaCC &&
+ abs( V_CC(k,5) - curr_data[2]) <= deltaCC)
+ {
+ Pv += PV_CC(k);
+ Pvb += PVB_CC(k);
+ }
+ }
+ Pb = stat->Pbcc;
+ if( 2 * Pvb * Pb <= Pv ) val = 1;
+ }
+ }
+ else if( stat->is_trained_st_model )
+ {
+ // Compare with stored Ct vectors:
+ for( k = 0; PV_C(k) > model->params.alpha2 && k < model->params.N1c; k++ )
+ {
+ if ( abs( V_C(k,0) - curr_data[0]) <= deltaC &&
+ abs( V_C(k,1) - curr_data[1]) <= deltaC &&
+ abs( V_C(k,2) - curr_data[2]) <= deltaC )
+ {
+ Pv += PV_C(k);
+ Pvb += PVB_C(k);
+ }
+ }
+ Pb = stat->Pbc;
+ if( 2 * Pvb * Pb <= Pv ) val = 1;
+ }
+
+ // Update foreground:
+ ((uchar*)model->foreground->imageData)[i*mask_step+j] = (uchar)(val*255);
+ FG_pixels_count += val;
+
+ } // end if( change detection...
+ } // for j...
+ } // for i...
+ //end BG/FG classification
+
+ // Foreground segmentation.
+ // Smooth foreground map:
+ if( model->params.perform_morphing ){
+ cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_OPEN, model->params.perform_morphing );
+ cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_CLOSE, model->params.perform_morphing );
+ }
+
+
+ if( model->params.minArea > 0 || model->params.is_obj_without_holes ){
+
+ // Discard under-size foreground regions:
+ //
+ cvFindContours( model->foreground, model->storage, &first_seq, sizeof(CvContour), CV_RETR_LIST );
+ for( seq = first_seq; seq; seq = seq->h_next )
+ {
+ CvContour* cnt = (CvContour*)seq;
+ if( cnt->rect.width * cnt->rect.height < model->params.minArea ||
+ (model->params.is_obj_without_holes && CV_IS_SEQ_HOLE(seq)) )
+ {
+ // Delete under-size contour:
+ prev_seq = seq->h_prev;
+ if( prev_seq )
+ {
+ prev_seq->h_next = seq->h_next;
+ if( seq->h_next ) seq->h_next->h_prev = prev_seq;
+ }
+ else
+ {
+ first_seq = seq->h_next;
+ if( seq->h_next ) seq->h_next->h_prev = NULL;
+ }
+ }
+ else
+ {
+ region_count++;
+ }
+ }
+ model->foreground_regions = first_seq;
+ cvZero(model->foreground);
+ cvDrawContours(model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);
+
+ } else {
+
+ model->foreground_regions = NULL;
+ }
+
+ // Check ALL BG update condition:
+ if( ((float)FG_pixels_count/(model->Ftd->width*model->Ftd->height)) > CV_BGFG_FGD_BG_UPDATE_TRESH )
+ {
+ for( i = 0; i < model->Ftd->height; i++ )
+ for( j = 0; j < model->Ftd->width; j++ )
+ {
+ CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
+ stat->is_trained_st_model = stat->is_trained_dyn_model = 1;
+ }
+ }
+
+
+ // Update background model:
+ for( i = 0; i < model->Ftd->height; i++ )
+ {
+ for( j = 0; j < model->Ftd->width; j++ )
+ {
+ CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
+ CvBGPixelCStatTable* ctable = stat->ctable;
+ CvBGPixelCCStatTable* cctable = stat->cctable;
+
+ uchar *curr_data = (uchar*)(curr_frame->imageData)+i*curr_frame->widthStep+j*3;
+ uchar *prev_data = (uchar*)(prev_frame->imageData)+i*prev_frame->widthStep+j*3;
+
+ if( ((uchar*)model->Ftd->imageData)[i*mask_step+j] || !stat->is_trained_dyn_model )
+ {
+ float alpha = stat->is_trained_dyn_model ? model->params.alpha2 : model->params.alpha3;
+ float diff = 0;
+ int dist, min_dist = 2147483647, indx = -1;
+
+ //update Pb
+ stat->Pbcc *= (1.f-alpha);
+ if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
+ {
+ stat->Pbcc += alpha;
+ }
+
+ // Find best Vi match:
+ for(k = 0; PV_CC(k) && k < model->params.N2cc; k++ )
+ {
+ // Exponential decay of memory
+ PV_CC(k) *= (1-alpha);
+ PVB_CC(k) *= (1-alpha);
+ if( PV_CC(k) < MIN_PV )
+ {
+ PV_CC(k) = 0;
+ PVB_CC(k) = 0;
+ continue;
+ }
+
+ dist = 0;
+ for( l = 0; l < 3; l++ )
+ {
+ int val = abs( V_CC(k,l) - prev_data[l] );
+ if( val > deltaCC ) break;
+ dist += val;
+ val = abs( V_CC(k,l+3) - curr_data[l] );
+ if( val > deltaCC) break;
+ dist += val;
+ }
+ if( l == 3 && dist < min_dist )
+ {
+ min_dist = dist;
+ indx = k;
+ }
+ }
+
+
+ if( indx < 0 )
+ { // Replace N2th elem in the table by new feature:
+ indx = model->params.N2cc - 1;
+ PV_CC(indx) = alpha;
+ PVB_CC(indx) = alpha;
+ //udate Vt
+ for( l = 0; l < 3; l++ )
+ {
+ V_CC(indx,l) = prev_data[l];
+ V_CC(indx,l+3) = curr_data[l];
+ }
+ }
+ else
+ { // Update:
+ PV_CC(indx) += alpha;
+ if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
+ {
+ PVB_CC(indx) += alpha;
+ }
+ }
+
+ //re-sort CCt table by Pv
+ for( k = 0; k < indx; k++ )
+ {
+ if( PV_CC(k) <= PV_CC(indx) )
+ {
+ //shift elements
+ CvBGPixelCCStatTable tmp1, tmp2 = cctable[indx];
+ for( l = k; l <= indx; l++ )
+ {
+ tmp1 = cctable[l];
+ cctable[l] = tmp2;
+ tmp2 = tmp1;
+ }
+ break;
+ }
+ }
+
+
+ float sum1=0, sum2=0;
+ //check "once-off" changes
+ for(k = 0; PV_CC(k) && k < model->params.N1cc; k++ )
+ {
+ sum1 += PV_CC(k);
+ sum2 += PVB_CC(k);
+ }
+ if( sum1 > model->params.T ) stat->is_trained_dyn_model = 1;
+
+ diff = sum1 - stat->Pbcc * sum2;
+ // Update stat table:
+ if( diff > model->params.T )
+ {
+ //printf("once off change at motion mode\n");
+ //new BG features are discovered
+ for( k = 0; PV_CC(k) && k < model->params.N1cc; k++ )
+ {
+ PVB_CC(k) =
+ (PV_CC(k)-stat->Pbcc*PVB_CC(k))/(1-stat->Pbcc);
+ }
+ assert(stat->Pbcc<=1 && stat->Pbcc>=0);
+ }
+ }
+
+ // Handle "stationary" pixel:
+ if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] )
+ {
+ float alpha = stat->is_trained_st_model ? model->params.alpha2 : model->params.alpha3;
+ float diff = 0;
+ int dist, min_dist = 2147483647, indx = -1;
+
+ //update Pb
+ stat->Pbc *= (1.f-alpha);
+ if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
+ {
+ stat->Pbc += alpha;
+ }
+
+ //find best Vi match
+ for( k = 0; k < model->params.N2c; k++ )
+ {
+ // Exponential decay of memory
+ PV_C(k) *= (1-alpha);
+ PVB_C(k) *= (1-alpha);
+ if( PV_C(k) < MIN_PV )
+ {
+ PV_C(k) = 0;
+ PVB_C(k) = 0;
+ continue;
+ }
+
+ dist = 0;
+ for( l = 0; l < 3; l++ )
+ {
+ int val = abs( V_C(k,l) - curr_data[l] );
+ if( val > deltaC ) break;
+ dist += val;
+ }
+ if( l == 3 && dist < min_dist )
+ {
+ min_dist = dist;
+ indx = k;
+ }
+ }
+
+ if( indx < 0 )
+ {//N2th elem in the table is replaced by a new features
+ indx = model->params.N2c - 1;
+ PV_C(indx) = alpha;
+ PVB_C(indx) = alpha;
+ //udate Vt
+ for( l = 0; l < 3; l++ )
+ {
+ V_C(indx,l) = curr_data[l];
+ }
+ } else
+ {//update
+ PV_C(indx) += alpha;
+ if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
+ {
+ PVB_C(indx) += alpha;
+ }
+ }
+
+ //re-sort Ct table by Pv
+ for( k = 0; k < indx; k++ )
+ {
+ if( PV_C(k) <= PV_C(indx) )
+ {
+ //shift elements
+ CvBGPixelCStatTable tmp1, tmp2 = ctable[indx];
+ for( l = k; l <= indx; l++ )
+ {
+ tmp1 = ctable[l];
+ ctable[l] = tmp2;
+ tmp2 = tmp1;
+ }
+ break;
+ }
+ }
+
+ // Check "once-off" changes:
+ float sum1=0, sum2=0;
+ for( k = 0; PV_C(k) && k < model->params.N1c; k++ )
+ {
+ sum1 += PV_C(k);
+ sum2 += PVB_C(k);
+ }
+ diff = sum1 - stat->Pbc * sum2;
+ if( sum1 > model->params.T ) stat->is_trained_st_model = 1;
+
+ // Update stat table:
+ if( diff > model->params.T )
+ {
+ //printf("once off change at stat mode\n");
+ //new BG features are discovered
+ for( k = 0; PV_C(k) && k < model->params.N1c; k++ )
+ {
+ PVB_C(k) = (PV_C(k)-stat->Pbc*PVB_C(k))/(1-stat->Pbc);
+ }
+ stat->Pbc = 1 - stat->Pbc;
+ }
+ } // if !(change detection) at pixel (i,j)
+
+ // Update the reference BG image:
+ if( !((uchar*)model->foreground->imageData)[i*mask_step+j])
+ {
+ uchar* ptr = ((uchar*)model->background->imageData) + i*model->background->widthStep+j*3;
+
+ if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] &&
+ !((uchar*)model->Fbd->imageData)[i*mask_step+j] )
+ {
+ // Apply IIR filter:
+ for( l = 0; l < 3; l++ )
+ {
+ int a = cvRound(ptr[l]*(1 - model->params.alpha1) + model->params.alpha1*curr_data[l]);
+ ptr[l] = (uchar)a;
+ //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l]*=(1 - model->params.alpha1);
+ //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] += model->params.alpha1*curr_data[l];
+ }
+ }
+ else
+ {
+ // Background change detected:
+ for( l = 0; l < 3; l++ )
+ {
+ //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] = curr_data[l];
+ ptr[l] = curr_data[l];
+ }
+ }
+ }
+ } // j
+ } // i
+
+ // Keep previous frame:
+ cvCopy( curr_frame, model->prev_frame );
+
+ return region_count;
+}
+
+/* End of file. */