| /*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. */ |