精华内容
下载资源
问答
  • 帧间差分法

    千次阅读 2019-05-05 19:59:44
    原文链接: https://blog.csdn.net/qq_36670033/article/details/89853555 https://blog.csdn.net/tiemaxiaosu/article/details/51736975 ...1.基本原理 帧间差分法是一种通过对视频图像序...

    原文链接:
    https://blog.csdn.net/qq_36670033/article/details/89853555
    https://blog.csdn.net/tiemaxiaosu/article/details/51736975
    https://blog.csdn.net/dcrmg/article/details/52234929

    1.基本原理

    帧间差分法是一种通过对视频图像序列的连续两帧图像做差分运算获取运动目标轮廓的方法。当监控场景中出现异常目标运动时,相邻两帧图像之间会出现较为明显的差别,两帧相减,求得图像对应位置像素值差的绝对值,判断其是否大于某一阈值,进而分析视频或图像序列的物体运动特性。其数学公式描述如下:
    在这里插入图片描述
    D(x,y)为连续两帧图像之间的差分图像,I(t)和I(t-1)分别为t和t-1时刻的图像,T为差分图像二值化时选取的阈值,D(x,y) = 1表示前景,D(x,y) = 0表示背景。
    优点:算法实现简单,程序设计复杂度低,运行速度快;动态环境自适应性强,对场景光线变化不敏感。
    缺点:“空洞”现象(运动物体内部灰度值相近);“双影”现象(差分图像物体边缘轮廓较粗);不能提取出运动对象的完整区域,仅能提取轮廓;算法效果严重依赖所选取的帧间时间间隔和分割阈值。

    1.1 帧间差分自适应

    对视频序列中相邻两帧图像进行帧间差分得到运动区域图像,运动区域图形与背景图像进行差分提取出运动目标图像,运动目标图像与阈值比较得到二值化图像。
    a)对Ak(i,j) 中每一对相邻两帧图像进行差分处理,获得帧差图像Dk(i,j) :
    在这里插入图片描述
    b) 将帧差图像Dk(i,j) 与背景图像Bk(i,j)差分提取出运动目标Wk(i,j) :
    在这里插入图片描述
    c) 将运动目标Wk(i,j)转换为二值图像T(i,j) :
    在这里插入图片描述
    其中:T(i,j) 为二值化输出图像,0、1分别为前景和背景;Th为阈值。
    *

    1.2 阈值Th的取法

    如果阈值Th选择过高,会将运动目标区域严重碎化,如果选择得过低,会引入大量的噪声。因此,提出一个运用当前图像灰度值来确定动态阈值的方法:
    a) 求出图像中的最小和最大灰度值, 取其平均值为初始阈值,记为T。
    b)根据初始阈值将图像分割成目标和背景两部分,求出两部分的平均灰度值μ1、μ2和两部分的灰度概率a1、a2:
    在这里插入图片描述
    c)分割阈值Th为
    在这里插入图片描述

    2 相邻帧间差分法C++、python实现

    相邻帧间差分法直接对相邻的两帧图像做差分运算,并取差分运算的绝对值构成移动物体,优点是运算快速,实时性高,缺点是无法应对光照的突变,物体间一般具有空洞。物体的轮廓是“双边”的,并且物体的移动速度越快,双边轮廓现象越粗越明显(这是不是给监控中速度检测提供了一个思路~~),另一个就是物体具有较大的空洞。

    #include "core/core.hpp"
    #include "highgui/highgui.hpp"
    #include "imgproc/imgproc.hpp"
     
    using namespace cv;
     
    int main(int argc,char *argv[])
    {
    	VideoCapture videoCap(argv[1]);
    	if(!videoCap.isOpened())
    	{
    		return -1;
    	}
    	double videoFPS=videoCap.get(CV_CAP_PROP_FPS);  //获取帧率
    	double videoPause=1000/videoFPS;
    	Mat framePre; //上一帧
    	Mat frameNow; //当前帧
    	Mat frameDet; //运动物体
    	videoCap>>framePre;
    	cvtColor(framePre,framePre,CV_RGB2GRAY);	
    	while(true)
    	{
    		videoCap>>frameNow;
    		if(frameNow.empty()||waitKey(2500)==27)
    		{
    			break;
    		}
    		cvtColor(frameNow,frameNow,CV_RGB2GRAY);
    		absdiff(frameNow,framePre,frameDet);
    		framePre=frameNow;		
    		imshow("Video",frameNow);
    		imshow("Detection",frameDet);		
    	}
    	return 0;
    }
    

    Python实现:

    import cv2 as cv
    import numpy as np
    import matplotlib.pyplot as plt
    cv.namedWindow('Image')
    cap  = cv.VideoCapture('WIN_20180903_16_06_34_Pro.mp4')
    formerimg=0
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    plt.grid(True) #添加网格
    plt.ion()  #interactive mode on
    graylist = list()
    i=0
    while 1:
        i=i+1
        issuc,img = cap.read()
        img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
        img = img.astype(np.int32)
        finalimg = img-formerimg
        finalimg =finalimg.astype(np.uint8)
        finalimg[finalimg<200]=0
        finalimg[finalimg>200]=255
    #            r = img[:,:,0]
        print(len(finalimg[np.where(finalimg==255)]))
        graylist.append(len(finalimg[np.where(finalimg==255)]))
        ax.scatter(i,len(finalimg[np.where(finalimg==255)]),c='r',marker='.')
    #            plt.hist(r.flatten(),256)
        intimg = img.astype(np.uint8)
        cv.imshow('Image',intimg)
        formerimg = img
        plt.pause(0.5)
        if cv.waitKey(1) & 0xFF == 'q':
            break
        
    cv.destroyAllWindows()
    cv.release()
     
    plt.plot(range(175),graylist,'-',linewidth=2.0)
    

    3 三帧差法

    三帧差法是在相邻帧差法基础上改进的算法,在一定程度上优化了运动物体双边,粗轮廓的现象,相比之下,三帧差法比相邻帧差法更适用于物体移动速度较快的情况,比如道路上车辆的智能监控。相比相邻两帧差法,原始的三帧差法对物体的双边粗轮廓和“鬼影”现象有所改善,比较适合对运动速度较快物体的检测,但是仍然会有空洞出现,并且物体移动速度较慢时容易丢失轮廓。

    三帧差法基本实现步骤:

    1. 前两帧图像做灰度差
    2. 当前帧图像与前一帧图像做灰度差
    3. 1和2的结果图像按位做“与”操作
    #include "core/core.hpp"
    #include "highgui/highgui.hpp"
    #include "imgproc/imgproc.hpp"
     
    using namespace cv;
     
    int main(int argc,char *argv[])
    {
    	VideoCapture videoCap(argv[1]);
    	if(!videoCap.isOpened())
    	{
    		return -1;
    	}
    	double videoFPS=videoCap.get(CV_CAP_PROP_FPS);  //获取帧率
    	double videoPause=1000/videoFPS;
    	Mat framePrePre; //上上一帧
    	Mat framePre; //上一帧
    	Mat frameNow; //当前帧
    	Mat frameDet; //运动物体
    	videoCap>>framePrePre;
    	videoCap>>framePre;
    	cvtColor(framePrePre,framePrePre,CV_RGB2GRAY);	
    	cvtColor(framePre,framePre,CV_RGB2GRAY);	
    	int save=0;
    	while(true)
    	{
    		videoCap>>frameNow;
    		if(frameNow.empty()||waitKey(videoPause)==27)
    		{
    			break;
    		}
    		cvtColor(frameNow,frameNow,CV_RGB2GRAY);	
    		Mat Det1;
    		Mat Det2;
    		absdiff(framePrePre,framePre,Det1);  //帧差1
    		absdiff(framePre,frameNow,Det2);     //帧差2
    		threshold(Det1,Det1,0,255,CV_THRESH_OTSU);  //自适应阈值化
    		threshold(Det2,Det2,0,255,CV_THRESH_OTSU);
    		Mat element=getStructuringElement(0,Size(3,3));  //膨胀核
    		dilate(Det1,Det1,element);    //膨胀
    		dilate(Det2,Det2,element);
    		bitwise_and(Det1,Det2,frameDet);		
    		framePrePre=framePre;		
    		framePre=frameNow;		
    		imshow("Video",frameNow);
    		imshow("Detection",frameDet);
    	}
    	return 0;
    }
    
    展开全文
  • 用帧差分法和三帧差分法法提取运动目标
  • 时间差分法帧间差分法)opencv和vc代码实现,已经测试过,可以实现!
  • 帧间差分法的子代码

    2014-03-25 09:55:45
    帧间差分法的子代码
  • 背景提取算法——帧间差分法、背景差分法、ViBe算法、ViBe+算法: 背景提取有很多算法。针对静止摄像机的帧间差分法、高斯背景差分法、ViBe背景提取算法以及它的改进算法ViBe+,还有针对运动摄像机的光流法等。 本文...

    背景提取算法——帧间差分法、背景差分法、ViBe算法、ViBe+算法

    背景提取是在视频图像序列中提取出背景,背景就是场景中静止不动的景物。因为摄像机不动,因此图像中的每个像素点都有一个对应的背景值,在一段时间内,这个背景值是比较固定的。背景提取的目标就是根据视频图像序列,找出图像中每一点的背景值。
    背景提取有很多算法。针对静止摄像机的帧间差分法、高斯背景差分法、ViBe背景提取算法以及它的改进算法ViBe+,还有针对运动摄像机的光流法等。
    本文针对静止摄像机的背景提取算法进行讲解,主要讲解帧间差分法、背景差分法,ViBe算法,以及ViBe+算法。
    笔者已经将本文所有源码上传到笔者的GitHub账号上,地址如下:
    https://github.com/upcAutoLang/BackgroundSplit-OpenCV

    一. 帧间差分法

    1. 算法原理

    帧间差分法是将视频流中相邻两帧或相隔几帧图像的两幅图像像素值相减,并对相减后的图像进行阈值化来提取图像中的运动区域。
    若相减两帧图像的帧数分别为第k帧, 第(k+1)帧,其帧图像分别为fk(x,y),fk+1(x,y),差分图像二值化阈值为T,差分图像用D(x, y)表示,则帧间差分法的公式如下:

    D(x,y)={1,|fk+1(x,y)fk(x,y)|>T0,others

    帧间差分法的优缺点如下:

    • 优点:算法简单,不易受环境光线影响
    • 缺点:
      • 不能用于运动的摄像头中;
      • 无法识别静止或运动速度很慢的目标;
      • 运动目标表面有大面积灰度值相似区域的情况下,在做差分时图像会出现孔洞;

    2. 算法源码

    笔者已经将把源码上传到GitHub网站上,地址如下:
    https://github.com/upcAutoLang/BackgroundSplit-OpenCV/tree/master/src/FramesDifference

    二. 背景差分法

    参考网址:
    《背景差分法》
    《帧间差分法和背景建模法》

    1. 算法原理

    背景差分法是一种对静止场景进行运动分割的通用方法,它将当前获取的图像帧与背景图像做差分运算,得到目标运动区域的灰度图,对灰度图进行阈值化提取运动区域,而且为避免环境光照变化影响,背景图像根据当前获取图像帧进行更新。
    根据前景检测,背景维持和后处理方法,存在几种不同的背景差方法。若设It,Bt分别为当前帧与背景帧图像,T为前景灰度阈值,则其中一种方法流程如下:

    • 取前几帧图像的平均值,将其作为初始的背景图像Bt
    • 当前帧图像与背景图像作灰度减运算,并取绝对值;公式即为|It(x,y)Bt(x,y)|
    • 对当前帧的像素(x,y),若有|It(x,y)Bt(x,y)|>T,则该像素点为前景点;
    • (可选)对前景像素图进行形态学操作(腐蚀、膨胀、开闭操作等)
    • 用当前帧图像对背景图像进行更新;

    背景差分法的优缺点如下:

    • 优点:
      • 算法比较简单;
      • 一定程度上克服了环境光线的影响;
    • 缺点:
      • 不能用于运动的摄像头;
      • 对背景图像实时更新困难;

    2. 算法源码

    笔者已经将把源码上传到GitHub网站上,地址如下:
    https://github.com/upcAutoLang/BackgroundSplit-OpenCV/tree/master/src/GaussBGDifference

    三. ViBe背景提取算法

    ViBe - a powerful technique for background detection and subtraction in video sequences
    ——摘自ViBe算法官网

    ViBe是一种像素级视频背景建模或前景检测的算法,效果优于所熟知的几种算法,对硬件内存占用也少。该算法主要不同之处是背景模型的更新策略,随机选择需要替换的像素的样本,随机选择邻域像素进行更新。在无法确定像素变化的模型时,随机的更新策略,在一定程度上可以模拟像素变化的不确定性。

    参考地址:
    《ViBe算法原理和代码解析 》
    《背景建模–Vibe 算法优缺点分析》
    《第一次总结报告——Vibe 》
    《运动检测(前景检测)之(一)ViBe 》
    《VIBE改进算法》

    参考论文:
    《O. Barnich and M. Van Droogenbroeck. ViBe: a powerful random technique to estimate the background in video sequences.》
    《O. Barnich and M. Van Droogenbroeck. ViBe: A universal background subtraction algorithm for video sequences.》

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

    1. 一般背景提取算法存在的问题

    前文提到的帧间差分法、背景差分法中存在若干问题如下:

    • 对于环境变化的适应并不友好(如光照的变化造成色度的变化);
    • 相机抖动导致画面抖动
    • 物体检测中常出现的Ghost区域;

    其中值得一提的是Ghost区域:Ghost区域常常出现于帧间差分法,当一个原本静止的物体开始运动时,帧间差分法检测时,可能会将原本该物体覆盖区域错误的检测为运动的,这块被错误检测到的区域被称为Ghost。同样的,原本正在运动的物体变成静止物体时,也会出现Ghost区域。
    例如下图,原图像中只有三个正在运动的人,但由于帧间差分法取得的背景图中包含这三个运动的人的某一帧运动状态,后面的一系列帧序列与背景图相减,都会存在背景图中三个人所在的位置,这时候取得的前景会多出三个被检测区域,即Ghost区域。

    Ghost区域在检测中,一定要尽快消除。

    2. ViBe算法原理

    ViBe比较特殊的地方它的思想:它为所有像素点存储了一个样本集,样本集里面保存的采样值是该像素点过去的像素值其邻居点的像素值。后面每一帧的新像素值和样本集里的样本历史值进行比较,判断是否属于背景点。
    下面从几点讲解ViBe算法:

    (1) 背景、前景模型

    模型中,背景就是静止的,或者移动非常缓慢的物体;前景就是相对于背景的物体,即正在移动的物体。所以背景提取算法也可以看成是一个分类问题,遍历像素点的过程中,来确定一个像素点是属于前景点,还是属于背景点。
    在ViBe模型中,背景模型为每个像素点存储了样本集,样本集大小一般为20个点。对于采入的新一帧图像,该帧的某个像素点与该像素点的样本集内采样值比较接近时,就可以判断其是一个背景点。
    用公式表示,我们可以认为:

    • v(x,y):像素点(x, y)处的当前像素值;
    • M(x,y)={v1(x,y),v2(x,y),...vN(x,y)}:像素点(x, y)的背景样本集(样本集大小为N);
    • R:上下取值范围;

    v(x,y)M(x,y)中所有样本值作差,所有差值中,在±R范围内的个数为Nb,若Nb大于一个给定的阈值min,就说明当前像素值与该点历史样本中的多个值相似,那么就认为(x,y)点属于背景点。

    (2) 背景模型初始化

    初始化是建立背景模型的过程,一般的检测算法需要一定长度的视频序列学习完成,影响了检测的实时性,而且当视频画面突然变化时,重新学习背景模型需要较长时间。
    ViBe算法建立背景模型只需要一帧,即使用单帧视频序列初始化背景模型。将视频的第一帧作为背景模型的同时,算法也将该帧中每一个像素点周围随机取多个像素点,填充该像素点的样本集,这样样本集中就包含了像素点的时空分布信息。
    用公式表示,我们可以认为:

    • M0(x,y):初始背景模型中的像素点(x, y);
    • NG:邻居点;
    • v0(x,y):初始原图像中像素点(x, y)的像素值;

    于是有:
    M0(x)={v0(y|yNG(x))},t=0

    这种背景模型初始化的优缺点如下:

    • 优点:
      • 对于噪声的反应比较灵敏;
      • 计算量小速度快;
      • 不仅减少了背景模型建立的过程,还可以处理背景突然变化的情况,当检测到背景突然变化明显时,只需要舍弃原始的模型,重新利用变化后的首帧图像建立背景模型。
    • 缺点:
      • 用于作平均的几帧初始图像中可能采用了运动物体的像素,这种条件下初始化样本集,容易引入拖影(Ghost)区域;

    初始背景模型建立完毕后,就可以进行前景的检测和背景模型的更新了。

    (3) 前景检测

    此时已经建立起了背景模型,便可以已经建立好的背景模型进行前景的检测。
    遍历新一帧图像的所有像素点。用公式表示,则有:

    • v(x,y):新一帧的像素点(x, y);
    • M(x,y)={v1(x,y),v2(x,y),...vN(x,y)}:像素点(x, y)的背景样本集(样本集大小为N);
    • D(x,y)={d1(x,y),d2(x,y),...dN(x,y)}:像素点(x, y)当前值与样本集里所有样本值之差(样本集大小为N)
      • 其中,di=v(x,y)vi(x,y)
    • R:判断像素点与历史样本值是否相近的阈值;
    • T:判断前景点的阈值;
      • 统计当前像素点的值与历史样本值之差大于R的个数,若个数大于T,则判断该点为前景点;

    检测前景的流程如下:

    • 将某像素点的当前像素值v(x,y),与该像素点的样本集M(x,y)作差值,即得到D(x,y)
    • 遍历D(x,y)中的元素di(x,y),比较它与阈值R的大小;并计满足di(x,y)>R的个数为Nf
    • 若有Nf>T,则该点为前景点;

    检测过程的主要三个参数是:样本集数目N,阈值R,与阈值T。一般设置N = 20, R = 20, T = 2;

    (4) 背景模型更新策略

    即使已经建立起了背景模型,也应该对背景模型进行不断的更新,这样才能使得背景模型能够适应背景的不断变化(如光照变化,背景物体变更等)。

    A. 普通更新策略

    对于其他的背景提取算法,背景模型有两种不同的更新策略:

    • 保守更新策略:前景点永远不会用来填充模型
      • 这样会引起死锁,产生Ghost区域。比如初始化的时候如果一块静止的区域被错误的检测为运动的,那么在这种策略下它永远会被当做运动的物体来对待;
    • Blind策略:对死锁不敏感,前景和背景都可以用来更新背景模型;
      • 这样的缺点在于,缓慢移动的物体会融入到背景中,无法检测出来;

    B. ViBe算法更新策略

    ViBe算法中,使用的更新策略是:保守更新策略 + 前景点计数法 + 随机子采样。

    • 前景点计数法:对像素点进行统计,如果某个像素点连续N次被检测为前景,则将其更新为背景点;
    • 随机子采样:在每一个新的视频帧中都去更新背景模型中的每一个像素点的样本值是没有必要的,当一个像素点被分类为背景点时,它有1/φ的概率去更新背景模型。

    这就决定了ViBe算法的更新策略的其他属性:

    • 无记忆更新策略:每次确定需要更新像素点的背景模型时,以新的像素值随机取代该像素点样本集的一个样本值;
    • 时间取样更新策略:并非每处理一帧数据,都需要更新处理,而是按一定的更新率更新背景模型;
      • 当一个像素点被判定为背景时,它有1/φ的概率更新背景模型;
      • φ是时间采样因子,一般取值为16;
    • 空间邻域更新策略:针对需要更新像素点,在该像素点的邻域中随机选择一个像素点,以新选择的像素点更新被选中的背景模型;

    C. ViBe算法具体更新的方法:

    • 每个背景点都有1/φ的概率更新该像素点的模型样本值
    • 有1/φ的概率去更新该像素点邻居点的模型样本值
    • 前景点计数达到临界值时,将其变为背景,并有1/ φ的概率去更新自己的模型样本值。

    更新邻居的样本值利用了像素值的空间传播特性,背景模型逐渐向外扩散,这也有利于Ghost区域的更快的识别。
    在选择要替换的样本集中的样本值时,我们是随机选取一个样本值进行更新。这样就可以保证,样本值的平滑的生命周期的原因是由于是随机的更新,这种情况下一个样本值在时刻t不被更新的概率是(N - 1) / N。假设时间是连续的,那么在极小时间dt过去后,样本值仍然保留的概率是:
    P(t,t+dt)=(N1N)(t+dt)t
    也可以写作:
    P(t,t+dt)=eln(NN1)dt
    上面的公式表明,样本值在模型中是否被替换,与时间t无关,即更新策略是合适的。

    3. ViBe算法优缺点

    (1) 优点

    Vibe背景建模为运动目标检测研究领域开拓了新思路,是一种新颖、快速及有效的运动目标检测算法。优点主要有两点:

    • 思想简单,易于实现:
      • 初始化背景图像时,Vibe算法通常随机选取邻域20个样本,作为每个像素点建立一个基于样本的背景模型,具有初始化速度快、内存消耗少和占用资源少等优点;
      • 随后,利用一个二次抽样因子φ,使有限的样本基数能近似表示无限的时间窗口,即在较少样本前提下,保证算法的准确性;
      • 最后,并采用一种邻域传播机制保证算法的空间一致性。
    • 样本衰减最优:
      • 有人通过增加样本基数(上至200个)来处理复杂场景,也有人结合两个子模型分别处理快速更新和缓慢更新的情况。其实,选取被替换样本更新背景模型,实质上是样本寿命问题。
      • 传统方式采用先进先出的替换策略,而Vibe背景模型中每个样本被选中为替换样本的概率是相等的,与样本存在时间的长短无关,这种策略保证背景模型中的样本寿命呈指数衰减,模型更新达到最佳状态。
    • 运算效率高:
      • Vibe背景模型是基于少量样本的背景模型;
      • Vibe算法优化了背景模型中的相似度匹配算法;

    关于运算效率的比较,《背景建模–Vibe 算法优缺点分析》中做了实验:为了得到最佳样本数量N值,分别选取N为5、15、20、25进行了实验对比:结果如图所示:

    实验结果表明,N取20、25时,检测结果理想;考虑计算负载,N取20最优。与混合高斯的3-5个高斯模型的计算匹配比较,基于20个样本的背景模型计算具有计算开销低、检测速度快等优点。
    Vibe的背景模型相似度匹配函数只与判断像素点与历史样本值是否相近的阈值R,以及判断前景点的阈值T有关(具体见本文三.2.(3))。背景模型中的样本与待分类像素的欧式距离小于R的个数超过T时,更新背景模型;而找到T个匹配样本时,便立即判断该像素为背景像素点,并停止计算,这样提高了运算效率。

    (2) 缺点

    ViBe算法自身也存在着局限性。主要有静止目标、阴影前景和运动目标不完整等问题。

    A. 静止目标

    如下图所示:

    图(a)红框中的人在等地铁,从图(a)到图(c)经过498帧,长时间驻留未运动,该人物运动目标逐渐被背景吸收。而在本视频中,将在450帧以上都没有明显位移的运动目标区域定义成为静止目标区域。
    这样可以总结产生静止目标问题的原因有两个:

    • 运动目标从运动到静止;
    • 运动目标运动速度太过缓慢:当ViBe背景模型更新速度过快时,会将静止或缓慢运动目标吸收成为背景的一部分;

    B. 阴影前景

    如下图所示:

    图(b)和图(d)分别是用Vibe算法对人体运动目标(a)和车体运动目标(c)的检测结果。由于光线被人体或车体运动目标所遮挡,投射阴影区的背景被误检为运动目标前景。阴影的存在导致检测出来的运动目标形状不准确,影响后续目标分类、跟踪、识别和分析等其他智能视频处理模块。
    产生阴影前景问题的根源是:光线被运动目标前景遮挡,投射阴影区的颜色比背景的颜色暗,即阴影和背景颜色值的距离相差较大,背景差分后被误检为运动目标前景。

    C. 运动目标不完整问题

    如下图所示:

    • 图(a)中的人内部出现空洞;
    • 图(b)中的人中间出现断层;
    • 图(c)中的人上半身出现边缘残躯;
    • 图(d)车体的挡风玻璃出现空洞;

    总结图中的结果,可以将运动目标不完整现象大致分为三类:

    • 运动目标内部有大量空洞(图a);
    • 运动目标边缘残缺,呈现C字形凹陷(图d);
    • 运动目标中间有断层(图b);

    产生运动目标不完整问题的根源主要有两点:

    • ViBe算法自身存在的缺陷;
      • 基于统计学原理的Vibe样本模型受限于模型的样本个数,当样本趋于无穷大时才能准确描述场景,这在实际应用中是不可能实现的;
    • 场景或运动目标的复杂性和多变性;
      • 瞬时的光线突变,背景模型来不及更新;
      • 前景与背景颜色相近,将前景误判为背景;
      • 噪声干扰,出现孤立噪声点和连通噪声区域;

    4. ViBe算法源码

    笔者已经将把源码上传到GitHub网站上,地址如下:
    https://github.com/upcAutoLang/BackgroundSplit-OpenCV/tree/master/src/ViBe

    5. ViBe的改进算法ViBe+

    笔者对ViBe+进行了学习研究,博客地址如下:
    《论文翻译:ViBe+算法(ViBe算法的改进版本)》

    笔者已经将把源码上传到GitHub网站上,地址如下:
    https://github.com/upcAutoLang/BackgroundSplit-OpenCV/tree/master/src/ViBe%2B

    相关参考地址:
    《ViBe算法原理和代码解析 》
    《VIBE改进算法》

    参考论文:
    《M. Van Droogenbroeck and O. Paquot. Background Subtraction: Experiments and Improvements for ViBe.》

    展开全文
  • 背景提取是在视频图像序列中提取出背景,背景就是场景中静止不动的...针对静止摄像机的帧间差分法、高斯背景差分法,还有针对运动摄像机的光流法等。  一. 帧间差分法 相邻帧间图像差分思想:检测出了相邻两...

          背景提取是在视频图像序列中提取出背景,背景就是场景中静止不动的景物。因为摄像机不动,因此图像中的每个像素点都有一个对应的背景值,在一段时间内,这个背景值是比较固定的。背景提取的目标就是根据视频图像序列,找出图像中每一点的背景值。 背景提取有很多算法。针对静止摄像机的帧间差分法、高斯背景差分法,还有针对运动摄像机的光流法等。 

    一. 帧间差分法

    相邻帧间图像差分思想:检测出了相邻两帧图像中发生变化的区域。该方法是用图像序列中的连续两帧图像进行差分,然后二值化该灰度差分图像来提取运动信息。由帧间变化区域检测分割得到的图像,区分出背景区域和运动车辆区域,进而提取要检测的车辆目标。它是通过比较图像序列中前后两帧图像对应像素点灰度值的不同,通过两帧相减,如果灰度值很小,可以认为该点无运动物体经过;反之灰度变化很大,则认为有物体经过。
    1. 算法原理

         帧间差分法是将视频流中相邻两帧或相隔几帧图像的两幅图像像素值相减,并对相减后的图像进行阈值化来提取图像中的运动区域。 
    若相减两帧图像的帧数分别为第k帧, 第(k+1)帧,其帧图像分别为f_{k}(x,y)f_{k+1}(x,y)差分图像二值化阈值为T,差分图像用D(x, y)表示,则帧间差分法的公式如下: 

                                         

    帧间差分法的优缺点如下:

    • 优点:算法简单,不易受环境光线影响
    • 缺点: 
      • 不能用于运动的摄像头中;
      • 无法识别静止或运动速度很慢的目标;
      • 运动目标表面有大面积灰度值相似区域的情况下,在做差分时图像会出现孔洞;

    我们以年辆检测为例,车辆检测除了要检测出运动车辆.同时还要检测出暂时停止的车辆,在这个方面,此类方法无能为力。而且如果车辆的体积较大,那么车辆在前后帧中根容易产生重叠部分,尤其是大货车,这使得帧问差分的结果主要为车头和车尾。车辆中间部分的差分值相对报小.形成空洞,不利于检测。

    实例:

    #include<opencv2/opencv.hpp>
    #include<iostream>
    using namespace std;
    using namespace cv;
    
    int main()//帧间差分法
    {
    VideoCapture capture("tree.avi");//获取视频
    if (!capture.isOpened())
    return -1;
    double rate = capture.get(CV_CAP_PROP_FPS);//获取视频帧率
    int delay = 10000 / rate;
    Mat framepro, frame, dframe;
    bool flag = false;
    namedWindow("image");
    namedWindow("test");
    while (capture.read(frame))
    {
    if (false == flag)
    {
    framepro = frame.clone();//将第一帧图像拷贝给framePro
    flag = true;
    }
    else
    {
    absdiff(frame, framepro, dframe);//帧间差分计算两幅图像各个通道的相对应元素的差的绝对值。
    framepro = frame.clone();//将当前帧拷贝给framepro
    threshold(dframe, dframe, 80, 255, CV_THRESH_BINARY);//阈值分割
    imshow("image", frame);
    imshow("test", dframe);
    waitKey(delay);
    }
    }
    waitKey();
    return 0;
    }

    结果:

      

     

    二. 背景差分法

          为达到前景目标的识别,譬如,交通路口对车辆的识别、监控器对行人的识别,常用的且较为有效的方法就是背景差分法(还有其他的方法,比如光流场法,帧差法),背景差分法是一种对静止场景进行运动分割的通用方法,它将当前获取的图像帧与背景图像做差分运算,得到目标运动区域的灰度图,对灰度图进行阈值化提取运动区域,而且为避免环境光照变化影响,背景图像根据当前获取图像帧进行更新。 根据前景检测,背景维持和后处理方法,存在几种不同的背景差方法。如何获取一个“美好”的背景图,是背景差分法的关键和难点。此处介绍一种最为简单的获取背景的方法——平均背景法。

    顾名思义,其基本思想就是,将所采集到的背景图片叠加求和,而后求取平均值作为要求的背景。

    平均背景法使用四个OpenCV函数:

    1.将图像视频的前几帧,利用opencv的cvAcc函数累加求和,并统计累加次数,求取平均值,记为A。(注意理解该平均值的意思是在(x,y)位置处,所有参与求和计算的图像在该点出的像素平均值);

    2.前一帧图像和当前参与累加的图像求差的绝对值,利用cvAbsDiff函数,而后该绝对值图像也进行累加求平均值,记为D。(同理这是某点的差值平均值)它描述的是某点像素值得波动幅度。

    3.那么有1和2便可以认为,图像某点的像素值P若满足A-D < P < A+D,则认为此点属于背景,然而由于前景目标的加入,可能对背景点的亮度有一定的影响,故对波动幅度D进行一定的放缩,若 A -KD < p < A + KD,便认为该点为背景点。自然在该范围外的点,便是我们需要的前景点。那么用cvThreshold函数完成二值化。我们的目的就达到了。
    此为背景建模最简单的方法,在室内等干扰较少的环境下使用时,效果较好,一旦有些许干扰则效果严重下降.

    from :https://blog.csdn.net/q6541oijhk/article/details/41850129

    实例:

    #include<opencv2/opencv.hpp>
    #include<iostream>
    using namespace std;
    using namespace cv;
    Mat IavgF, IdiffF, IprevF, IhiF, IlowF;//平均,差分,前景,判断是否前景的高低阈值,在之间表示是背景(变化小)
    Mat tmp, tmp2;//作为中间变量,传递变量
    Mat Imaskt;
    float Icount;
    vector<Mat> Igray(3);
    vector<Mat> Ilow(3);//将高阈值按通道分别存放
    vector<Mat> Ihi(3);//将低阈值按通道分别存放
    
    void AllocateImages(Mat& I);
    void accumulateBackground(Mat& I);
    void creatModelsfromStats();
    void setHighThreshold(float scale);
    void setLowThreshold(float scale);
    void backgroundDiff(Mat& I, Mat& Imask);
    int main() {
    	Mat image;
    	VideoCapture capture("tree.avi");
    	double rate = capture.get(CV_CAP_PROP_FPS);//获取视频帧率
    	int delay = 10000 / rate;
    	capture >> image;
    	if (image.empty())//如果某帧为空则退出循环
    		cout << "read fail" << endl;
    	AllocateImages(image);
    	if (!capture.isOpened())
    		return -1;
    	int num = 1;
    	while (num==50) {//这一个是学习背景模型,也可以设置为前50帧用来学习,不一定非要学习整个视频
    		accumulateBackground(image);
    		capture >> image;
    		if (image.empty())
    			break;
    		imshow("intput", image);
    		waitKey(delay);
    		num++;
    	}
    	destroyAllWindows();
    
    	creatModelsfromStats();//获得高低阈值
    	Mat mask;
    	while (1) {
    		capture >> image;
    		if (image.empty())
    			break;
    		backgroundDiff(image, mask);//mask输出二值化图像,前景为255
    		imshow("ed", mask);
    		split(image, Igray);
    		Igray[2] = max(mask, Igray[2]);//用红色标记前景
    	    merge(Igray, image);
    		imshow("标记", image);
    		waitKey(delay);
    	}
    	destroyAllWindows();//防止异常退出
    }
    void AllocateImages(Mat& I) {
    	Size sz = I.size();
    	IavgF = Mat::zeros(sz, CV_32FC3);
    	IdiffF = Mat::zeros(sz, CV_32FC3);
    	IprevF = Mat::zeros(sz, CV_32FC3);
    	IhiF = Mat::zeros(sz, CV_32FC3);
    	IlowF = Mat::zeros(sz, CV_32FC3);
    	Icount = 0.00001f;//防止除以0
    	tmp = Mat::zeros(sz, CV_32FC3);
    	tmp2 = Mat::zeros(sz, CV_32FC3);
    	Imaskt = Mat(sz, CV_32FC3);
    }
    void accumulateBackground(Mat& I) {
    	static int flag = 1;
    	I.convertTo(tmp, CV_32F);//类型转换
    	if (!flag) {
    		IavgF += tmp;
    		absdiff(tmp, IprevF, tmp2);
    		IdiffF += tmp2;
    		Icount += 1;
    	}
    	flag = 0;
    	IprevF = tmp;
    }
    void creatModelsfromStats() {//得到背景模型
    	IavgF *= (1 / Icount);
    	IdiffF *= (1 / Icount);
    	IdiffF += Scalar(1.0, 1.0, 1.0);
    	setHighThreshold(12.0);//参数指 倍数
    	setLowThreshold(13.0);
    }
    void setHighThreshold(float scale) {//高低阈值的设定
    	IhiF = IavgF + (IdiffF*scale);
    	split(IhiF, Ihi);
    }
    void setLowThreshold(float scale) {
    	IlowF = IavgF - (IdiffF*scale);
    	split(IlowF, Ilow);
    }
    void backgroundDiff(Mat& I, Mat& Imask) {
    	I.convertTo(tmp, CV_32F);//类型转换
    	split(tmp, Igray);
    	//对三个通道依次比较是否在阈值区间内
    	inRange(Igray[0], Ilow[0], Ihi[0], Imask);//输出二值图像Imask
    	inRange(Igray[1], Ilow[1], Ihi[1], Imaskt);
    	Imask = min(Imask, Imaskt);//与运算
    	inRange(Igray[2], Ilow[2], Ihi[2], Imaskt);
    	Imask = min(Imask, Imaskt);
    	Imask = 255 - Imask;//在阈值内的置255,表示背景,所以做翻转,使背景标为0,前景为255.
    }

    结果:

      

    程序可以运行,但我使用opencv例程里的tree.avi做检测,但结果失败了,未能检测出手掌,求各位大佬能指出算法哪地方出了问题? 

    下面是网上普遍流传的c版本的平均背景法,亲测有效,效果一般。

    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include<opencv\cv.h>
    #include<opencv\highgui.h>
    
    //float 3-channel iamges
    IplImage *IavgF, *IdiffF, *IprevF, *IhiF, *IlowF;
    
    IplImage *Iscratch, *Iscratch2;
    
    //float 1-channel iamges
    IplImage *Igray1, *Igray2, *Igray3;
    IplImage *Ilow1, *Ilow2, *Ilow3;
    IplImage *Ihi1, *Ihi2, *Ihi3;
    
    //byte  1-channel image
    IplImage *Imaskt;
    
    //counts number of images learned for averaging later
    float Icount;
    
    void  AllocateImages(IplImage* I);
    void  accumulateBackground(IplImage* I);
    void  createModelsfromStats();
    void  setHighThreshold(float scale);
    void  setLowThreshold(float scale);
    void  backgroundDiff(IplImage *I, IplImage * Imask);
    void  DeallocateImage();
    
    int main(int argc, char** argv)
    {
    	cvNamedWindow("intput", CV_WINDOW_AUTOSIZE);                    //创建输入显示窗口
    	cvNamedWindow("output", CV_WINDOW_AUTOSIZE);                    //创建输出显示窗口
    	CvCapture* capture = cvCreateFileCapture(argv[1]);          //返回一个capture指针,指向视频
    
    	IplImage* img = cvQueryFrame(capture);                          //从视频中取出的图片
    
    	IplImage* Imask = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);//创建输出图片,这里不能去掉cvCreateImage(cvGetSize(Img),IPL_DEPTH_8U,1),虽然我看例程里省略了
    
    	AllocateImages(img);//给图像分配内存
    
    						/*累积图像,只取了前30帧图片*/
    	while (Icount<30) {
    		accumulateBackground(img);                                   //调用累积图像的函数,循环30次                               
    		img = cvQueryFrame(capture);								 //从视频中取出的图片
    		cvShowImage("intput", img);
    		cvWaitKey(20);
    	}
    
    
    	createModelsfromStats();                                        //背景建模
    
    	while (1)
    	{
    		img = cvQueryFrame(capture);
    		if (!img) break;
    
    		backgroundDiff(img, Imask);                                 //根据模型分割前景
    
    		cvShowImage("output", Imask);                               //显示图像,视频是一张一张图片连续播放的结果
    		cvShowImage("intput", img);
    		char c = cvWaitKey(33);                                    //当前帧被显示后,等待33ms再读取下一张图片
    		if (c == 27) break;                                           //等待期间按下esc键,ASCII码为27,则循环退出
    	}
    	cvReleaseCapture(&capture);
    	cvDestroyWindow("output");
    	cvDestroyWindow("intput");
    	DeallocateImage();
    }
    
    
    
    
    //给需要的所有临时图像分配内存
    void  AllocateImages(IplImage* I) {
    	CvSize sz = cvGetSize(I);
    	IavgF = cvCreateImage(sz, IPL_DEPTH_32F, 3); cvZero(IavgF);
    	IdiffF = cvCreateImage(sz, IPL_DEPTH_32F, 3); cvZero(IdiffF);
    	IprevF = cvCreateImage(sz, IPL_DEPTH_32F, 3); cvZero(IprevF);
    	IhiF = cvCreateImage(sz, IPL_DEPTH_32F, 3); cvZero(IhiF);
    	IlowF = cvCreateImage(sz, IPL_DEPTH_32F, 3); cvZero(IlowF);
    	Ilow1 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Ilow2 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Ilow3 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Ihi1 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Ihi2 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Ihi3 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Icount = 0.00001;//protect against divide by zero
    
    	Iscratch = cvCreateImage(sz, IPL_DEPTH_32F, 3);
    	Iscratch2 = cvCreateImage(sz, IPL_DEPTH_32F, 3);
    	Igray1 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Igray2 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Igray3 = cvCreateImage(sz, IPL_DEPTH_32F, 1);
    	Imaskt = cvCreateImage(sz, IPL_DEPTH_8U, 1);
    	cvZero(Iscratch);
    	cvZero(Iscratch2);
    }
    /*积累背景图像和每一帧图像差值的绝对值
    /函数调用需要通道为3,深度为8的彩色图像
    */
    void  accumulateBackground(IplImage* I) {
    	static int first = 1;
    	cvCvtScale(I, Iscratch, 1, 0);//convert to float,3-channel
    	if (!first) {
    		cvAcc(Iscratch, IavgF);
    		cvAbsDiff(Iscratch, IprevF, Iscratch2);
    		cvAcc(Iscratch2, IdiffF);
    		Icount += 1.0;
    	}
    	first = 0;
    	cvCopy(Iscratch, IprevF);
    }
    
    //计算每一个像素的均值和方差观测(平均绝对差分)
    void createModelsfromStats() {
    	cvConvertScale(IavgF, IavgF, (double)(1.0 / Icount));
    	cvConvertScale(IdiffF, IdiffF, (double)(1.0 / Icount));
    
    	//make sure diff is always something
    	cvAddS(IdiffF, cvScalar(1.0, 1.0, 1.0), IdiffF);
    	setHighThreshold(7.0);
    	setLowThreshold(6.0);
    }
    
    void setHighThreshold(float scale) {
    	cvConvertScale(IdiffF, Iscratch, scale);
    	cvAdd(Iscratch, IavgF, IhiF);
    	cvSplit(IhiF, Ihi1, Ihi2, Ihi3, 0);
    }
    
    void setLowThreshold(float scale) {
    	cvConvertScale(IdiffF, Iscratch, scale);
    	cvAdd(IavgF, Iscratch, IlowF);
    	cvSplit(IlowF, Ilow1, Ilow2, Ilow3, 0);
    }
    
    //图像分割
    void backgroundDiff(IplImage *I, IplImage *Imask) {
    	cvConvertScale(I, Iscratch, 1, 0);//to float
    	cvSplit(Iscratch, Igray1, Igray2, Igray3, 0);
    	//channel-1
    	cvInRange(Igray1, Ilow1, Ihi1, Imask);
    	//channel-2
    	cvInRange(Igray2, Ilow2, Ihi2, Imaskt);
    	cvOr(Imask, Imaskt, Imask);
    	//channel-3
    	cvInRange(Igray3, Ilow3, Ihi3, Imaskt);
    	cvOr(Imask, Imaskt, Imask);
    	cvSubRS(Imask, cvScalar(255), Imask);
    }
    
    //内存释放
    void DeallocateImage() {
    	cvReleaseImage(&IavgF);
    	cvReleaseImage(&IdiffF);
    	cvReleaseImage(&IprevF);
    	cvReleaseImage(&IhiF);
    	cvReleaseImage(&IlowF);
    	cvReleaseImage(&Ilow1);
    	cvReleaseImage(&Ilow2);
    	cvReleaseImage(&Ilow3);
    	cvReleaseImage(&Ihi1);
    	cvReleaseImage(&Ihi2);
    	cvReleaseImage(&Ihi3);
    	cvReleaseImage(&Iscratch);
    	cvReleaseImage(&Iscratch2);
    	cvReleaseImage(&Igray1);
    	cvReleaseImage(&Igray2);
    	cvReleaseImage(&Igray3);
    	cvReleaseImage(&Imaskt);
    }
    

    关于int argc, char** argv的参数传递和调试时如何使用请参见:

    https://blog.csdn.net/qq_30815237/article/details/87006586 

     

    展开全文
  • 1.两帧差分法: 适用场景:相邻帧背景差异小,物体运动较慢 两帧的差异D=∣Fn−1−Fn∣D=|Fn-1-Fn|D=∣Fn−1−Fn∣ 阈值T M(find(D<T))=0 M(find(D>=T))=255 2.三帧差分法: 适用场景:相邻帧背景...

    参考博客:帧间差分法、背景减法、光流场法简介
    前一帧Fn1Fn-1 ; Fn当前帧:Fn ; 后一帧:Fn+1Fn+1
    输出图M
    M初始化为全0矩阵

    1.两帧差分法:

    适用场景:相邻帧间背景差异小,物体运动较慢
    两帧间的差异D=Fn1FnD=|Fn-1-Fn|
    阈值T
    M(find(D<T))=0
    M(find(D>=T))=255

    2.三帧差分法:

    适用场景:相邻帧间背景差异小,物体运动较快
    前两帧差异D1=Fn1FnD1=|Fn-1-Fn|
    后两帧差异D3=Fn+1FnD3=|Fn+1-Fn|
    当前帧的差分图像D2=D1D2D2=D1*D2 ,这里*为逐像素相乘
    阈值T
    M(find(D<T))=0
    M(find(D>=T))=255

    展开全文
  • 帧间差分法_OpenCV_详解

    万次阅读 热门讨论 2013-11-07 22:19:16
    下面分享的是运动目标检测算法中最基本的方法—帧间差分法。  相邻帧间图像差分思想:检测出了相邻两图像中发生变化的区域。该方法是用图像序列中的连续两图像进行差分,然后二值化该灰度差分
  • MATLAB+VideoReader+二帧间差分法

    千次阅读 2019-03-18 22:10:02
    MATLAB+VideoReader+二帧间差分法 刚刚接触图像处理没多久,由于之前并没有学过C++,所以现在正在用MATLAB做仿真。 今天刚开始目标检测算法的帧间差分法,遇到了很多问题,今晚解决了一部分,记录一下心得。并将...
  • 帧间差分法怎么用visual tudio 采用C语言实现动作检测
  • 【转载】背景提取算法——帧间差分法、背景差分法、ViBe算法、ViBe+算法 ...
  • 帧间差分法是一种通过对视频图像序列中相邻两作差分运算来获得运动目标轮廓的方法,它可以很好地适用于存在多个运动目标和摄像机移动的情况。实现起来也较简单,关键是对差分后的图像进行二值化时需要选取适当的...
  • Matlab移动目标检测(帧间差分法

    万次阅读 多人点赞 2015-11-04 19:27:39
    以前上计算机视觉课时,老师演示了一个移动目标检测的程序。觉得好玩,回去后自己写了一个。...基于帧间差分法+阈值(门限)的移动目标检测。检测前进行了中值滤波处理,手动设置了阈值。效果一般吧。
  • 运动目标检测--帧间差分法

    万次阅读 多人点赞 2016-06-24 09:31:11
    帧间差分法(Temporal Difference)就是借鉴了上述思想。由于场景中的目标在运动,目标的影像在不同图像中的位置不同。该类算法对时间上连续的两或三图像进行差分运算,不同对应的像素点相减,判断灰度差的...
  • 基于帧间差分法的运动目标检测

    千次阅读 热门讨论 2017-10-11 19:42:17
    基于帧间差分法的运动目标检测 运动目标一般涉及一下对象:   1 )运动目标:需要研究的运动物体;   2 )背景噪声:没有被检测出来的运动目标区域。   3 )前景噪声:前景噪声是指被认定已发生了图像变化,...
  • 图像处理算法之帧间差分法

    千次阅读 2016-03-15 10:32:00
    帧间差分法是一种通过对视频图像序列的连续两图像做差分运算获取运动目标轮廓的方法。当监控场景中出现异常目标运动时,相邻两图像之间会出现较为明显的差别,两相减,求得图像对应位置像素值差的绝对值,判断...
  • 帧间差分法和背景建模法

    万次阅读 2016-07-09 17:28:28
    原理:利用相邻两帧...三帧差法原理:对两两差分得到的图像进行闭运算,然后相与得到结果图像。 优点:由于噪声具有在时间域难重复的特点,在进行了与运算后,部分孤立噪声也会得到消除,可以解决双影等现象。
  • 帧间差分法、背景减法、光流场法简介

    万次阅读 多人点赞 2018-04-25 16:31:07
    概述 运动目标检测是指当监控场景中有活动目标时,采用图像分割的方法从背景图像中提取出目标的运动区域。运动目标检测技术是智能视频分析的基础,因为目标跟踪、行为理解等视频...其中最常用的三类方法是:帧间差...
  • 帧间差分法是通过比较两之间的像素差来确定运动的物体,是一种常用的运动检测方法。如果物体是运动的,那么在视频中两之间其所在位置的像素是不一样的,我们只需要求出两之间的像素差,并设定一个阈值,就能...
  • 运动目标检测算法官方demo的简化版,十分简洁易懂,运行环境matlab2014
  • 帧间差分法

    千次阅读 2017-06-12 09:45:31
    帧差法
  • 背景差分法 (一)BackgroundSubtractorMOG 这是一个以混合高斯模型为基础的前景/背景分割算法。2001年,P. KadewTraKuPong 和 R. Bowden在论文"An improved adaptive background mixture model for real-time ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,716
精华内容 5,486
关键字:

帧间差分法