BackgroundSubtractorMOG部分代码

1、Opencv的帮助文档地址:http://docs.opencv.org/2.4/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractormog#backgroundsubtractormog

2、关键函数
2.1、BackgroundSubtractorMOG::BackgroundSubtractorMOG(int _history, int _nmixtures,
double _backgroundRatio,
double _noiseSigma)
2.2、void BackgroundSubtractorMOG::operator()(InputArray _image, OutputArray _fgmask, double learningRate)

3、几点说明:
3.1、这个参数int _history 的用途。仅仅用于第一帧学习率,或者在学习率为负值时的学习率计算,其他地方没有使用
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./min( nframes, history );

3.2、 _backgroundRatio的用途,是个阈值,用于判断前多少个model作为背景模型。是前n个模型权重的和的比较阈值。具体可以参考下面代码注释部分

**代码摘自opencv


//backgroundRatio : 通过代码可知,就是k个模型的权重累加值>= backgroundRatio ,此时前k个model就是背景的model,大于k的model就是前景的model
//nmixtures : model数量
static void process8uC1( const Mat& image, Mat& fgmask, double learningRate,
                         Mat& bgmodel, int nmixtures, double backgroundRatio,
                         double varThreshold, double noiseSigma )
{
    int x, y, k, k1, rows = image.rows, cols = image.cols;
    float alpha = (float)learningRate, T = (float)backgroundRatio, vT = (float)varThreshold;
    int K = nmixtures;
    MixData<float>* mptr = (MixData<float>*)bgmodel.data;

    const float w0 = (float)defaultInitialWeight;
    const float sk0 = (float)(w0/(defaultNoiseSigma*2));
    const float var0 = (float)(defaultNoiseSigma*defaultNoiseSigma*4);
    const float minVar = (float)(noiseSigma*noiseSigma);

    for( y = 0; y < rows; y++ )
    {
        const uchar* src = image.ptr<uchar>(y);
        uchar* dst = fgmask.ptr<uchar>(y);

        if( alpha > 0 ) 
        {
            // 考虑更新模型的情况。
            for( x = 0; x < cols; x++, mptr += K )
            {
                float wsum = 0;
                float pix = src[x];
                int kHit = -1, kForeground = -1;

                // K:是高斯分布数量
                for( k = 0; k < K; k++ )
                {
                    float w = mptr[k].weight;
                    wsum += w;
                    if( w < FLT_EPSILON ) // 由于mptr是按权重降序排列的,因此权重太小的情况就结束循环
                        break;
                    float mu = mptr[k].mean;
                    float var = mptr[k].var;
                    float diff = pix - mu;
                    float d2 = diff*diff;
                    if( d2 < vT*var ) //同样判断是否是匹配的分布
                    {
                        wsum -= w; // 这里得到的wsum是前面k-1个不匹配的分布的权重的累加和
                        float dw = alpha*(1.f - w); //
                        mptr[k].weight = w + dw; //更新权重
                        mptr[k].mean = mu + alpha*diff; // 更新均值
                        var = max(var + alpha*(d2 - var), minVar); // 更新方差
                        mptr[k].var = var;
                        mptr[k].sortKey = w/sqrt(var); // 排序的关键字,为什么?

                        for( k1 = k-1; k1 >= 0; k1-- ) // 重新排序,通过交换的方式,将该高斯分布适当前移
                        {
                            if( mptr[k1].sortKey >= mptr[k1+1].sortKey )
                                break;
                            std::swap( mptr[k1], mptr[k1+1] );
                        }

                        kHit = k1+1;
                        break;
                    }
                }

                if( kHit < 0 ) // no appropriate gaussian mixture found at all, remove the weakest mixture and create a new one
                {
                    kHit = k = min(k, K-1);
                    wsum += w0 - mptr[k].weight;
                    mptr[k].weight = w0;
                    mptr[k].mean = pix;
                    mptr[k].var = var0;
                    mptr[k].sortKey = sk0;
                }
                else
                    for( ; k < K; k++ )
                        wsum += mptr[k].weight;

                float wscale = 1.f/wsum;
                wsum = 0;
                for( k = 0; k < K; k++ )
                {
                    wsum += mptr[k].weight *= wscale;
                    mptr[k].sortKey *= wscale; // 前面权重或者分布有变化,这里做更新
                    if( wsum > T && kForeground < 0 )
                        kForeground = k+1;
                }

                dst[x] = (uchar)(-(kHit >= kForeground));
            }
        }
        else
        {
            //不考虑更新模型的情况。这里更好理解些,看完这个在看考虑学习率的场景
            for( x = 0; x < cols; x++, mptr += K )
            {
                float pix = src[x];
                int kHit = -1, kForeground = -1;
                //kForeground:前多少个分布是背景的分布。大于kForeground的就是前景分布
                //kHit: 表示给定的像素最符合哪个分布 ; 
                //kHit <0 ,表示没有匹配的分布,自然是前景;
                //kHit<kForeground 自然是背景;否则是前景

                //这里判断给定的像素值与哪个高斯分布最匹配,匹配的依据就是小于vT倍的方差
                for( k = 0; k < K; k++ )
                {
                    if( mptr[k].weight < FLT_EPSILON )
                        break;
                    float mu = mptr[k].mean;
                    float var = mptr[k].var;
                    float diff = pix - mu;
                    float d2 = diff*diff;
                    if( d2 < vT*var )
                    {
                        kHit = k; 
                        break;
                    }
                }

                if( kHit >= 0 ) //若是没有找到匹配的分布,就被判定为为前景,不需要下面的计算
                {
                    float wsum = 0;
                    for( k = 0; k < K; k++ )
                    {
                        wsum += mptr[k].weight;
                        if( wsum > T )
                        {
                            kForeground = k+1; // 前k个作为背景模型,这里计算的到k值。
                            break;
                        }
                    }
                }

                dst[x] = (uchar)(kHit < 0 || kHit >= kForeground ? 255 : 0);// kForeground表示前kForeground个作为背景模型,若是kHit>=kForeground说明匹配的高斯分布已经不在前kForeground中,因此该像素点就是前景了。否则就判定为背景点
            }
        }
    }
}

[1]: 《An Improved Adaptive Background Mixture Model for Realtime Tracking with Shadow Detection》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值