Vibe算法详解及源码

前段时间研究了下Vibe目标检算法并做了代码实现,今天得空讲下我对Vibe算法的理解。

算法官方网站:http://www.telecom.ulg.ac.be/research/vibe/

综述:
ViBe是一种像素级视频通过背景建模的方式来检测前景目标的算法。具体的思想就是为每个像素点存储了一个样本集,样本集中采样值就是该像素点过去的像素值和其邻居点的像素值,然后将每一个新的像素值和样本集进行比较来判断是否属于背景点。
相对一般的目标检测算法有以下几个优点:
1、背景的初始化比较迅速,通常一帧就能搞定,但实际运行时可以能也可能因此而引入Ghost区域。但随着程序运行Ghost区域会被消除。
2、占用内在较少,每个像只需要比较一次,且比较算法简单,不会占用太多内存。
3、不易引入Ghost区域,所谓Ghost区域就是:在前后背景的目标检测中当一个原本静止的物体开始运动,背景差检测算法可能会将该目标原本所覆盖的区域错误的检测目标区域,这块区域就成为Ghost。当然原来运动的物体变为静止的也会引入Ghost区域。

下面分几大模块讲述Vibe目标检测的主要思想:

1、Vibe模型的初始化:
通用的检测算法的初始化需要一定长度的视频序列来完成背景模型构造,不利于对实时性要求较高的目标检测。ViBe的初始化仅仅通过一帧图像即可完成,就是:对于一个像素点,随机的选择它的3X3领域内的点的像素值作为,该点的背景模型样本集,本源码中为每个像素点建立总量为20的样本集来表达该点的背景(这是根据图像中相近的像素点总是相似的)。这种初始化方法能快速建立初始的背景模型,同时它能规避一些的噪声的影响,如果了解一些去噪算法的人知道去噪其实就是利用相邻像素相近的原理来滤波实现去噪,比如高斯去噪。另外它有个缺点,初始时容易引起Ghost区域,比如第一帧如果有前景目标在图像中,会在初始时把其设成背景像素点。

源码如下:

    for(usYIdx = 1; usYIdx < usVibeAreaHgt-1; usYIdx++)   /*周围一圈的图像信息不要了*/
    {
        uiYTtlIdx = usYIdx * usVibeAreaStp;

        for(usXIdx = 1; usXIdx < usVibeAreaWth-1; usXIdx++)
        {
            uiXTtlIdx = uiYTtlIdx + usXIdx;

            for(ucSmpleIdx = 0; ucSmpleIdx < VB_SMPL_NUM; ucSmpleIdx++)
            {
                U8 ucRanDom = (U8)(rand()%VB_KNL_NUM);  

                sTmpRow = (S16)(usYIdx) + c_yoff[ucRanDom];
                sTmpCol = (S16)(usXIdx) + c_xoff[ucRanDom];

                *(papucVibeSmplePtr[ucSmpleIdx] + uiXTtlIdx) = pucOriImgIn[sTmpRow*usVibeAreaStp+sTmpCol];
            }
        }
    }

2、Vibe的背景模型更新策略:
我们先来看看常见的背景更新策略:
A:最保守的背景更新策略是,前景点永远不会被用来更新背景模型,但这样会引起死锁:比如初始时图像中的有一前景目标,初始化后被当成了背景,如果目标移动位置后原位置会始终被检测为前景目标,而得不到更新。
B:Blind策略:对死锁不敏感,就是前景背景都用来更新背景模型,但是缓慢移动的物体的前景像素点会融入背景中,前景目标无法被检测出来。
在本算法中采用的更新策略是保守的更新策略+前景点计数方法:首先如果新的像素点与背景模型相似,则将些这个像素值融入背景。如果检测到这个像素点为前景,则对该像素点进行统计,如果连续N次被检测为前景,则将其更新为背景点。这样目标运动进入区域停止较长时间后可以认为此目标已成为背景,如果一个目标初始化时停留在区域内移动后能够将新的背景及时更新到背景模型中去。这就规避了上述A种情况出现的可能。

在本算法中,对于更新背景像素点采用一种随机采样的方法:当一个新的像素点来更新背景模型时,总是1/ φ的概率去更新背景模型,以1/ φ的概率去更新模型样本集中本像素点值,同时以1/ φ的概率去更新模型样本集中相邻的像素点的值,同时当前景点计数达到一定数目后刚认为该点为背景,并有1/ φ的概率去更新模型样本集中本像素点值。这样降低了背景的更新速率,使得缓慢移动的目标不易被融入背景,规避了上述B种情况出现的可能。而且,更新相邻的像素点的值,利用了像素值的空间传播特性,背景模型逐渐向外扩散,保证Ghost区域能尽快的消除。

在选择要替换的样本集中的样本值时候,我们是随机选取一个样本值进行更新,这样可以保证样本值的平滑的生命周期由于是随机的更新,这样一个样本值在时刻t不被更新的概率是 (N-1)/N,假设时间是连续的,那么在dt的时间过去后,样本值仍然保留的概率是
这里写图片描述
这样随着时间的推移,任何一个像素点背景模型样本集中的像素值总能得到及时更新。

源码如下:

{  
    S32 iRetOk = OD_RLT_OK;

    U8 ucRanDom = 0;

    U16 usYIdx = 0;
    U16 usXIdx = 0;

    S16 sTmpRow = 0;
    S16 sTmpCol = 0;

    U8 ucSmpleIdx = 0;

    U32 uiYTtlIdx = 0;
    U32 uiXTtlIdx = 0;

    U16 usVibeAreaWth = pstODVibeInnerS->usVibeAreaWth;
    U16 usVibeAreaHgt = pstODVibeInnerS->usVibeAreaHgt;
    U16 usVibeAreaStp = pstODVibeInnerS->usVibeAreaStp;

    PU8 *papucVibeSmplePtr = pstODVibeInnerS->apucVibeSmplePtr;

    PU8 pucVibeFGMatchNum = pstODVibeInnerS->pucVibeFGMatchNum;
    PU8 pucVibeFGMask     = pstODVibeInnerS->pucVibeFGMask;

    srand((unsigned)time(NULL)); 

    if(NULL == pucOriImgIn || NULL == pstODVibeInnerS)
    {
        return OD_RLT_NO;
    }

    memset(pucVibeFGMask, 0, sizeof(U8)*usVibeAreaHgt*usVibeAreaStp);

    for(usYIdx = 1; usYIdx < usVibeAreaHgt-1; usYIdx++)
    {
        uiYTtlIdx = usYIdx*usVibeAreaStp;

        for(usXIdx = 1; usXIdx < usVibeAreaWth-1; usXIdx++)
        {
            U8 ucTmpOriVal = 0;

            U8 ucMatchNum = 0;
               ucSmpleIdx = 0;

            uiXTtlIdx = uiYTtlIdx + usXIdx;

            ucTmpOriVal = pucOriImgIn[uiXTtlIdx];   

            while(ucMatchNum < VB_MTCH_MIN && ucSmpleIdx < VB_SMPL_NUM)
            {
                if(abs((S8)(*(papucVibeSmplePtr[ucSmpleIdx]+uiXTtlIdx))-(S8)(ucTmpOriVal)) < VB_RADIUS)
                {
                    ucMatchNum++;
                }
                ucSmpleIdx++;
            }

            if(ucMatchNum >= VB_MTCH_MIN)
            {
                /*It is a background pixel*/
                pucVibeFGMatchNum[uiXTtlIdx] = 0;
                pucVibeFGMask[uiXTtlIdx]     = 0;

                /*如果一个像素是背景点,那么它有1/VB_SUB_SMPL_FACT的概率去更新自己的模型样本值*/
                if(0 == (U8)(rand()%VB_SUB_SMPL_FACT))
                {
                    *(papucVibeSmplePtr[(U8)(rand()%VB_SMPL_NUM)]+uiXTtlIdx) = ucTmpOriVal;
                }

                /*同时也有1/VB_SUB_SMPL_FACT的概率去更新它的邻居点的模型样本值*/
                if(0 == (U8)(rand()%VB_SUB_SMPL_FACT))
                {
                    ucRanDom = (U8)(rand()%VB_KNL_NUM);

                    sTmpRow = usYIdx + c_yoff[ucRanDom];

                    sTmpCol = usXIdx + c_xoff[ucRanDom];

                    *(papucVibeSmplePtr[(U8)(rand()%VB_SMPL_NUM)]+sTmpRow*usVibeAreaStp+sTmpCol) = ucTmpOriVal;
                }
            }
            else
            {
                /*It is a foreground pixel*/
                pucVibeFGMatchNum[uiXTtlIdx] += 1;

                /*Set background pixel to 255 to foreground pix*/
                pucVibeFGMask[uiXTtlIdx]     = 255;

                /*如果某个像素点连续N次被检测为前景,则认为一块静止区域被误判为运动,将其更新为背景点*/
                if(pucVibeFGMatchNum[uiXTtlIdx] > VB_STATIC_THR)
                {
                    if(0 == (U8)(rand()%VB_SUB_SMPL_FACT))
                    {
                        *(papucVibeSmplePtr[(U8)(rand()%VB_SMPL_NUM)]+sTmpRow*usVibeAreaStp+sTmpCol) = ucTmpOriVal;
                    }
                }               
            }
        }   
    }

    return iRetOk;
}

你可能感兴趣的:(源码,算法,目标检测,VIBE,Vibe源码)