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 is based on the "An Improved Adaptive Background Mixture Model for
43// Real-time Tracking with Shadow Detection" by P. KaewTraKulPong and R. Bowden
44// http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf
45//
46// The windowing method is used, but not the shadow detection. I make some of my
47// own modifications which make more sense. There are some errors in some of their
48// equations.
49//
50//IplImage values of image that are useful
51//int  nSize;         /* sizeof(IplImage) */
52//int  depth;         /* pixel depth in bits: IPL_DEPTH_8U ...*/
53//int  nChannels;     /* OpenCV functions support 1,2,3 or 4 channels */
54//int  width;         /* image width in pixels */
55//int  height;        /* image height in pixels */
56//int  imageSize;     /* image data size in bytes in case of interleaved data)*/
57//char *imageData;    /* pointer to aligned image data */
58//char *imageDataOrigin; /* pointer to very origin of image -deallocation */
59//Values useful for gaussian integral
60//0.5 - 0.19146 - 0.38292
61//1.0 - 0.34134 - 0.68268
62//1.5 - 0.43319 - 0.86638
63//2.0 - 0.47725 - 0.95450
64//2.5 - 0.49379 - 0.98758
65//3.0 - 0.49865 - 0.99730
66//3.5 - 0.4997674 - 0.9995348
67//4.0 - 0.4999683 - 0.9999366
68
69#include "_cvaux.h"
70
71
72//internal functions for gaussian background detection
73static void icvInsertionSortGaussians( CvGaussBGPoint* g_point, double* sort_key, CvGaussBGStatModelParams *bg_model_params );
74
75/*
76   Test whether pixel can be explained by background model;
77   Return -1 if no match was found; otherwise the index in match[] is returned
78
79   icvMatchTest(...) assumes what all color channels component exhibit the same variance
80   icvMatchTest2(...) accounts for different variances per color channel
81 */
82static int icvMatchTest( double* src_pixel, int nChannels, int* match,
83                 const CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params );
84/*static int icvMatchTest2( double* src_pixel, int nChannels, int* match,
85                 const CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params );*/
86
87
88/*
89   The update procedure differs between
90      * the initialization phase (named *Partial* ) and
91      * the normal phase (named *Full* )
92   The initalization phase is defined as not having processed <win_size> frames yet
93 */
94static void icvUpdateFullWindow( double* src_pixel, int nChannels,
95                         int* match,
96                         CvGaussBGPoint* g_point,
97                         const CvGaussBGStatModelParams *bg_model_params );
98static void icvUpdateFullNoMatch( IplImage* gm_image, int p,
99                          int* match,
100                          CvGaussBGPoint* g_point,
101                          const CvGaussBGStatModelParams *bg_model_params);
102static void icvUpdatePartialWindow( double* src_pixel, int nChannels, int* match,
103                            CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params );
104static void icvUpdatePartialNoMatch( double* src_pixel, int nChannels,
105                             int* match,
106                             CvGaussBGPoint* g_point,
107                             const CvGaussBGStatModelParams *bg_model_params);
108
109
110static void icvGetSortKey( const int nChannels, double* sort_key, const CvGaussBGPoint* g_point,
111                    const CvGaussBGStatModelParams *bg_model_params );
112static void icvBackgroundTest( const int nChannels, int n, int i, int j, int *match, CvGaussBGModel* bg_model );
113
114static void CV_CDECL icvReleaseGaussianBGModel( CvGaussBGModel** bg_model );
115static int CV_CDECL icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel*  bg_model );
116
117//#define for if(0);else for
118
119//g = 1 for first gaussian in list that matches else g = 0
120//Rw is the learning rate for weight and Rg is leaning rate for mean and variance
121//Ms is the match_sum which is the sum of matches for a particular gaussian
122//Ms values are incremented until the sum of Ms values in the list equals window size L
123//SMs is the sum of match_sums for gaussians in the list
124//Rw = 1/SMs note the smallest Rw gets is 1/L
125//Rg = g/Ms for SMs < L and Rg = g/(w*L) for SMs = L
126//The list is maintained in sorted order using w/sqrt(variance) as a key
127//If there is no match the last gaussian in the list is replaced by the new gaussian
128//This will result in changes to SMs which results in changes in Rw and Rg.
129//If a gaussian is replaced and SMs previously equaled L values of Ms are computed from w
130//w[n+1] = w[n] + Rw*(g - w[n])   weight
131//u[n+1] = u[n] + Rg*(x[n+1] - u[n]) mean value Sg is sum n values of g
132//v[n+1] = v[n] + Rg*((x[n+1] - u[n])*(x[n+1] - u[n])) - v[n]) variance
133//
134
135CV_IMPL CvBGStatModel*
136cvCreateGaussianBGModel( IplImage* first_frame, CvGaussBGStatModelParams* parameters )
137{
138    CvGaussBGModel* bg_model = 0;
139
140    CV_FUNCNAME( "cvCreateGaussianBGModel" );
141
142    __BEGIN__;
143
144    double var_init;
145    CvGaussBGStatModelParams params;
146    int i, j, k, m, n;
147
148    //init parameters
149    if( parameters == NULL )
150      {                        /* These constants are defined in cvaux/include/cvaux.h: */
151        params.win_size      = CV_BGFG_MOG_WINDOW_SIZE;
152        params.bg_threshold  = CV_BGFG_MOG_BACKGROUND_THRESHOLD;
153
154        params.std_threshold = CV_BGFG_MOG_STD_THRESHOLD;
155        params.weight_init   = CV_BGFG_MOG_WEIGHT_INIT;
156
157        params.variance_init = CV_BGFG_MOG_SIGMA_INIT*CV_BGFG_MOG_SIGMA_INIT;
158        params.minArea       = CV_BGFG_MOG_MINAREA;
159        params.n_gauss       = CV_BGFG_MOG_NGAUSSIANS;
160    }
161    else
162    {
163        params = *parameters;
164    }
165
166    if( !CV_IS_IMAGE(first_frame) )
167        CV_ERROR( CV_StsBadArg, "Invalid or NULL first_frame parameter" );
168
169    CV_CALL( bg_model = (CvGaussBGModel*)cvAlloc( sizeof(*bg_model) ));
170    memset( bg_model, 0, sizeof(*bg_model) );
171    bg_model->type = CV_BG_MODEL_MOG;
172    bg_model->release = (CvReleaseBGStatModel)icvReleaseGaussianBGModel;
173    bg_model->update = (CvUpdateBGStatModel)icvUpdateGaussianBGModel;
174
175    bg_model->params = params;
176
177    //prepare storages
178    CV_CALL( bg_model->g_point = (CvGaussBGPoint*)cvAlloc(sizeof(CvGaussBGPoint)*
179        ((first_frame->width*first_frame->height) + 256)));
180
181    CV_CALL( bg_model->background = cvCreateImage(cvSize(first_frame->width,
182        first_frame->height), IPL_DEPTH_8U, first_frame->nChannels));
183    CV_CALL( bg_model->foreground = cvCreateImage(cvSize(first_frame->width,
184        first_frame->height), IPL_DEPTH_8U, 1));
185
186    CV_CALL( bg_model->storage = cvCreateMemStorage());
187
188    //initializing
189    var_init = 2 * params.std_threshold * params.std_threshold;
190    CV_CALL( bg_model->g_point[0].g_values =
191        (CvGaussBGValues*)cvAlloc( sizeof(CvGaussBGValues)*params.n_gauss*
192        (first_frame->width*first_frame->height + 128)));
193
194    for( i = 0, n = 0; i < first_frame->height; i++ )
195    {
196        for( j = 0; j < first_frame->width; j++, n++ )
197        {
198            const int p = i*first_frame->widthStep+j*first_frame->nChannels;
199
200            bg_model->g_point[n].g_values =
201                bg_model->g_point[0].g_values + n*params.n_gauss;
202            bg_model->g_point[n].g_values[0].weight = 1;    //the first value seen has weight one
203            bg_model->g_point[n].g_values[0].match_sum = 1;
204            for( m = 0; m < first_frame->nChannels; m++)
205            {
206                bg_model->g_point[n].g_values[0].variance[m] = var_init;
207                bg_model->g_point[n].g_values[0].mean[m] = (unsigned char)first_frame->imageData[p + m];
208            }
209            for( k = 1; k < params.n_gauss; k++)
210            {
211                bg_model->g_point[n].g_values[k].weight = 0;
212                bg_model->g_point[n].g_values[k].match_sum = 0;
213                for( m = 0; m < first_frame->nChannels; m++){
214                    bg_model->g_point[n].g_values[k].variance[m] = var_init;
215                    bg_model->g_point[n].g_values[k].mean[m] = 0;
216                }
217            }
218        }
219    }
220
221    bg_model->countFrames = 0;
222
223    __END__;
224
225    if( cvGetErrStatus() < 0 )
226    {
227        CvBGStatModel* base_ptr = (CvBGStatModel*)bg_model;
228
229        if( bg_model && bg_model->release )
230            bg_model->release( &base_ptr );
231        else
232            cvFree( &bg_model );
233        bg_model = 0;
234    }
235
236    return (CvBGStatModel*)bg_model;
237}
238
239
240static void CV_CDECL
241icvReleaseGaussianBGModel( CvGaussBGModel** _bg_model )
242{
243    CV_FUNCNAME( "icvReleaseGaussianBGModel" );
244
245    __BEGIN__;
246
247    if( !_bg_model )
248        CV_ERROR( CV_StsNullPtr, "" );
249
250    if( *_bg_model )
251    {
252        CvGaussBGModel* bg_model = *_bg_model;
253        if( bg_model->g_point )
254        {
255            cvFree( &bg_model->g_point[0].g_values );
256            cvFree( &bg_model->g_point );
257        }
258
259        cvReleaseImage( &bg_model->background );
260        cvReleaseImage( &bg_model->foreground );
261        cvReleaseMemStorage(&bg_model->storage);
262        memset( bg_model, 0, sizeof(*bg_model) );
263        cvFree( _bg_model );
264    }
265
266    __END__;
267}
268
269
270static int CV_CDECL
271icvUpdateGaussianBGModel( IplImage* curr_frame, CvGaussBGModel*  bg_model )
272{
273    int i, j, k, n;
274    int region_count = 0;
275    CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL;
276
277    bg_model->countFrames++;
278
279    for( i = 0, n = 0; i < curr_frame->height; i++ )
280    {
281        for( j = 0; j < curr_frame->width; j++, n++ )
282        {
283            int match[CV_BGFG_MOG_MAX_NGAUSSIANS];
284            double sort_key[CV_BGFG_MOG_MAX_NGAUSSIANS];
285            const int nChannels = curr_frame->nChannels;
286            const int p = curr_frame->widthStep*i+j*nChannels;
287
288            // A few short cuts
289            CvGaussBGPoint* g_point = &bg_model->g_point[n];
290            const CvGaussBGStatModelParams bg_model_params = bg_model->params;
291            double pixel[4];
292            int no_match;
293
294            for( k = 0; k < nChannels; k++ )
295                pixel[k] = (uchar)curr_frame->imageData[p+k];
296
297            no_match = icvMatchTest( pixel, nChannels, match, g_point, &bg_model_params );
298            if( bg_model->countFrames >= bg_model->params.win_size )
299            {
300                icvUpdateFullWindow( pixel, nChannels, match, g_point, &bg_model->params );
301                if( no_match == -1)
302                    icvUpdateFullNoMatch( curr_frame, p, match, g_point, &bg_model_params );
303            }
304            else
305            {
306                icvUpdatePartialWindow( pixel, nChannels, match, g_point, &bg_model_params );
307                if( no_match == -1)
308                    icvUpdatePartialNoMatch( pixel, nChannels, match, g_point, &bg_model_params );
309            }
310            icvGetSortKey( nChannels, sort_key, g_point, &bg_model_params );
311            icvInsertionSortGaussians( g_point, sort_key, (CvGaussBGStatModelParams *)&bg_model_params );
312            icvBackgroundTest( nChannels, n, i, j, match, bg_model );
313        }
314    }
315
316    //foreground filtering
317
318    //filter small regions
319    cvClearMemStorage(bg_model->storage);
320
321    //cvMorphologyEx( bg_model->foreground, bg_model->foreground, 0, 0, CV_MOP_OPEN, 1 );
322    //cvMorphologyEx( bg_model->foreground, bg_model->foreground, 0, 0, CV_MOP_CLOSE, 1 );
323
324    cvFindContours( bg_model->foreground, bg_model->storage, &first_seq, sizeof(CvContour), CV_RETR_LIST );
325    for( seq = first_seq; seq; seq = seq->h_next )
326    {
327        CvContour* cnt = (CvContour*)seq;
328        if( cnt->rect.width * cnt->rect.height < bg_model->params.minArea )
329        {
330            //delete small contour
331            prev_seq = seq->h_prev;
332            if( prev_seq )
333            {
334                prev_seq->h_next = seq->h_next;
335                if( seq->h_next ) seq->h_next->h_prev = prev_seq;
336            }
337            else
338            {
339                first_seq = seq->h_next;
340                if( seq->h_next ) seq->h_next->h_prev = NULL;
341            }
342        }
343        else
344        {
345            region_count++;
346        }
347    }
348    bg_model->foreground_regions = first_seq;
349    cvZero(bg_model->foreground);
350    cvDrawContours(bg_model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);
351
352    return region_count;
353}
354
355static void icvInsertionSortGaussians( CvGaussBGPoint* g_point, double* sort_key, CvGaussBGStatModelParams *bg_model_params )
356{
357    int i, j;
358    for( i = 1; i < bg_model_params->n_gauss; i++ )
359    {
360        double index = sort_key[i];
361        for( j = i; j > 0 && sort_key[j-1] < index; j-- ) //sort decending order
362        {
363            double temp_sort_key = sort_key[j];
364            sort_key[j] = sort_key[j-1];
365            sort_key[j-1] = temp_sort_key;
366
367            CvGaussBGValues temp_gauss_values = g_point->g_values[j];
368            g_point->g_values[j] = g_point->g_values[j-1];
369            g_point->g_values[j-1] = temp_gauss_values;
370        }
371//        sort_key[j] = index;
372    }
373}
374
375
376static int icvMatchTest( double* src_pixel, int nChannels, int* match,
377                         const CvGaussBGPoint* g_point,
378                         const CvGaussBGStatModelParams *bg_model_params )
379{
380    int k;
381    int matchPosition=-1;
382    for ( k = 0; k < bg_model_params->n_gauss; k++) match[k]=0;
383
384    for ( k = 0; k < bg_model_params->n_gauss; k++) {
385        double sum_d2 = 0.0;
386        double var_threshold = 0.0;
387        for(int m = 0; m < nChannels; m++){
388            double d = g_point->g_values[k].mean[m]- src_pixel[m];
389            sum_d2 += (d*d);
390            var_threshold += g_point->g_values[k].variance[m];
391        }  //difference < STD_LIMIT*STD_LIMIT or difference**2 < STD_LIMIT*STD_LIMIT*VAR
392        var_threshold = bg_model_params->std_threshold*bg_model_params->std_threshold*var_threshold;
393        if(sum_d2 < var_threshold){
394            match[k] = 1;
395            matchPosition = k;
396            break;
397        }
398    }
399
400    return matchPosition;
401}
402
403/*
404static int icvMatchTest2( double* src_pixel, int nChannels, int* match,
405                          const CvGaussBGPoint* g_point,
406                          const CvGaussBGStatModelParams *bg_model_params )
407{
408    int k, m;
409    int matchPosition=-1;
410
411    for( k = 0; k < bg_model_params->n_gauss; k++ )
412        match[k] = 0;
413
414    for( k = 0; k < bg_model_params->n_gauss; k++ )
415    {
416        double sum_d2 = 0.0, var_threshold;
417        for( m = 0; m < nChannels; m++ )
418        {
419            double d = g_point->g_values[k].mean[m]- src_pixel[m];
420            sum_d2 += (d*d) / (g_point->g_values[k].variance[m] * g_point->g_values[k].variance[m]);
421        }  //difference < STD_LIMIT*STD_LIMIT or difference**2 < STD_LIMIT*STD_LIMIT*VAR
422
423        var_threshold = bg_model_params->std_threshold*bg_model_params->std_threshold;
424        if( sum_d2 < var_threshold )
425        {
426            match[k] = 1;
427            matchPosition = k;
428            break;
429        }
430    }
431
432    return matchPosition;
433}
434*/
435
436static void icvUpdateFullWindow( double* src_pixel, int nChannels, int* match,
437                                 CvGaussBGPoint* g_point,
438                                 const CvGaussBGStatModelParams *bg_model_params )
439{
440    const double learning_rate_weight = (1.0/(double)bg_model_params->win_size);
441    for(int k = 0; k < bg_model_params->n_gauss; k++){
442        g_point->g_values[k].weight = g_point->g_values[k].weight +
443            (learning_rate_weight*((double)match[k] -
444            g_point->g_values[k].weight));
445        if(match[k]){
446            double learning_rate_gaussian = (double)match[k]/(g_point->g_values[k].weight*
447                (double)bg_model_params->win_size);
448            for(int m = 0; m < nChannels; m++){
449                const double tmpDiff = src_pixel[m] - g_point->g_values[k].mean[m];
450                g_point->g_values[k].mean[m] = g_point->g_values[k].mean[m] +
451                    (learning_rate_gaussian * tmpDiff);
452                g_point->g_values[k].variance[m] = g_point->g_values[k].variance[m]+
453                    (learning_rate_gaussian*((tmpDiff*tmpDiff) - g_point->g_values[k].variance[m]));
454            }
455        }
456    }
457}
458
459
460static void icvUpdatePartialWindow( double* src_pixel, int nChannels, int* match, CvGaussBGPoint* g_point, const CvGaussBGStatModelParams *bg_model_params )
461{
462    int k, m;
463    int window_current = 0;
464
465    for( k = 0; k < bg_model_params->n_gauss; k++ )
466        window_current += g_point->g_values[k].match_sum;
467
468    for( k = 0; k < bg_model_params->n_gauss; k++ )
469    {
470        g_point->g_values[k].match_sum += match[k];
471        double learning_rate_weight = (1.0/((double)window_current + 1.0)); //increased by one since sum
472        g_point->g_values[k].weight = g_point->g_values[k].weight +
473            (learning_rate_weight*((double)match[k] - g_point->g_values[k].weight));
474
475        if( g_point->g_values[k].match_sum > 0 && match[k] )
476        {
477            double learning_rate_gaussian = (double)match[k]/((double)g_point->g_values[k].match_sum);
478            for( m = 0; m < nChannels; m++ )
479            {
480                const double tmpDiff = src_pixel[m] - g_point->g_values[k].mean[m];
481                g_point->g_values[k].mean[m] = g_point->g_values[k].mean[m] +
482                    (learning_rate_gaussian*tmpDiff);
483                g_point->g_values[k].variance[m] = g_point->g_values[k].variance[m]+
484                    (learning_rate_gaussian*((tmpDiff*tmpDiff) - g_point->g_values[k].variance[m]));
485            }
486        }
487    }
488}
489
490static void icvUpdateFullNoMatch( IplImage* gm_image, int p, int* match,
491                                  CvGaussBGPoint* g_point,
492                                  const CvGaussBGStatModelParams *bg_model_params)
493{
494    int k, m;
495    double alpha;
496    int match_sum_total = 0;
497
498    //new value of last one
499    g_point->g_values[bg_model_params->n_gauss - 1].match_sum = 1;
500
501    //get sum of all but last value of match_sum
502
503    for( k = 0; k < bg_model_params->n_gauss ; k++ )
504        match_sum_total += g_point->g_values[k].match_sum;
505
506    g_point->g_values[bg_model_params->n_gauss - 1].weight = 1./(double)match_sum_total;
507    for( m = 0; m < gm_image->nChannels ; m++ )
508    {
509        // first pass mean is image value
510        g_point->g_values[bg_model_params->n_gauss - 1].variance[m] = bg_model_params->variance_init;
511        g_point->g_values[bg_model_params->n_gauss - 1].mean[m] = (unsigned char)gm_image->imageData[p + m];
512    }
513
514    alpha = 1.0 - (1.0/bg_model_params->win_size);
515    for( k = 0; k < bg_model_params->n_gauss - 1; k++ )
516    {
517        g_point->g_values[k].weight *= alpha;
518        if( match[k] )
519            g_point->g_values[k].weight += alpha;
520    }
521}
522
523
524static void
525icvUpdatePartialNoMatch(double *pixel,
526                        int nChannels,
527                        int* /*match*/,
528                        CvGaussBGPoint* g_point,
529                        const CvGaussBGStatModelParams *bg_model_params)
530{
531    int k, m;
532    //new value of last one
533    g_point->g_values[bg_model_params->n_gauss - 1].match_sum = 1;
534
535    //get sum of all but last value of match_sum
536    int match_sum_total = 0;
537    for(k = 0; k < bg_model_params->n_gauss ; k++)
538        match_sum_total += g_point->g_values[k].match_sum;
539
540    for(m = 0; m < nChannels; m++)
541    {
542        //first pass mean is image value
543        g_point->g_values[bg_model_params->n_gauss - 1].variance[m] = bg_model_params->variance_init;
544        g_point->g_values[bg_model_params->n_gauss - 1].mean[m] = pixel[m];
545    }
546    for(k = 0; k < bg_model_params->n_gauss; k++)
547    {
548        g_point->g_values[k].weight = (double)g_point->g_values[k].match_sum /
549            (double)match_sum_total;
550    }
551}
552
553static void icvGetSortKey( const int nChannels, double* sort_key, const CvGaussBGPoint* g_point,
554                           const CvGaussBGStatModelParams *bg_model_params )
555{
556    int k, m;
557    for( k = 0; k < bg_model_params->n_gauss; k++ )
558    {
559        // Avoid division by zero
560        if( g_point->g_values[k].match_sum > 0 )
561        {
562            // Independence assumption between components
563            double variance_sum = 0.0;
564            for( m = 0; m < nChannels; m++ )
565                variance_sum += g_point->g_values[k].variance[m];
566
567            sort_key[k] = g_point->g_values[k].weight/sqrt(variance_sum);
568        }
569        else
570            sort_key[k]= 0.0;
571    }
572}
573
574
575static void icvBackgroundTest( const int nChannels, int n, int i, int j, int *match, CvGaussBGModel* bg_model )
576{
577    int m, b;
578    uchar pixelValue = (uchar)255; // will switch to 0 if match found
579    double weight_sum = 0.0;
580    CvGaussBGPoint* g_point = bg_model->g_point;
581
582    for( m = 0; m < nChannels; m++)
583        bg_model->background->imageData[ bg_model->background->widthStep*i + j*nChannels + m]  = (unsigned char)(g_point[n].g_values[0].mean[m]+0.5);
584
585    for( b = 0; b < bg_model->params.n_gauss; b++)
586    {
587        weight_sum += g_point[n].g_values[b].weight;
588        if( match[b] )
589            pixelValue = 0;
590        if( weight_sum > bg_model->params.bg_threshold )
591            break;
592    }
593
594    bg_model->foreground->imageData[ bg_model->foreground->widthStep*i + j] = pixelValue;
595}
596
597/* End of file. */
598