1. 程式人生 > >背景建模之高斯混合模型

背景建模之高斯混合模型

Flag Counter

在運動目標檢測提取中,背景目標對於目標的識別和跟蹤至關重要。而建模正是背景目標提取的一個重要環節。

前景是指在假設背景為靜止的情況下,任何有意義的運動物體即為前景。

運動物體檢測的問題主要分為兩類,攝像機固定和攝像機運動。對於攝像機運動的運動物體檢測問題,比較著名的解決方案是光流法,通過求解偏微分方程求的影象序列的光流場,從而預測攝像機的運動狀態。對於攝像機固定的情形,當然也可以用光流法,但是由於光流法的複雜性,往往難以實時的計算。而混合高斯背景建模適合於在攝像機固定的情況下從影象序列中分離出背景和前景。在攝像機固定的情況下,背景的變化是緩慢的,而且大都是光照,風等等的影響,通過對背景建模,對一幅給定影象分離前景和背景,一般來說,前景就是運動物體,從而達到運動物體檢測的目的。

混合高斯模型已經廣泛應用於魯棒的複雜場景背景建模,特別是在有微小重複運動的場合,如搖動的樹葉、灌木叢、旋轉的風扇、海面波濤、雨雪天氣、光線反射等。基於畫素的混合高斯模型對多峰分佈背景進行建模很有效,能適應背景的變化如光線漸變,並能基本滿足實際應用中對演算法的實時性要求。

演算法基本思想

用GMM對背景建模的基本思想是把每一個畫素點所呈現的顏色用K個高斯分佈的疊加來表示,通常K取3-5之間。將畫素點所呈現的顏色X認為是隨機變數,則在每時刻t=1,…,T所得到視訊幀影象的畫素值只是隨機變數X的取樣值(觀值)。

在進行前景檢測前,先對背景進行訓練,對每一幀影象中每個背景採用一個混合高斯模型進行模擬,背景一旦提取出來,前景的檢測就簡單了,檢查畫素是否與背景的高斯模型匹配,匹配是背景,不匹配就是前景。

所以關鍵就是混合高斯背景模型的建立。

GMM之所以能夠將前景和背景分開是基於如下兩點事實的:
(1)在長期觀測的場景中,背景佔大多數時間,更多的資料是支援背景分佈的
(2)即使是相對顏色一致的運動物體也會比背景產生更多變化,況且一般情況下物體都是帶有不同顏色的

演算法大致這樣:

首先初始化預先定義的幾個高斯模型,對高斯模型中的引數進行初始化,並求出之後將要用到的引數。其次,對於每一幀中的每一個畫素進行處理,看其是否匹配某個模型,若匹配,則將其歸入該模型中,並對該模型根據新的畫素值進行新,若不匹配,則以該畫素建立一個高斯模型,初始化引數,代替原有模型中最不可能的模型。最後選擇前面幾個最有可能的模型作為背景模型,為背景目標提取做鋪墊。

演算法原理

背景影象的每一個畫素分別用由K個高斯分佈構成的混合高斯模型來建模, 即:


Xj表示畫素j在t時刻的取值,如果畫素為RGB三通道,則xj為向量,xj=[xjR xjG xjB],表示時刻t混合高斯模型中第i個高斯分佈的權係數的估計值,分別表示時刻t混合高斯模型中第i個高斯分佈的均值向量和協方差矩陣(此處假定畫素的紅、綠、藍分量相互獨立);η表示高斯分佈概率密度函式。




引數初始化

在第一幀影象時每個畫素對應的第一個高斯分佈進行初始化,均值賦為當前畫素的值,權值賦為1,除第一以外的高斯分佈函式的均值、權值和都初始化零。

引數更新

在時刻t對影象幀的每個畫素Xt與它對應的高斯模型進行匹配,

匹配規則為:

如果畫素值Xt 與混合高斯模型中第i個高斯分佈Gi均值的距離小於其標準差的2.5倍, 則定義該高斯分佈Gi 與畫素值Xt 匹配。

如果檢驗出該畫素混合高斯模型中至少有一個高斯分佈與畫素值Xt 匹配, 那麼混合高斯模型的引數更新規則為:

1)對於不匹配的高斯分佈, 它們的均值μ和協方差矩陣保持不變;

2)匹配的高斯分佈Gi 的均值μ和協方差矩陣按下式更新:



α為引數估計的學習速率。

如果該畫素對應的混合高斯模型中沒有高斯分佈與畫素值Xt 匹配, 那麼將最不可能代表背景過程的高斯分佈Gj重新賦值, 即


式中, W0 和V0是預先給定的正值;I為一個3 ×3單位矩陣。

然後按下式更新所有K 個高斯分佈在時刻t的權係數ωi,t:


式中, 如果高斯分佈Gi與t時刻畫素值Xt匹配, 則Mi,t取值1,否則取值為0。後一式完成權重的歸一化,保證權重和為1。

背景模型估計及運動分割

影象幀中每個畫素的混合高斯模型的引數更新後, 要確定混合高斯模型中哪些高斯分佈是由背景過程產生的, 或者說能最佳描述背景過程。
對於K個分佈,必須估計出其中哪些代表背景模型.在實際應用中,處理完當前幀後,模型的引數要進行必要的更新,所以每一幀都要進行背景模型的重新估計。一般的,我們會對有較多資料支援且方差較小的高斯分佈感興趣,因為它們代表背景模型的可能性較大。考慮兩種情況,一是當背景物體是持久靜止的時候,這時該物體表面產生的高斯分佈代表著背景分佈,那麼支援這個分佈的資料會持續累積,而且它的方差也會越來越小。第二種情況,當一個新的物體遮擋了原來的背景物體時,一般會導致兩種結果:要麼產生一個新的分佈,要麼把一個己存在的分佈的方差增大。另外,當新的物體是一個運動物體時,它一般也會比背景畫素保持更大的變化直到它停下來。從以上兩種情況可以看出,影響一個分佈是否背景分佈的重要因素有兩個: (1)該分佈產生的資料所佔的比例大小 (2)該分佈的方差大小 基於這兩個因素,採取如下的方法進行估計:

首先按ωk / |k|的比值由大到小將組成每個畫素混合高斯模型的K 個高斯分佈排序, ωk代表了第k個分佈產生的資料所佔的比例,k代表第k個分佈的方差,ωk / |k|的值越大,第k個分佈是背景模型的可能性就越大。一個分佈的權值增大或方差減小,都可以使ωk / |k|的值增大。

那麼最有可能描述穩定背景過程的高斯分佈將位於序列的頂部, 而由背景暫態擾動產生的分佈將滑向序列的底部, 最終被新賦值的高斯分佈所取代。

這樣, 我們選擇上述序列中前B 個高斯分佈作為背景畫素模型:


式中, T為預定的閾值(0. 5≤ T ≤ 1);B的含義是, 排序後的K個高斯分佈中的前B 個高斯分佈是該背景畫素的最佳描述。

前景(運動)分割

現在, 從新檢驗t時刻每一個畫素值Xt與其得到的前B 個高斯分佈的匹配關係, 如果該畫素值Xt與前B個高斯分佈之一匹配, 則該畫素為背景點, 否則該畫素被分類為前景, 即運動物體。這樣, 就完成了將運動物體從背景中分割出來的任務。

<span style="font-size:14px;">////////////////////////cvCreateGaussianBGModel///////////////////////////////////////////
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 )   
      {                       
        params.win_size      = CV_BGFG_MOG_WINDOW_SIZE;   // 初始化階段的幀數;使用者自定義模型學 習率a=1/win_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;    //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;    //權值賦為1
            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;
                }
            }
        }
    } //g_point[]:畫素,g_values[]:高斯分佈函式,mean[]:通道
   
    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;
}
 

cvUpdateBGStatModel(videoFrame,bgModel);
typedef int (CV_CDECL * CvUpdateBGStatModel)( IplImage* curr_frame, struct CvBGStatModel* bg_model );

/////////////////////////cvUpdateBGStatModel//////////////////////////////////
//函式功能:背景模型的更新,不僅要更新高斯分佈函式的引數,還要更新各高斯函式的權重
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];   //對高斯函式做標記,match[m]=1表示函式m為匹配的高斯分佈函式
            double sort_key[CV_BGFG_MOG_MAX_NGAUSSIANS]; //此陣列存貯每個高斯函式的均值與方差比值
            const int nChannels = curr_frame->nChannels;
            const int p = curr_frame->widthStep*i+j*nChannels;
            
           CvGaussBGPoint* g_point = &bg_model->g_point[n];
            const CvGaussBGStatModelParams bg_model_params = bg_model->params;
            double pixel[4];   // pixel[]存貯當前畫素的各通道RGB值
            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;
}
 
/////////////////////////////////////icvMatchTest//////////////////////////////////////////
//函式功能:拿當前畫素的值與已存在的高斯分佈函式比較,查詢是否存在匹配的的高斯分佈函式,如果有則返回 k值(高斯分佈函式的序號)
static int icvMatchTest( double* src_pixel, int nChannels, int* match,
                         const CvGaussBGPoint* g_point,
                         const CvGaussBGStatModelParams *bg_model_params )
{    //引數的傳遞:src_pixel為piexl[]:即當前畫素的各通道值
    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]; //通道m的原始模型值與當前畫素的值之差
            sum_d2 += (d*d);
            var_threshold += g_point->g_values[k].variance[m];
        }  
      
       //當前sum_d2為d0,d1,d2的平方和,var_threshold的值為畫素各通道方差之和
        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; //如果和第k個高斯函式匹配,則終止與後續函式的匹配
        }
    }
   
    return matchPosition;
}
 
///////////////////////icvUpdateFullWindow////////////////////////////////////////
//函式功能:更新每個高斯分佈的權值(對匹配的高斯函式k加大權值,其餘的則減小權值),如果前面的結果中存在匹配的高斯分佈函式k,則需要再對第k個高斯分佈函式的均值mean和方差variance做修正
static void icvUpdateFullWindow( double* src_pixel, int nChannels, int* match,
                                 CvGaussBGPoint* g_point,
                                 const CvGaussBGStatModelParams *bg_model_params )
{ //引數的傳遞:src_pixel為piexl[]:即當前幀中該畫素的RGB值
    const double learning_rate_weight = (1.0/(double)bg_model_params->win_size); //使用者自定義模型學習率a
    for(int k = 0; k < bg_model_params->n_gauss; k++)
{  
   //對每個高斯分佈的權值做修正:w=(1-a)w+a*m (a:模型學習率,m是匹配,匹配就是1,不匹配就是0)
        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]) //如果存在匹配的高斯分佈函式k(當前畫素為背景畫素),則需要再對第k個高斯分佈函式的均值mean和方差variance更新
   {
            double learning_rate_gaussian = (double)match[k]/(g_point->g_values[k].weight*
                (double)bg_model_params->win_size); //引數學習率p(p=a/w)
            for(int m = 0; m < nChannels; m++)
    {   //引數更新公式:u=(1-p)*u0+p*x; o*o=(1-p)*o*o+p*tmpDiff*tmpDiff
                const double tmpDiff = src_pixel[m] - g_point->g_values[k].mean[m]; //當前畫素的通道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]));
            }
        }
    }
}
 
 
 

/////////////////////////icvUpdatePartialWindow/////////////////////////////
//函式功能:對所有的高斯分佈函式做更新.至少每個高斯分佈的權值必須修正,如果前面的結果中存在匹配的高斯分佈函式k,則需要再對第k個高斯分佈函式的match_sum修改,最終對那些匹配的高斯分佈函式k的引數match_sum>0的做均值mean和方差variance修正
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;   //window_current為k個高斯分佈函式的match_sum值之和
   
    for( k = 0; k < bg_model_params->n_gauss; k++ )
    {
        g_point->g_values[k].match_sum += match[k]; //修正匹配的高斯分佈函式k的match_sum值
        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]));
            }
        }
    }
}
 
//////////////////////////icvUpdateFullNoMatch//////////////////////////
//函式功能:當所有的高斯函式均不匹配時,說明有新的分佈出現,需要將原高斯函式中sort_key最小的替換為新的高斯函式(權值小,方差大),其餘的高斯函式對應的只需更新權值
static void icvUpdateFullNoMatch( IplImage* gm_image, int p, int* match,
                                  CvGaussBGPoint* g_point,
                                  const CvGaussBGStatModelParams *bg_model_params)
{ //引數的傳遞:gm_image為當前幀curr_frame
    int k, m;
    double alpha;
    int match_sum_total = 0;
    g_point->g_values[bg_model_params->n_gauss - 1].match_sum = 1;    //將新的高斯分佈函式的match_sum置為1
   
   
    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; //要給新的高斯分佈函式賦一個較小的權值
    //將新的高斯分佈函式的variance[m]全部置為variance_init;mean[m]的值置為當前畫素各通道的值
    for( m = 0; m < gm_image->nChannels ; m++ )
    {
        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];
    }
    
//對其他的高斯分佈函式做權值更新:w=(1-a)*w+a*m (a:模型學習率,m是匹配,匹配就是1,不匹配就是0)
    alpha = 1.0 - (1.0/bg_model_params->win_size);   //alpha=1-a;
    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;
    }
}
 
 
////////////////////////////icvUpdatePartialNoMatch////////////////////////////////
static void
icvUpdatePartialNoMatch(double *pixel,
                        int nChannels,
                        int* ,
                        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;
    }
}
 

/////////////////////////////////icvGetSortKey///////////////////////////////////
//函式功能:計算各個高斯分佈weight/sqrt(variance_sum)的值,後面將對該值進行排序(該值越大則表示背景的可能性就越大)
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);     //sort_key=w/(o*o)
        }
        else
            sort_key[k]= 0.0;
    }
}
 

//////////////////////////////icvInsertionSortGaussians////////////////////////////
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_key[]按降序排序
        {
            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;
    }
}
 
///////////////////////////////////icvBackgroundTest/////////////////////////
static void icvBackgroundTest( const int nChannels, int n, int i, int j, int *match, CvGaussBGModel* bg_model )
{
    int m, b;
    uchar pixelValue = (uchar)255;   // 畫素預設都為前景
    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為真,說明該畫素已與某高斯函式匹配,該畫素為背景
        if( weight_sum > bg_model->params.bg_threshold )  
            break; //如果if語句為真,則前b個高斯分佈被選為描述背景的函式
    }
   
    bg_model->foreground->imageData[ bg_model->foreground->widthStep*i + j] = pixelValue;
}</span>

下面是我的檢測結果,分別為原視訊,前景和背景


論文:An Improved Adaptive Background Mixture Model for Real-time Tracking with Shadow Detection