精华内容
下载资源
问答
  • OpenCv背景差分

    2014-04-11 12:17:37
    OpenCv 背景差分--将背景差分成功封装成一个函数,借助高斯平滑等去噪
  • 背景差分算法

    热门讨论 2013-03-13 20:59:31
    利用matlab实现背景差分,将第一个图片设置为背景图片,简单明了
  • 背景差分人体检测

    2015-04-30 18:29:49
    针对基于混合高斯模型背景差分法对光照突变敏感的问题&提出了背景差分法与三帧间差分法相结合的运动目标检测算法$ 首先利用当前帧与混合高斯模型建立的背景模型差分&快速检测出运动变化区域$然后&通过与设定的阈值...
  • opencv背景差分

    2014-10-16 11:08:20
    包含背景更新的背景差分法opencv实现
  • 为了方便快速实时的检测出医疗输液过程中的液滴速度,本文将基于连续帧间差分与背景差分的算法相融合运用到液滴速度检测中,该算法利用Matlab工具对输液视频提取出每帧的图像序列。对图像序列首先进行连续帧间差分,...
  • 背景差分框图

    2012-12-16 17:31:14
    本框图可以清楚的表达出整个背景差分的流程及处理过程
  • 针对复杂背景下运动目标检测失检率高的问题,提出了用于复杂背景目标检测改进的基于RGB颜色分离的背景差分目标检测方法。主要是对RGB三通道图像独立进行背景差分运算,阈值二值化后合并三通道前景图像,得到完整前景...
  • 背景差分

    热门讨论 2012-08-20 23:59:44
    一个用VC6.0和opencv结合的背景差分法程序,里面现在使用的事opencv2.1,如果需要改动,可以打开工作区间后右键属性,修改里面链接的opencv库文件,如cv210d.lib
  • 可以实现背景和目标的分离
  • 背景差分与三帧差分结合的运动目标检测算法
  • 背景提取算法——帧间差分法、背景差分法、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.》

    展开全文
  • 基于MATLAB的运动目标检测算法,背景差分法。其中包含已处理图像和代码,更改路径后可以直接运行。也可以使用用自己的视频文件,但是记得先把视频文件处理成图像序列。
  • opencv背景差分

    2014-05-11 23:44:55
    基于opencv的视频图像背景差分算法例子
  • 基于opencv的背景差分法小车资料内容
  • 帧间差分与背景差分相融合的运动目标检测算法论文.rar
  • 背景差分法示例

    万次阅读 2017-07-10 16:41:45
    背景差分背景差分法是一种很常用而且广泛传感的技术,主要用于背景不动的情况下提取前景。它主要的原理是在当前帧和背景做减法,然后使用threshold进行二值化得到前景掩码。下面是背景减法的示意图。 背景差分...

    背景差分法


    背景差分法是一种很常用而且广泛传感的技术,主要用于背景不动的情况下提取前景。它主要的原理是在当前帧和背景做减法,然后使用threshold进行二值化得到前景掩码。下面是背景减法的示意图。
    这里写图片描述
    背景差分法主要包含以下两个步骤:
    1.背景的建立
    2.背景的更新
    两个关键点,第一个就是如何选择背景,第二个就是什么时候更新背景,这样可以适应背景变化的情况,更新背景的快慢也很重要,如果更新慢了,快速运动的物体如果没有重叠就认为是两个物体了,如果更新快了,慢速运动的物体会被认为是背景,这样就造成漏检。
    这里学习一下在opencv中如何使用背景差分法。

    代码

    使用了两个方法来产生前景掩码:
    1. cv::bgsegm::BackgroundSubtractorMog
    2. cv::BackgroundSubtractorMog2

    //opencv
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/imgproc.hpp"
    #include "opencv2/videoio.hpp"
    #include <opencv2/highgui.hpp>
    #include <opencv2/video.hpp>
    //C
    #include <stdio.h>
    //C++
    #include <iostream>
    #include <sstream>
    using namespace cv;
    using namespace std;
    // Global variables
    Mat frame; //current frame
    Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
    Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
    char keyboard; //input from keyboard
    void help();
    void processVideo(char* videoFilename);
    void processImages(char* firstFrameFilename);
    void help()
    {
        cout
        << "--------------------------------------------------------------------------" << endl
        << "This program shows how to use background subtraction methods provided by "  << endl
        << " OpenCV. You can process both videos (-vid) and images (-img)."             << endl
                                                                                        << endl
        << "Usage:"                                                                     << endl
        << "./bg_sub {-vid <video filename>|-img <image filename>}"                     << endl
        << "for example: ./bg_sub -vid video.avi"                                       << endl
        << "or: ./bg_sub -img /data/images/1.png"                                       << endl
        << "--------------------------------------------------------------------------" << endl
        << endl;
    }
    int main(int argc, char* argv[])
    {
        //print help information
        help();
        //check for the input parameter correctness
        if(argc != 3) {
            cerr <<"Incorret input list" << endl;
            cerr <<"exiting..." << endl;
            return EXIT_FAILURE;
        }
        //create GUI windows
        namedWindow("Frame");
        namedWindow("FG Mask MOG 2");
        //create Background Subtractor objects
        pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
        if(strcmp(argv[1], "-vid") == 0) {
            //input data coming from a video
            processVideo(argv[2]);
        }
        else if(strcmp(argv[1], "-img") == 0) {
            //input data coming from a sequence of images
            processImages(argv[2]);
        }
        else {
            //error in reading input parameters
            cerr <<"Please, check the input parameters." << endl;
            cerr <<"Exiting..." << endl;
            return EXIT_FAILURE;
        }
        //destroy GUI windows
        destroyAllWindows();
        return EXIT_SUCCESS;
    }
    void processVideo(char* videoFilename) {
        //create the capture object
        VideoCapture capture(videoFilename);
        if(!capture.isOpened()){
            //error in opening the video input
            cerr << "Unable to open video file: " << videoFilename << endl;
            exit(EXIT_FAILURE);
        }
        //read input data. ESC or 'q' for quitting
        keyboard = 0;
        while( keyboard != 'q' && keyboard != 27 ){
            //read the current frame
            if(!capture.read(frame)) {
                cerr << "Unable to read next frame." << endl;
                cerr << "Exiting..." << endl;
                exit(EXIT_FAILURE);
            }
            //update the background model
            pMOG2->apply(frame, fgMaskMOG2);
            //get the frame number and write it on the current frame
            stringstream ss;
            rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
                      cv::Scalar(255,255,255), -1);
            ss << capture.get(CAP_PROP_POS_FRAMES);
            string frameNumberString = ss.str();
            putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
                    FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
            //show the current frame and the fg masks
            imshow("Frame", frame);
            imshow("FG Mask MOG 2", fgMaskMOG2);
            //get the input from the keyboard
            keyboard = (char)waitKey( 30 );
        }
        //delete capture object
        capture.release();
    }
    void processImages(char* fistFrameFilename) {
        //read the first file of the sequence
        frame = imread(fistFrameFilename);
        if(frame.empty()){
            //error in opening the first image
            cerr << "Unable to open first image frame: " << fistFrameFilename << endl;
            exit(EXIT_FAILURE);
        }
        //current image filename
        string fn(fistFrameFilename);
        //read input data. ESC or 'q' for quitting
        keyboard = 0;
        while( keyboard != 'q' && keyboard != 27 ){
            //update the background model
            pMOG2->apply(frame, fgMaskMOG2);
            //get the frame number and write it on the current frame
            size_t index = fn.find_last_of("/");
            if(index == string::npos) {
                index = fn.find_last_of("\\");
            }
            size_t index2 = fn.find_last_of(".");
            string prefix = fn.substr(0,index+1);
            string suffix = fn.substr(index2);
            string frameNumberString = fn.substr(index+1, index2-index-1);
            istringstream iss(frameNumberString);
            int frameNumber = 0;
            iss >> frameNumber;
            rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
                      cv::Scalar(255,255,255), -1);
            putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
                    FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
            //show the current frame and the fg masks
            imshow("Frame", frame);
            imshow("FG Mask MOG 2", fgMaskMOG2);
            //get the input from the keyboard
            keyboard = (char)waitKey( 30 );
            //search for the next image in the sequence
            ostringstream oss;
            oss << (frameNumber + 1);
            string nextFrameNumberString = oss.str();
            string nextFrameFilename = prefix + nextFrameNumberString + suffix;
            //read the next frame
            frame = imread(nextFrameFilename);
            if(frame.empty()){
                //error in opening the next image in the sequence
                cerr << "Unable to open image frame: " << nextFrameFilename << endl;
                exit(EXIT_FAILURE);
            }
            //update the path of the current frame
            fn.assign(nextFrameFilename);
        }
    }

    关键代码说明

    1. 创建三个全局变量,frame用于保存当前图像,fgMaskMog用于保存前景掩码,fgMaskMog2是第二种方法产生的前景掩码。
    Mat frame; //current frame
    Mat fgMaskMOG; //fg mask generated by MOG method
    Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
    1. 使用两个方法产生前景掩码,cv::BackgroundSubtractor。在本例中,使用默认的参数,你也可以尝试其它参数。
    Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
    Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
    ...
    //create Background Subtractor objects
    pMOG = createBackgroundSubtractorMOG(); //MOG approach
    1. 传入视频还是单张图像,使用-vid参数传入视频 使用-img参数传入图像
    if(strcmp(argv[1], "-vid") == 0) {
      //input data coming from a video
      processVideo(argv[2]);
    }
    else if(strcmp(argv[1], "-img") == 0) {
      //input data coming from a sequence of images
      processImages(argv[2]);
    }
    1. 建议使用视频进行测试。如果要停止,输入“q”或者esc。
    while( (char)keyboard != 'q' && (char)keyboard != 27 ){
      //read the current frame
      if(!capture.read(frame)) {
        cerr << "Unable to read next frame." << endl;
        cerr << "Exiting..." << endl;
        exit(EXIT_FAILURE);
      }
    1. 每一帧图像都用于计算前景掩码和更新背景,可以通过传入参数来修改更新速度。
    //update the background model
    pMOG->apply(frame, fgMaskMOG);
    pMOG2->apply(frame, fgMaskMOG2);
    1. 在在上角显示当前帧号。
    //get the frame number and write it on the current frame
    stringstream ss;
    rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
              cv::Scalar(255,255,255), -1);
    ss << capture.get(CAP_PROP_POS_FRAMES);
    string frameNumberString = ss.str();
    putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
            FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
    1. 然后显示输入图像和结果
    //show the current frame and the fg masks
    imshow("Frame", frame);
    imshow("FG Mask MOG", fgMaskMOG);
    imshow("FG Mask MOG 2", fgMaskMOG2);
    1. 如果选择图片进行处理,需要输入几张图像。使用cv::imread进行处理。
    //read the first file of the sequence
    frame = imread(fistFrameFilename);
    if(!frame.data){
      //error in opening the first image
      cerr << "Unable to open first image frame: " << fistFrameFilename << endl;
      exit(EXIT_FAILURE);
    }
    ...
    //search for the next image in the sequence
    ostringstream oss;
    oss << (frameNumber + 1);
    string nextFrameNumberString = oss.str();
    string nextFrameFilename = prefix + nextFrameNumberString + suffix;
    //read the next frame
    frame = imread(nextFrameFilename);
    if(!frame.data){
      //error in opening the next image in the sequence
      cerr << "Unable to open image frame: " << nextFrameFilename << endl;
      exit(EXIT_FAILURE);
    }
    //update the path of the current frame
    fn.assign(nextFrameFilename);

    结果显示:

    结果显示如果人停下了,那么就检测不到了。
    这里写图片描述

    保存

    保存使用cv::imread就可以了,代码实现很简单

    string imageToSave = "output_MOG_" + frameNumberString + ".png";
    bool saved = imwrite(imageToSave, fgMaskMOG);
    if(!saved) {
      cerr << "Unable to save " << imageToSave << endl;
    }
    展开全文
  • 图像处理背景差分

    2012-11-03 15:16:17
    通过将捕捉到的视频进行背景差分,从而捕获视频中运动的物体
  • 背景差分法提取目标

    2014-11-03 16:02:46
    背景差分法提取出运动目标 首先提取背景
  • 基于灰度相关的帧间差分和背景差分相融合的实时目标检测
  • OpenCV 背景差分

    千次阅读 2020-11-25 21:43:34
    OpenCV 背景差分法 Background Subtraction Methods(BS) 在这篇博文中,您将学会如何用Python调用OpenCV,进行背景差分(Background Subtraction)。 背景差分法是一种被广泛应用的技术,我们可以利用背景差分法,...

    OpenCV 背景差分法 Background Subtraction Methods(BS)

    在这篇博文中,您将学会如何用Python调用OpenCV,进行背景差分(Background Subtraction)。

    在这里插入图片描述

    背景差分法是一种被广泛应用的技术,我们可以利用背景差分法,通过架设的固定位置的相机(static camera)来得到前景或者移动目标在图像中所在的位置(foreground mask)。

    就像背景差分法这个名字所说的那样,这种方法是通过把当前帧(current frame)的每一个像素与背景模板(background model)每一个像素做减法,来判断这个像素是属于前景还是背景。

    在这里插入图片描述

    背景差分法可以应用与很多领域,比如日常的安防监控,我们可以通过背景差分法来判断监控视频中是否有人来回走动,快速的定位有人活动的视频片段。也可以在野外架设摄像头,用这种方法快速定位有野生动物的出没的视频片段!

    而且,背景差分法是一种计算资源需求比较小的算法,我们可以在嵌入式设备,比如树莓派(Raspberry Pi)或者Jetson Nano中部署它。

    OpenCV中为我们提供了很多种背景差分的方法,我们在实际项目中可以选择一种方法来进行背景差分,方法种类如下:

    在这里插入图片描述

    从图中我们可以看到,OpenCV中的背景差分法一共分为三类cv下面一类,cv::bgsegm下面一类,还有提供了cuda加速的cv::cuda一类,在这篇博文中,我将向大家介绍最常用的MOG2和KNN这两种方法。

    BackgroundSubtractorMOG2

    这个是以高斯混合模型(GMM)为基础的背景/前景分割算法(关于GMM,大家可以看两篇知乎的帖子:一文详解高斯混合模型原理高斯混合模型(GMM))。它是以 2004 年和 2006 年 Z.Zivkovic 的两篇文章为基础的,分别是"Improved adaptive Gaussian mixture model for background subtraction" 和 “Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction” 。这个算法的一个特点是它为每一个像素选择一个合适数目的高斯分布。。它能更好地适应光照不同等各种场景。 我们需要创建一个背景对象。但在这里我们我们可以选择是否检测阴影。如果 detectShadows = True(默认值),它就会检测并将影子标记出来,但是这样做会降低处理速度。影子会被标记为灰色。

    import numpy as np
    import cv2 as cv
    
    cap = cv.VideoCapture('vtest.avi')	# read from a video file
    # cap = cv.VideoCapture(0)   # read from a webcam
    fgbg = cv.createBackgroundSubtractorMOG2()
    
    while(1):
        ret, frame = cap.read()
        fgmask = fgbg.apply(frame)
        cv.imshow('frame',fgmask)
        k = cv.waitKey(30) & 0xff
        if k == 27:
            break
    
    cap.release()
    cv.destroyAllWindows()
    

    BackgroundSubtractorKNN

    这是一个基于K最近邻(分类算法之邻近算法:KNN(理论篇))的背景前景区分算法,当前景像素数量比较少的时候,算法运行得会非常快。在OpenCV中的调用方式与MOG2很类似,我们只需要把

    fgbg = cv.createBackgroundSubtractorMOG2() 换成 fgbg = cv.createBackgroundSubtractorKNN()

    即可。或者你如果熟悉argparse,你也可以使用输入变量给python文件的方法来改变使用的背景提取算法。 capture = cv.VideoCapture(0)的时候,就会打开摄像头。

    from __future__ import print_function
    import cv2 as cv
    import argparse
    
    parser = argparse.ArgumentParser(description='This program shows how to use background subtraction methods provided by \
                                                  OpenCV. You can process both videos and images.')
    parser.add_argument('--input', type=str, help='Path to a video or a sequence of image.', default='vtest.avi')
    parser.add_argument('--algo', type=str, help='Background subtraction method (KNN, MOG2).', default='MOG2')
    args = parser.parse_args()
    if args.algo == 'MOG2':
        backSub = cv.createBackgroundSubtractorMOG2()
    else:
        backSub = cv.createBackgroundSubtractorKNN()
    capture = cv.VideoCapture(cv.samples.findFileOrKeep(args.input))
    
    if not capture.isOpened:
        print('Unable to open: ' + args.input)
        exit(0)
    while True:
        ret, frame = capture.read()
    
        if frame is None:
            break
    
        # fgMask = backSub.apply(frame,learningRate=0)
        fgMask = backSub.apply(frame)
        cv.rectangle(frame, (10, 2), (100, 20), (255, 255, 255), -1)
        cv.putText(frame, str(capture.get(cv.CAP_PROP_POS_FRAMES)), (15, 15),
                   cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0))
    
        cv.imshow('Frame', frame)
        cv.imshow('FG Mask', fgMask)
    
        keyboard = cv.waitKey(30)
        if keyboard == 'q' or keyboard == 27:
            break
    
    
    

    在这里插入图片描述

    fgMask = backSub.apply(frame) 这个函数中learningRate 默认是-1,就是自动调节,这样我们的背景模板是动态变化的,因此,这时候的背景提取算法只对移动的目标感兴趣。

    如果我们想要的不是对移动的目标感兴趣,而是对与背景不同的目标感兴趣,我们可以这样做fgMask = backSub.apply(frame, learningRate=0) 这样就会停止背景模板的更新。

    在这里插入图片描述

    当然,在想这么做之前,要先把摄像头的自动亮度和自动调焦距的功能关掉(可以参考我的另一篇CSDN),否则噪声会非常多。更多的关于OpenCV 背景差分法和更多OpenCV 的知识,欢迎大家到我的视频课《OpenCV从萌新到专家》中收看,谢谢大家的支持!

    展开全文
  • 利用背景差分法实现运动目标的检测,并完成实时跟踪。
  • 基于动态背景差分的三帧差分检测算法,耿沛,苏小龙,运动检测中的动态背景差分法和三帧差分法是运动检测常用算法,但是前者检测出的物体比实际大,而后者检测不出两帧间重叠部分,针
  • 通过背景差分方法提取视频图片的前景,主要是建立背景统计模型,再通过差分得到前景运动物体
  • 基于卡尔曼滤波的背景差分算法存在背景更新不自适应,对光照变化、物体移入移出敏感等问题。提出了一种改进的以分类分块为核心的背景差分算法。首先,将前N帧视频序列图像求取均值得到初始背景模型;将第K帧图像与...
  • matlab背景差分算法

    2015-03-20 17:09:30
    运用背景差分算法,实现对视频车辆的检测与跟踪。
  • 背景提取是在视频图像序列中提取出背景,背景就是场景中静止不动的...针对静止摄像机的帧间差分法、高斯背景差分法,还有针对运动摄像机的光流法等。  一. 帧间差分法 相邻帧间图像差分思想:检测出了相邻两帧...

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

    一. 帧间差分法

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

     

    展开全文
  • 背景差分求目标高度

    2013-05-29 18:49:45
    利用背景差分得到图像中的目标,并对中值滤波后的图像求目标的像素高度
  • 针对经典自适应背景差分法中存在的问题,提出一种基于分级自适应背景差分的运动检测方法%方法结合了帧间差 分法和背景差分法的优点,解决了运动检测中常见的拖尾问题和空洞问题%
  • MATLAB源码集锦-基于背景差分的运动目标检测与MATLAB实现

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 83,809
精华内容 33,523
关键字:

背景差分