X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=cvaux%2Fsrc%2Fcvbgfg_acmmm2003.cpp;h=bcb03b32da6e8fdbe24708185536426b34a6542a;hb=80cd7b93506cc1926882d5fd08a2c74ee9359e29;hp=7a7d864f7b206c28411681ba5a5f7330a3c960f1;hpb=467a270adf12425827305759c0c4ea8f5b2b3854;p=opencv diff --git a/cvaux/src/cvbgfg_acmmm2003.cpp b/cvaux/src/cvbgfg_acmmm2003.cpp index 7a7d864..bcb03b3 100644 --- a/cvaux/src/cvbgfg_acmmm2003.cpp +++ b/cvaux/src/cvbgfg_acmmm2003.cpp @@ -38,6 +38,15 @@ // //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 @@ -48,9 +57,12 @@ static double* _cv_max_element( double* start, double* end ) { double* p = start++; - for( ; start != end; start++ ) - if( *p < *start ) - p = start; + + for( ; start != end; ++start) { + + if (*p < *start) p = start; + } + return p; } @@ -58,7 +70,7 @@ static void CV_CDECL icvReleaseFGDStatModel( CvFGDStatModel** model ); static int CV_CDECL icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ); -// Function cvCreateFGDStatModel initializes foreground detection process +// 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 @@ -81,23 +93,28 @@ cvCreateFGDStatModel( IplImage* first_frame, CvFGDStatModelParams* parameters ) if (first_frame->nChannels != 3) CV_ERROR( CV_StsBadArg, "first_frame must have 3 color channels" ); - //init parameters + // 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.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; + params.perform_morphing = 1; } else { @@ -111,8 +128,8 @@ cvCreateFGDStatModel( IplImage* first_frame, CvFGDStatModelParams* parameters ) p_model->update = (CvUpdateBGStatModel)icvUpdateFGDStatModel;; p_model->params = params; - //init storages - pixel_count = first_frame->width*first_frame->height; + // 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) ); @@ -126,19 +143,20 @@ cvCreateFGDStatModel( IplImage* first_frame, CvFGDStatModelParams* parameters ) 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++ ) + 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 + // 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->background = cvCloneImage(first_frame)); CV_CALL( p_model->prev_frame = cvCloneImage(first_frame)); CV_CALL( p_model->storage = cvCreateMemStorage()); @@ -205,16 +223,29 @@ cvChangeDetection( IplImage* prev_frame, 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 || - !CV_ARE_SIZES_EQ( prev_frame, curr_frame ) || !CV_ARE_SIZES_EQ( prev_frame, change_mask ) ) return 0; + 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 ; bnChannels ; b++) { - // create histogram + + // Create histogram: long HISTOGRAM[PIXELRANGE]; for (i=0 ; istorage); cvZero(model->foreground); - //form FG pixels candidates using image differencing with adaptive threshold [P.Rosin, Thresholding for change detection, ICCV, 1998 ] + // 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 ); @@ -319,23 +357,31 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) { if( ((uchar*)model->Fbd->imageData)[i*mask_step+j] || ((uchar*)model->Ftd->imageData)[i*mask_step+j] ) { - float Pb=0, Pv=0, Pvb=0; + float Pb = 0; + float Pv = 0; + float Pvb = 0; + CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j; - CvBGPixelCStatTable* ctable = stat->ctable; + + 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; + 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? + + // 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( !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 && @@ -354,8 +400,8 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } 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++ ) + // 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 && @@ -368,24 +414,28 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) Pb = stat->Pbc; if( 2 * Pvb * Pb <= Pv ) val = 1; } - //update FG + + // 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 if( change detection... + } // for j... + } // for i... //end BG/FG classification - //foreground segmentation - //smooth FG map + // Foreground segmentation. + // Smooth foreground map: if( model->params.perform_morphing ){ - cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_OPEN, 1 ); - cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_CLOSE, 1 ); + 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 ){ - //filter small regions + + // 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 ) { @@ -393,7 +443,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) if( cnt->rect.width * cnt->rect.height < model->params.minArea || (model->params.is_obj_without_holes && CV_IS_SEQ_HOLE(seq)) ) { - //delete small contour + // Delete under-size contour: prev_seq = seq->h_prev; if( prev_seq ) { @@ -414,12 +464,13 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) 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{ + + } else { + model->foreground_regions = NULL; } - //check ALL BG update condition + // 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++ ) @@ -431,7 +482,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } - //update BG model + // Update background model: for( i = 0; i < model->Ftd->height; i++ ) { for( j = 0; j < model->Ftd->width; j++ ) @@ -456,7 +507,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) stat->Pbcc += alpha; } - // find best Vi match + // Find best Vi match: for(k = 0; PV_CC(k) && k < model->params.N2cc; k++ ) { // Exponential decay of memory @@ -488,7 +539,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) if( indx < 0 ) - {//N2th elem in the table is replaced by a new features + { // Replace N2th elem in the table by new feature: indx = model->params.N2cc - 1; PV_CC(indx) = alpha; PVB_CC(indx) = alpha; @@ -500,7 +551,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } } else - {//update + { // Update: PV_CC(indx) += alpha; if( !((uchar*)model->foreground->imageData)[i*mask_step+j] ) { @@ -536,7 +587,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) if( sum1 > model->params.T ) stat->is_trained_dyn_model = 1; diff = sum1 - stat->Pbcc * sum2; - //update stat table + // Update stat table: if( diff > model->params.T ) { //printf("once off change at motion mode\n"); @@ -550,7 +601,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } } - //case of stational pixel + // 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; @@ -627,7 +678,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } } - //check "once-off" changes + // Check "once-off" changes: float sum1=0, sum2=0; for( k = 0; PV_C(k) && k < model->params.N1c; k++ ) { @@ -637,7 +688,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) diff = sum1 - stat->Pbc * sum2; if( sum1 > model->params.T ) stat->is_trained_st_model = 1; - //update stat table + // Update stat table: if( diff > model->params.T ) { //printf("once off change at stat mode\n"); @@ -648,9 +699,9 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } stat->Pbc = 1 - stat->Pbc; } - }//if !(change detection) at pixel (i,j) + } // if !(change detection) at pixel (i,j) - //update the reference BG image + // 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; @@ -658,7 +709,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] && !((uchar*)model->Fbd->imageData)[i*mask_step+j] ) { - //apply IIR filter + // Apply IIR filter: for( l = 0; l < 3; l++ ) { int a = cvRound(ptr[l]*(1 - model->params.alpha1) + model->params.alpha1*curr_data[l]); @@ -669,7 +720,7 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } else { - //background change is detected + // Background change detected: for( l = 0; l < 3; l++ ) { //((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] = curr_data[l]; @@ -677,10 +728,10 @@ icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model ) } } } - }//j - }//i + } // j + } // i - //keep prev frame + // Keep previous frame: cvCopy( curr_frame, model->prev_frame ); return region_count;