Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cvaux / cvbgfg_gaussmix.cpp
diff --git a/src/cvaux/cvbgfg_gaussmix.cpp b/src/cvaux/cvbgfg_gaussmix.cpp
new file mode 100644 (file)
index 0000000..eb548c9
--- /dev/null
@@ -0,0 +1,598 @@
+/*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 is based on the "An Improved Adaptive Background Mixture Model for
+// Real-time Tracking with Shadow Detection" by P. KaewTraKulPong and R. Bowden
+// http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf
+//
+// The windowing method is used, but not the shadow detection. I make some of my
+// own modifications which make more sense. There are some errors in some of their
+// equations.
+//
+//IplImage values of image that are useful
+//int  nSize;         /* sizeof(IplImage) */
+//int  depth;         /* pixel depth in bits: IPL_DEPTH_8U ...*/
+//int  nChannels;     /* OpenCV functions support 1,2,3 or 4 channels */
+//int  width;         /* image width in pixels */
+//int  height;        /* image height in pixels */
+//int  imageSize;     /* image data size in bytes in case of interleaved data)*/
+//char *imageData;    /* pointer to aligned image data */
+//char *imageDataOrigin; /* pointer to very origin of image -deallocation */
+//Values useful for gaussian integral
+//0.5 - 0.19146 - 0.38292
+//1.0 - 0.34134 - 0.68268
+//1.5 - 0.43319 - 0.86638
+//2.0 - 0.47725 - 0.95450
+//2.5 - 0.49379 - 0.98758
+//3.0 - 0.49865 - 0.99730
+//3.5 - 0.4997674 - 0.9995348
+//4.0 - 0.4999683 - 0.9999366
+
+#include "_cvaux.h"
+
+
+//internal functions for gaussian background detection
+static void icvInsertionSortGaussians( CvGaussBGPoint* g_point, double* sort_key, CvGaussBGStatModelParams *bg_model_params );
+
+/* 
+   Test whether pixel can be explained by background model; 
+   Return -1 if no match was found; otherwise the index in match[] is returned
+
+   icvMatchTest(...) assumes what all color channels component exhibit the same variance
+   icvMatchTest2(...) accounts for different variances per color channel
+ */
+static int icvMatchTest( double* src_pixel, int nChannels, int* match, 
+                 const CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params );
+/*static int icvMatchTest2( double* src_pixel, int nChannels, int* match, 
+                 const CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params );*/
+
+
+/* 
+   The update procedure differs between  
+      * the initialization phase (named *Partial* ) and
+      * the normal phase (named *Full* )
+   The initalization phase is defined as not having processed <win_size> frames yet
+ */
+static void icvUpdateFullWindow( double* src_pixel, int nChannels, 
+                         int* match,
+                         CvGaussBGPoint* g_point, 
+                         const CvGaussBGStatModelParams *bg_model_params );
+static void icvUpdateFullNoMatch( IplImage* gm_image, int p, 
+                          int* match, 
+                          CvGaussBGPoint* g_point, 
+                          const CvGaussBGStatModelParams *bg_model_params);
+static void icvUpdatePartialWindow( double* src_pixel, int nChannels, int* match, 
+                            CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params );
+static void icvUpdatePartialNoMatch( double* src_pixel, int nChannels, 
+                             int* match, 
+                             CvGaussBGPoint* g_point, 
+                             const CvGaussBGStatModelParams *bg_model_params);
+
+
+static void icvGetSortKey( const int nChannels, double* sort_key, const CvGaussBGPoint* g_point, 
+                    const CvGaussBGStatModelParams *bg_model_params );
+static void icvBackgroundTest( const int nChannels, int n, int i, int j, int *match, CvGaussBGModel* bg_model );
+
+static void CV_CDECL icvReleaseGaussianBGModel( CvGaussBGModel** bg_model );
+static int CV_CDECL icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel*  bg_model );
+
+//#define for if(0);else for
+
+//g = 1 for first gaussian in list that matches else g = 0
+//Rw is the learning rate for weight and Rg is leaning rate for mean and variance
+//Ms is the match_sum which is the sum of matches for a particular gaussian
+//Ms values are incremented until the sum of Ms values in the list equals window size L
+//SMs is the sum of match_sums for gaussians in the list
+//Rw = 1/SMs note the smallest Rw gets is 1/L
+//Rg = g/Ms for SMs < L and Rg = g/(w*L) for SMs = L
+//The list is maintained in sorted order using w/sqrt(variance) as a key
+//If there is no match the last gaussian in the list is replaced by the new gaussian
+//This will result in changes to SMs which results in changes in Rw and Rg.
+//If a gaussian is replaced and SMs previously equaled L values of Ms are computed from w
+//w[n+1] = w[n] + Rw*(g - w[n])   weight
+//u[n+1] = u[n] + Rg*(x[n+1] - u[n]) mean value Sg is sum n values of g
+//v[n+1] = v[n] + Rg*((x[n+1] - u[n])*(x[n+1] - u[n])) - v[n]) variance
+//
+
+CV_IMPL CvBGStatModel*
+cvCreateGaussianBGModel( IplImage* first_frame, CvGaussBGStatModelParams* parameters )
+{
+    CvGaussBGModel* bg_model = 0;
+    
+    CV_FUNCNAME( "cvCreateGaussianBGModel" );
+    
+    __BEGIN__;
+    
+    double var_init;
+    CvGaussBGStatModelParams params;
+    int i, j, k, m, n;
+    
+    //init parameters
+    if( parameters == NULL )
+      {                        /* These constants are defined in cvaux/include/cvaux.h: */
+        params.win_size      = CV_BGFG_MOG_WINDOW_SIZE;
+        params.bg_threshold  = CV_BGFG_MOG_BACKGROUND_THRESHOLD;
+
+        params.std_threshold = CV_BGFG_MOG_STD_THRESHOLD;
+        params.weight_init   = CV_BGFG_MOG_WEIGHT_INIT;
+
+        params.variance_init = CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT;
+        params.minArea       = CV_BGFG_MOG_MINAREA;
+        params.n_gauss       = CV_BGFG_MOG_NGAUSSIANS;
+    }
+    else
+    {
+        params = *parameters;
+    }
+    
+    if( !CV_IS_IMAGE(first_frame) )
+        CV_ERROR( CV_StsBadArg, "Invalid or NULL first_frame parameter" );
+    
+    CV_CALL( bg_model = (CvGaussBGModel*)cvAlloc( sizeof(*bg_model) ));
+    memset( bg_model, 0, sizeof(*bg_model) );
+    bg_model->type = CV_BG_MODEL_MOG;
+    bg_model->release = (CvReleaseBGStatModel)icvReleaseGaussianBGModel;
+    bg_model->update = (CvUpdateBGStatModel)icvUpdateGaussianBGModel;
+    
+    bg_model->params = params;
+    
+    //prepare storages
+    CV_CALL( bg_model->g_point = (CvGaussBGPoint*)cvAlloc(sizeof(CvGaussBGPoint)*
+        ((first_frame->width*first_frame->height) + 256)));
+    
+    CV_CALL( bg_model->background = cvCreateImage(cvSize(first_frame->width,
+        first_frame->height), IPL_DEPTH_8U, first_frame->nChannels));
+    CV_CALL( bg_model->foreground = cvCreateImage(cvSize(first_frame->width,
+        first_frame->height), IPL_DEPTH_8U, 1));
+    
+    CV_CALL( bg_model->storage = cvCreateMemStorage());
+    
+    //initializing
+    var_init = 2 * params.std_threshold * params.std_threshold;
+    CV_CALL( bg_model->g_point[0].g_values =
+        (CvGaussBGValues*)cvAlloc( sizeof(CvGaussBGValues)*params.n_gauss*
+        (first_frame->width*first_frame->height + 128)));
+    
+    for( i = 0, n = 0; i < first_frame->height; i++ )
+    {
+        for( j = 0; j < first_frame->width; j++, n++ )
+        {
+            const int p = i*first_frame->widthStep+j*first_frame->nChannels;
+
+            bg_model->g_point[n].g_values =
+                bg_model->g_point[0].g_values + n*params.n_gauss;
+            bg_model->g_point[n].g_values[0].weight = 1;    //the first value seen has weight one
+            bg_model->g_point[n].g_values[0].match_sum = 1;
+            for( m = 0; m < first_frame->nChannels; m++)
+            {
+                bg_model->g_point[n].g_values[0].variance[m] = var_init;
+                bg_model->g_point[n].g_values[0].mean[m] = (unsigned char)first_frame->imageData[p + m];
+            }
+            for( k = 1; k < params.n_gauss; k++)
+            {
+                bg_model->g_point[n].g_values[k].weight = 0;
+                bg_model->g_point[n].g_values[k].match_sum = 0;
+                for( m = 0; m < first_frame->nChannels; m++){
+                    bg_model->g_point[n].g_values[k].variance[m] = var_init;
+                    bg_model->g_point[n].g_values[k].mean[m] = 0;
+                }
+            }
+        }
+    }
+    
+    bg_model->countFrames = 0;
+    
+    __END__;
+    
+    if( cvGetErrStatus() < 0 )
+    {
+        CvBGStatModel* base_ptr = (CvBGStatModel*)bg_model;
+        
+        if( bg_model && bg_model->release )
+            bg_model->release( &base_ptr );
+        else
+            cvFree( &bg_model );
+        bg_model = 0;
+    }
+    
+    return (CvBGStatModel*)bg_model;
+}
+
+
+static void CV_CDECL
+icvReleaseGaussianBGModel( CvGaussBGModel** _bg_model )
+{
+    CV_FUNCNAME( "icvReleaseGaussianBGModel" );
+
+    __BEGIN__;
+    
+    if( !_bg_model )
+        CV_ERROR( CV_StsNullPtr, "" );
+
+    if( *_bg_model )
+    {
+        CvGaussBGModel* bg_model = *_bg_model;
+        if( bg_model->g_point )
+        {
+            cvFree( &bg_model->g_point[0].g_values );
+            cvFree( &bg_model->g_point );
+        }
+        
+        cvReleaseImage( &bg_model->background );
+        cvReleaseImage( &bg_model->foreground );
+        cvReleaseMemStorage(&bg_model->storage);
+        memset( bg_model, 0, sizeof(*bg_model) );
+        cvFree( _bg_model );
+    }
+
+    __END__;
+}
+
+
+static int CV_CDECL
+icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel*  bg_model )
+{
+    int i, j, k, n;
+    int region_count = 0;
+    CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL;
+    
+    bg_model->countFrames++;
+    
+    for( i = 0, n = 0; i < curr_frame->height; i++ )
+    {
+        for( j = 0; j < curr_frame->width; j++, n++ )
+        {
+            int match[CV_BGFG_MOG_MAX_NGAUSSIANS];
+            double sort_key[CV_BGFG_MOG_MAX_NGAUSSIANS];
+            const int nChannels = curr_frame->nChannels;
+            const int p = curr_frame->widthStep*i+j*nChannels;
+            
+            // A few short cuts
+            CvGaussBGPoint* g_point = &bg_model->g_point[n];
+            const CvGaussBGStatModelParams bg_model_params = bg_model->params;
+            double pixel[4];
+            int no_match;
+            
+            for( k = 0; k < nChannels; k++ )
+                pixel[k] = (uchar)curr_frame->imageData[p+k];
+            
+            no_match = icvMatchTest( pixel, nChannels, match, g_point, &bg_model_params );
+            if( bg_model->countFrames >= bg_model->params.win_size )
+            {
+                icvUpdateFullWindow( pixel, nChannels, match, g_point, &bg_model->params );
+                if( no_match == -1)
+                    icvUpdateFullNoMatch( curr_frame, p, match, g_point, &bg_model_params );
+            }
+            else
+            {
+                icvUpdatePartialWindow( pixel, nChannels, match, g_point, &bg_model_params );
+                if( no_match == -1)
+                    icvUpdatePartialNoMatch( pixel, nChannels, match, g_point, &bg_model_params );
+            }
+            icvGetSortKey( nChannels, sort_key, g_point, &bg_model_params );
+            icvInsertionSortGaussians( g_point, sort_key, (CvGaussBGStatModelParams *)&bg_model_params );
+            icvBackgroundTest( nChannels, n, i, j, match, bg_model );
+        }
+    }
+    
+    //foreground filtering
+    
+    //filter small regions
+    cvClearMemStorage(bg_model->storage);
+    
+    //cvMorphologyEx( bg_model->foreground, bg_model->foreground, 0, 0, CV_MOP_OPEN, 1 );
+    //cvMorphologyEx( bg_model->foreground, bg_model->foreground, 0, 0, CV_MOP_CLOSE, 1 );
+    
+    cvFindContours( bg_model->foreground, bg_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 < bg_model->params.minArea )
+        {
+            //delete small 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++;
+        }
+    }
+    bg_model->foreground_regions = first_seq;
+    cvZero(bg_model->foreground);
+    cvDrawContours(bg_model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);
+    
+    return region_count;
+}
+
+static void icvInsertionSortGaussians( CvGaussBGPoint* g_point, double* sort_key, CvGaussBGStatModelParams *bg_model_params )
+{
+    int i, j;
+    for( i = 1; i < bg_model_params->n_gauss; i++ )
+    {
+        double index = sort_key[i];
+        for( j = i; j > 0 && sort_key[j-1] < index; j-- ) //sort decending order
+        {
+            double temp_sort_key = sort_key[j];
+            sort_key[j] = sort_key[j-1];
+            sort_key[j-1] = temp_sort_key;
+            
+            CvGaussBGValues temp_gauss_values = g_point->g_values[j];
+            g_point->g_values[j] = g_point->g_values[j-1];
+            g_point->g_values[j-1] = temp_gauss_values;
+        }
+//        sort_key[j] = index;
+    }
+}
+
+
+static int icvMatchTest( double* src_pixel, int nChannels, int* match,
+                         const CvGaussBGPoint* g_point,
+                         const CvGaussBGStatModelParams *bg_model_params )
+{
+    int k;
+    int matchPosition=-1;
+    for ( k = 0; k < bg_model_params->n_gauss; k++) match[k]=0;
+    
+    for ( k = 0; k < bg_model_params->n_gauss; k++)
+    if (g_point->g_values[k].match_sum > 0) {
+        double sum_d2 = 0.0;
+        double var_threshold = 0.0;
+        for(int m = 0; m < nChannels; m++){
+            double d = g_point->g_values[k].mean[m]- src_pixel[m];
+            sum_d2 += (d*d);
+            var_threshold += g_point->g_values[k].variance[m];
+        }  //difference < STD_LIMIT*STD_LIMIT or difference**2 < STD_LIMIT*STD_LIMIT*VAR
+        var_threshold = bg_model_params->std_threshold*bg_model_params->std_threshold*var_threshold;
+        if(sum_d2 < var_threshold){
+            match[k] = 1;
+            matchPosition = k;
+            break;
+        }
+    }
+    
+    return matchPosition;
+}
+
+/*
+static int icvMatchTest2( double* src_pixel, int nChannels, int* match,
+                          const CvGaussBGPoint* g_point,
+                          const CvGaussBGStatModelParams *bg_model_params )
+{
+    int k, m;
+    int matchPosition=-1;
+    
+    for( k = 0; k < bg_model_params->n_gauss; k++ )
+        match[k] = 0;
+    
+    for( k = 0; k < bg_model_params->n_gauss; k++ )
+    {
+        double sum_d2 = 0.0, var_threshold;
+        for( m = 0; m < nChannels; m++ )
+        {
+            double d = g_point->g_values[k].mean[m]- src_pixel[m];
+            sum_d2 += (d*d) / (g_point->g_values[k].variance[m] * g_point->g_values[k].variance[m]);
+        }  //difference < STD_LIMIT*STD_LIMIT or difference**2 < STD_LIMIT*STD_LIMIT*VAR
+        
+        var_threshold = bg_model_params->std_threshold*bg_model_params->std_threshold;
+        if( sum_d2 < var_threshold )
+        {
+            match[k] = 1;
+            matchPosition = k;
+            break;
+        }
+    }
+    
+    return matchPosition;
+}
+*/
+
+static void icvUpdateFullWindow( double* src_pixel, int nChannels, int* match,
+                                 CvGaussBGPoint* g_point,
+                                 const CvGaussBGStatModelParams *bg_model_params )
+{
+    const double learning_rate_weight = (1.0/(double)bg_model_params->win_size);
+    for(int k = 0; k < bg_model_params->n_gauss; k++){
+        g_point->g_values[k].weight = g_point->g_values[k].weight +
+            (learning_rate_weight*((double)match[k] -
+            g_point->g_values[k].weight));
+        if(match[k]){
+            double learning_rate_gaussian = (double)match[k]/(g_point->g_values[k].weight*
+                (double)bg_model_params->win_size);
+            for(int m = 0; m < nChannels; m++){
+                const double tmpDiff = src_pixel[m] - g_point->g_values[k].mean[m];
+                g_point->g_values[k].mean[m] = g_point->g_values[k].mean[m] +
+                    (learning_rate_gaussian * tmpDiff);
+                g_point->g_values[k].variance[m] = g_point->g_values[k].variance[m]+
+                    (learning_rate_gaussian*((tmpDiff*tmpDiff) - g_point->g_values[k].variance[m]));
+            }
+        }
+    }
+}
+
+
+static void icvUpdatePartialWindow( double* src_pixel, int nChannels, int* match, CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params )
+{
+    int k, m;
+    int window_current = 0;
+    
+    for( k = 0; k < bg_model_params->n_gauss; k++ )
+        window_current += g_point->g_values[k].match_sum;
+    
+    for( k = 0; k < bg_model_params->n_gauss; k++ )
+    {
+        g_point->g_values[k].match_sum += match[k];
+        double learning_rate_weight = (1.0/((double)window_current + 1.0)); //increased by one since sum
+        g_point->g_values[k].weight = g_point->g_values[k].weight +
+            (learning_rate_weight*((double)match[k] - g_point->g_values[k].weight));
+        
+        if( g_point->g_values[k].match_sum > 0 && match[k] )
+        {
+            double learning_rate_gaussian = (double)match[k]/((double)g_point->g_values[k].match_sum);
+            for( m = 0; m < nChannels; m++ )
+            {
+                const double tmpDiff = src_pixel[m] - g_point->g_values[k].mean[m];
+                g_point->g_values[k].mean[m] = g_point->g_values[k].mean[m] +
+                    (learning_rate_gaussian*tmpDiff);
+                g_point->g_values[k].variance[m] = g_point->g_values[k].variance[m]+
+                    (learning_rate_gaussian*((tmpDiff*tmpDiff) - g_point->g_values[k].variance[m]));
+            }
+        }
+    }
+}
+
+static void icvUpdateFullNoMatch( IplImage* gm_image, int p, int* match,
+                                  CvGaussBGPoint* g_point,
+                                  const CvGaussBGStatModelParams *bg_model_params)
+{
+    int k, m;
+    double alpha;
+    int match_sum_total = 0;
+
+    //new value of last one
+    g_point->g_values[bg_model_params->n_gauss - 1].match_sum = 1;
+    
+    //get sum of all but last value of match_sum
+    
+    for( k = 0; k < bg_model_params->n_gauss ; k++ )
+        match_sum_total += g_point->g_values[k].match_sum;
+    
+    g_point->g_values[bg_model_params->n_gauss - 1].weight = 1./(double)match_sum_total;
+    for( m = 0; m < gm_image->nChannels ; m++ )
+    {
+        // first pass mean is image value
+        g_point->g_values[bg_model_params->n_gauss - 1].variance[m] = bg_model_params->variance_init;
+        g_point->g_values[bg_model_params->n_gauss - 1].mean[m] = (unsigned char)gm_image->imageData[p + m];
+    }
+    
+    alpha = 1.0 - (1.0/bg_model_params->win_size);
+    for( k = 0; k < bg_model_params->n_gauss - 1; k++ )
+    {
+        g_point->g_values[k].weight *= alpha;
+        if( match[k] )
+            g_point->g_values[k].weight += alpha;
+    }
+}
+
+
+static void
+icvUpdatePartialNoMatch(double *pixel,
+                        int nChannels,
+                        int* /*match*/,
+                        CvGaussBGPoint* g_point,
+                        const CvGaussBGStatModelParams *bg_model_params)
+{
+    int k, m;
+    //new value of last one
+    g_point->g_values[bg_model_params->n_gauss - 1].match_sum = 1;
+    
+    //get sum of all but last value of match_sum
+    int match_sum_total = 0;
+    for(k = 0; k < bg_model_params->n_gauss ; k++)
+        match_sum_total += g_point->g_values[k].match_sum;
+
+    for(m = 0; m < nChannels; m++)
+    {
+        //first pass mean is image value
+        g_point->g_values[bg_model_params->n_gauss - 1].variance[m] = bg_model_params->variance_init;
+        g_point->g_values[bg_model_params->n_gauss - 1].mean[m] = pixel[m];
+    }
+    for(k = 0; k < bg_model_params->n_gauss; k++)
+    {
+        g_point->g_values[k].weight = (double)g_point->g_values[k].match_sum /
+            (double)match_sum_total;
+    }
+}
+
+static void icvGetSortKey( const int nChannels, double* sort_key, const CvGaussBGPoint* g_point,
+                           const CvGaussBGStatModelParams *bg_model_params )
+{
+    int k, m;
+    for( k = 0; k < bg_model_params->n_gauss; k++ )
+    {
+        // Avoid division by zero
+        if( g_point->g_values[k].match_sum > 0 )
+        {
+            // Independence assumption between components
+            double variance_sum = 0.0;
+            for( m = 0; m < nChannels; m++ )
+                variance_sum += g_point->g_values[k].variance[m];
+            
+            sort_key[k] = g_point->g_values[k].weight/sqrt(variance_sum);
+        }
+        else
+            sort_key[k]= 0.0;
+    }
+}
+
+
+static void icvBackgroundTest( const int nChannels, int n, int i, int j, int *match, CvGaussBGModel* bg_model )
+{
+    int m, b;
+    uchar pixelValue = (uchar)255; // will switch to 0 if match found
+    double weight_sum = 0.0;
+    CvGaussBGPoint* g_point = bg_model->g_point;
+    
+    for( m = 0; m < nChannels; m++)
+        bg_model->background->imageData[ bg_model->background->widthStep*i + j*nChannels + m]  = (unsigned char)(g_point[n].g_values[0].mean[m]+0.5);
+    
+    for( b = 0; b < bg_model->params.n_gauss; b++)
+    {
+        weight_sum += g_point[n].g_values[b].weight;
+        if( match[b] )
+            pixelValue = 0;
+        if( weight_sum > bg_model->params.bg_threshold )
+            break;
+    }
+    
+    bg_model->foreground->imageData[ bg_model->foreground->widthStep*i + j] = pixelValue;
+}
+
+/* End of file. */