Background Segment CNT

CNT简介

CNT算法是OpenCV Contrib 模块中的背景减除(Background segment)算法之一。相较于OpenCV提供的其他背景减 除算法,该算法具有运行速度快,检测精度高等优点。因此背景减除系列文章第一篇就先讲一下这个算法。因为CNT并没有任何参考论文,故而只能根据OpenCV的源码来进行分析。后面首先介绍OpenCV中该算法如何使用,然后再根据源码对算法进行一个详细的解释。

在OpenCV中使用CNT

  1. 创建一个CNT的实例
    cv::Ptr<cv::bgsegm::BackgroundSubtractor[ALGO_NAME]> bs = cv::bgsegm::createBackgroundSubtractorCNT([params]);
  2. 调用apply()函数进行背景减除
    bs->apply(current_frame, fgmask[, learning_rate]);
    可以看到,在OpenCV中使用CNT是非常简单的。只需简单调用两个函数即可。背景减除得到的结果通过fgmask参数返回,其中前景点背置为255,背景点被置为0

CNT源码解析

OpenCV的CNT算法有两个版本,一个版本会使用历史记录(useHistory = true),另一个版本则只考虑最近N帧的数据。

  1. useHistory 参数为 false
    void operator()(Vec4i &vec, uchar currColor, uchar prevColor, uchar &fgMaskPixelRef)
    {
        int &stabilityRef = vec[0];
        int &bgImgRef = vec[3];
        // 'threshold' 为类属性,由程序内部默认设置为 30
        if (abs(currColor - prevColor) < threshold)
        {
            ++stabilityRef;
            // 'minPixelStability' 为类属性,在生成CNT的实例时由同名参数指定,默认值为 15
            if (stabilityRef == minPixelStability)
            { // bg
                --stabilityRef;
                bgImgRef = prevColor;
            }
            else
            { // fg
                fgMaskPixelRef = 255;
            }
        }
        else
        { // fg
            stabilityRef = 0;
            fgMaskPixelRef = 255;
        }
    }

上面这个函数就是 useHistory 参数为 false 时的核心函数。代码很简单,就是通过简单的阈值操作判断Pixel的稳定性(stability)。如果在连续的minPixelStability帧内都保持稳定,则认为该 Pixel 是稳定的,否则该 Pixel 不稳定。在程序中,稳定的点,即为背景点。


2. useHistory 参数为 true

	void operator()(Vec4i &vec, uchar currColor, uchar prevColor, uchar &fgMaskPixelRef)
    {
        int &stabilityRef = vec[0];
        int &historyColorRef = vec[1];
        int &histStabilityRef = vec[2];
        int &bgImgRef = vec[3];
        // 'thresholdHistory' 参数为类属性,由程序内部设置为 30
        if (abs(currColor - historyColorRef) < thresholdHistory)
        { // No change compared to history - this is maybe a background
            stabilityRef = 0;
            incrStability(histStabilityRef);
            /*********************************************************
	         inline void incrStability(int &histStabilityRef)
			 {
				 // 'maxPixelStability' 参数在创建CNT实例时由程序员制定。默认为 900
		        if (histStabilityRef < maxPixelStability)
		        {
		            ++histStabilityRef;
		        }
		    }
            ********************************************************/
            
            if (histStabilityRef <= minPixelStability)
            {
                fgMaskPixelRef = 255;
            }
            else
            {
                bgImgRef = historyColorRef;
            }
        }
        // 'threshold' 参数同上
        else if (abs(currColor - prevColor) < threshold)
        { // No change compared to prev - this is maybe a background
            incrStability(stabilityRef);
            /*********************************************************
		     inline void decrStability(int &histStabilityRef)
		     {
		        if (histStabilityRef > 0)
		        {
		            --histStabilityRef;
		        }
		     }
            ********************************************************/
            
            if (stabilityRef > minPixelStability)
            { // Stable color - this is maybe a background
                if (stabilityRef >= histStabilityRef)
                {
                    historyColorRef = currColor;
                    histStabilityRef = stabilityRef;
                    bgImgRef = historyColorRef;
                }
                else
                { // Stable but different from stable history - this is a foreground
                    decrStability(histStabilityRef);
                    fgMaskPixelRef = 255;
                }
            }
            else
            { // This is FG.
                fgMaskPixelRef = 255;
            }
        }
        else
        { // Color changed - this is defently a foreground
            stabilityRef = 0;
            decrStability(histStabilityRef);
            fgMaskPixelRef = 255;
        }
    }

上面这个函数就是 useHistory 参数为 true 时的核心函数。代码也很简单。程序在运行过程中会 记录从程序运行到当前时刻为止 ,稳定时间最长的 Pixel 的灰度值 historyColorRef 和它的稳定时间 histStabilityRef。在新的一帧来到时,依然是通过一系列的阈值比较操作来判断 Pixel 的稳定性,从而判断其是否为背景点。


3. 区别
通过源码,我们不难发现, 两者区别在于是 useHistory 参数。 useHistory 参数为 true 时,程序保存历史记录,并将之作为第一判据,反之程序不会保存历史记录,因而只是简单的将 Pixel 最近一段时间的稳定性作为判据。 不使用历史记录的好处在于运动物体经过场景(Scene)时,不仅物体当前时刻的位置会被检测标记为前景点,最近一段时间运动经过的地方也会背标记为前景点,换言之,算法可以获得物体最近一段时间的运动轨迹 。反之, 使用历史记录,可以获得更好的检测效果,检测到的运动区域更准确

运行效果截图

左边为 useHistory = true , 右边为 fasle

总结

从CNT的源码可以看到,每当新的一帧输入时,只需要遍历一遍图像,进行一些简单的加减操作即可完成对像素点的分类。因此CNT算法相较于后面会讲到的GMM系列和LSBP背景减除算法,在运行速度和内存占用上具有得天独厚的优势。另外,算法的检测精度也很高。在对算法速度要求较高时,可以首先考虑使用该算法。

posted @ 2018-08-06 17:03  mysticalwing  阅读(575)  评论(0编辑  收藏  举报