前面的模型定义还有参数的变量定义可以参考源文件background_segm.hpp 里面英文注释已经说得很清楚了 我就不再写了 这里只写文件cvbgfg_acmmm2003
论文是Foreground Object Detection from Videos Containing Complex Background,其实如果已经打算看这个源代码了,应该不会不知道这篇论文。
没有看过论文的不建议看代码,不知道作者理论来源的也不会有很大收获
为了易看,以插入代码的方式书写
论文是Foreground Object Detection from Videos Containing Complex Background,其实如果已经打算看这个源代码了,应该不会不知道这篇论文。
没有看过论文的不建议看代码,不知道作者理论来源的也不会有很大收获
为了易看,以插入代码的方式书写
- #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:
- /*下面为申请内存空间 此处方法与GMM的方法一样 即申请一个整块空间 然后给不同的点分配相应的入口地址
- 并非是各个点分别申请空间 因此后面访问的时候一定要注意相应点的边界问题不要越界 否则大部分访问是不会出错的 但是实际访问的是其他点的数据
- */
- 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 );
- /* 还要注意的是每个像素点均维护两种类型的表 即c和cc 表的个数由参数N2c和N2cc指定 */
- 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 -
- /*用两幅三通道图像做差分 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]; //对0-255每个灰度差值进行统计
- 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 c[PIXELRANGE];
- for (i=0 ; i<PIXELRANGE; i++) relativeVariance[i]=0; //方差初始化
- /*后面所写的x均值随机变量 不特指程序中的任何一个变量
- 以下每个差值的概率分布均是指累计概率分布 不过此处的随机变量是从255-0进行累计,并非通常的0-255
- 即为 p(X)=1-sum(p(x<X)) = sum(p(x>=X))
- 所以此处是对每一个差值均构建了概率分布 也就是并非一条概率曲线的存在 而是254-0 共有255条概率曲线存在 每条曲线确定一个差值的方差
- */
- 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]); //因为后面会除总的统计数count 因而此处可以看做求E(x)
- sqsum += double(j*j)*double(HISTOGRAM[j]); //E(x*x)
- 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); //方差 D(x) = E(x*x) - E(x)*E(x)
- // 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; //因为uchar表示0-255,这里可以认为对最大方差进行规格化.同时设定方差的下界
- 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;
- }