背景建模或前景检测(Background Generation And Foreground Detection) 一

来源:互联网 发布:mysql 字段加一 编辑:程序博客网 时间:2024/06/05 17:54

【本文转自】http://blog.csdn.net/stellar0/article/details/8771380

前言
    在很多情况下,我们需要从一段视频或者一系列图片中找到感兴趣的目标,比如说当人进入已经打烊的超市时发出警报。为了达到这个目的,我们首先需要“学习”背景模型,然后将背景模型和当前图像进行比较,从而得到前景目标。

背景建模
    背景与前景都是相对的概念,以高速公路为例:有时我们对高速公路上来来往往的汽车感兴趣,这时汽车是前景,而路面以及周围的环境是背景;有时我们仅仅对闯入高速公路的行人感兴趣,这时闯入者是前景,而包括汽车之类的其他东西又成了背景。背景建模的方式很多,或高级或简单。不过各种背景模型都有自己适用的场合,即使是高级的背景模型也不能适用于任何场合。下面我将逐一介绍OpenCv中已经实现,或者在《学习OpenCv》这本书中介绍的背景建模方法。
1.帧差
    帧差可说是最简单的一种背景模型,指定视频中的一幅图像为背景,用当前帧与背景进行比较,根据需要过滤较小的差异,得到的结果就是前景了。OpenCv中为我们提供了一种动态计算阀值,然后用帧差进行前景检测的函数——cvChangeDetection(注:EmguCv中没有封装cvChangeDetection,我将其声明到OpenCvInvoke类中,具体实现见文末代码)。而通过对两幅图像使用减法运算,然后再用指定阀值过滤的方法在《学习OpenCv》一书中有详细的介绍。它们的实现代码如下:

帧差:

[cpp] view plaincopyprint?
  1. [DllImport("cvaux200.dll")]  
  2. public static extern void cvChangeDetection(IntPtr prev_frame, IntPtr curr_frame, IntPtr change_mask);  
  3. //backgroundMask为背景,imageBackgroundModel为背景模型,currentFrame为当前帧  
  4. if (backgroundMask == null)  
  5.     backgroundMask = new Image<Gray, byte>(imageBackgroundModel.Size);  
  6. if (threshold == 0d)  
  7.     //如果阀值为0,使用OpenCv中的自适应动态背景检测   
  8.     OpenCvInvoke.cvChangeDetection(imageBackgroundModel.Ptr, currentFrame.Ptr, backgroundMask.Ptr);  
  9. else  
  10. {  
  11.     //如果设置了阀值,使用帧差   
  12.     Image<TColor, Byte> imageTemp = imageBackgroundModel.AbsDiff(currentFrame);  
  13.     Image<Gray, Byte>[] images = imageTemp.Split();  
  14.     backgroundMask.SetValue(0d);  
  15.     foreach (Image<Gray, Byte> image in images)  
  16.         backgroundMask._Or(image.ThresholdBinary(new Gray(threshold), new Gray(255d)));  
  17. }  
  18. backgroundMask._Not();  

对于类似无人值守的仓库防盗之类的场合,使用帧差效果估计很好。


2.背景统计模型
    背景统计模型是:对一段时间的背景进行统计,然后计算其统计数据(例如平均值、平均差分、标准差、均值漂移值等等),将统计数据作为背景的方法。OpenCv中并未实现简单的背景统计模型,不过在《学习OpenCv》中对其中的平均背景统计模型有很详细的介绍。在模仿该算法的基础上,我实现了一系列的背景统计模型,包括:平均背景、均值漂移、标准差和标准协方差。对这些统计概念我其实不明白,在维基百科上看了好半天 -_-
调用背景统计模型很简单,只需4步而已:

[cpp] view plaincopyprint?
  1. //(1)初始化对象   
  2. BackgroundStatModelBase<Bgr> bgModel = new BackgroundStatModelBase<Bgr>(BackgroundStatModelType.AccAvg);  
  3. //(2)更新一段时间的背景图像,视情况反复调用(2)   
  4. bgModel.Update(image);  
  5. //(3)设置当前帧   
  6. bgModel.CurrentFrame = currentFrame;  
  7. //(4)得到背景或者前景   
  8. Image<Gray,Byte> imageForeground = bgModel.ForegroundMask;  

背景统计模型的实现代码如下:

[cpp] view plaincopyprint?
  1. /* 
  2. 背景统计模型 
  3. 作者:王先荣 
  4. 时间:2010年2月19日 
  5. */  
  6. using System;  
  7. using System.Collections.Generic;  
  8. using System.Linq;  
  9. using System.Text;  
  10. using System.Drawing;  
  11. using System.Diagnostics;  
  12. using System.Runtime.InteropServices;  
  13. using Emgu.CV;  
  14. using Emgu.CV.CvEnum;  
  15. using Emgu.CV.Structure;  
  16. using Emgu.CV.UI;  
  17. using Emgu.CV.VideoSurveillance;  
  18.   
  19. namespace ImageProcessLearn  
  20. {  
  21.     //背景模型接口,在IBGFGDetector接口的基础上增加了一个CurrentFrame属性  
  22.     public interface IBackgroundStatModel<TColor> : IDisposable  
  23.         where TColor : struct, IColor  
  24.     {  
  25.         /// <summary>   
  26.         /// 获取前景   
  27.         /// </summary>   
  28.         Image<Gray, byte> BackgroundMask { get; }  
  29.   
  30.         /// <summary>   
  31.         /// 获取背景   
  32.         /// </summary>   
  33.         Image<Gray, byte> ForegroundMask { get; }  
  34.   
  35.         /// <summary>   
  36.         /// 更新背景模型   
  37.         /// </summary>   
  38.         /// <param name="image"></param>   
  39.         void Update(Image<TColor, byte> image);  
  40.   
  41.         /// <summary>   
  42.         /// 计算统计数据   
  43.         /// </summary>   
  44.         void CalcStatData();  
  45.   
  46.         /// <summary>   
  47.         /// 获取或者设置当前帧   
  48.         /// </summary>   
  49.         Image<TColor, Byte> CurrentFrame  
  50.         {  
  51.             get;  
  52.             set;  
  53.         }  
  54.     }  
  55.   
  56.     /// <summary>   
  57.     /// 使用帧差的方式来建立背景模型   
  58.     /// </summary>   
  59.     /// <typeparam name="TColor"></typeparam>  
  60.     public class BackgroundStatModelFrameDiff<TColor> : IBackgroundStatModel<TColor>  
  61.         where TColor : struct, IColor  
  62.     {  
  63.         //成员   
  64.         private Image<TColor, Byte> imageBackgroundModel;   //背景模型图像  
  65.         private Image<TColor, Byte> currentFrame;           //当前帧  
  66.         private double threshold;                           //计算前景时所用的阀值,如果当前帧和背景的差别大于阀值,则被认为是前景  
  67.         private Image<Gray, Byte> backgroundMask;           //计算得到的背景图像  
  68.   
  69.         /// <summary>   
  70.         /// 构造函数   
  71.         /// </summary>   
  72.         /// <param name="image">用于背景统计模型的背景</param>   
  73.         public BackgroundStatModelFrameDiff(Image<TColor, Byte> image)  
  74.         {  
  75.             imageBackgroundModel = image;  
  76.             currentFrame = null;  
  77.             threshold = 15d;  
  78.             backgroundMask = null;  
  79.         }  
  80.   
  81.         public BackgroundStatModelFrameDiff()  
  82.             : this(null)  
  83.         {  
  84.         }  
  85.   
  86.         /// <summary>   
  87.         /// 设置或者获取计算前景时所用的阀值;如果阀值为0,则使用自适应的阀值  
  88.         /// </summary>   
  89.         public double Threshold  
  90.         {  
  91.             get  
  92.             {  
  93.                 return threshold;  
  94.             }  
  95.             set  
  96.             {  
  97.                 threshold = value >= 0 ? value : 15d;  
  98.             }  
  99.         }  
  100.   
  101.         /// <summary>   
  102.         /// 更新背景模型   
  103.         /// </summary>   
  104.         /// <param name="image"></param>   
  105.         public void Update(Image<TColor, Byte> image)  
  106.         {  
  107.             imageBackgroundModel = image;  
  108.         }  
  109.   
  110.         /// <summary>   
  111.         /// 获取或者设置当前帧   
  112.         /// </summary>   
  113.         public Image<TColor, Byte> CurrentFrame  
  114.         {  
  115.             get  
  116.             {  
  117.                 return currentFrame;  
  118.             }  
  119.             set  
  120.             {  
  121.                 currentFrame = value;  
  122.                 CalcBackgroundMask();  
  123.             }  
  124.         }  
  125.   
  126.         /// <summary>   
  127.         /// 计算统计数据   
  128.         /// </summary>   
  129.         public void CalcStatData()  
  130.         {  
  131.         }  
  132.   
  133.         /// <summary>   
  134.         /// 计算背景   
  135.         /// </summary>   
  136.         private void CalcBackgroundMask()  
  137.         {  
  138.             if (imageBackgroundModel == null || currentFrame == null || imageBackgroundModel.Size != currentFrame.Size)  
  139.                 throw new ArgumentException("在计算背景时发现参数错误。可能是:背景模型图像为空,当前帧为空,或者背景模型图像和当前帧的尺寸不一致。");  
  140.             if (backgroundMask == null)  
  141.                 backgroundMask = new Image<Gray, byte>(imageBackgroundModel.Size);  
  142.             if (threshold == 0d)  
  143.                 //如果阀值为0,使用OpenCv中的自适应动态背景检测  
  144.                 OpenCvInvoke.cvChangeDetection(imageBackgroundModel.Ptr, currentFrame.Ptr, backgroundMask.Ptr);  
  145.             else  
  146.             {  
  147.                 //如果设置了阀值,使用帧差   
  148.                 Image<TColor, Byte> imageTemp = imageBackgroundModel.AbsDiff(currentFrame);  
  149.                 Image<Gray, Byte>[] images = imageTemp.Split();  
  150.                 backgroundMask.SetValue(0d);  
  151.                 foreach (Image<Gray, Byte> image in images)  
  152.                     backgroundMask._Or(image.ThresholdBinary(new Gray(threshold), new Gray(255d)));  
  153.             }  
  154.             backgroundMask._Not();  
  155.         }  
  156.   
  157.         /// <summary>   
  158.         /// 获取背景   
  159.         /// </summary>   
  160.         public Image<Gray, Byte> BackgroundMask  
  161.         {  
  162.             get  
  163.             {  
  164.                 return backgroundMask;  
  165.             }  
  166.         }  
  167.   
  168.         /// <summary>   
  169.         /// 获取前景   
  170.         /// </summary>   
  171.         public Image<Gray, Byte> ForegroundMask  
  172.         {  
  173.             get  
  174.             {  
  175.                 return backgroundMask.Not();  
  176.             }  
  177.         }  
  178.   
  179.         /// <summary>   
  180.         /// 释放资源   
  181.         /// </summary>   
  182.         public void Dispose()  
  183.         {  
  184.             if (backgroundMask != null)  
  185.                 backgroundMask.Dispose();  
  186.         }  
  187.     }  
  188.   
  189.     /// <summary>   
  190.     /// 使用平均背景来建立背景模型   
  191.     /// </summary>   
  192.     /// <typeparam name="TColor"></typeparam>   
  193.     public class BackgroundStatModelAccAvg<TColor> : IBackgroundStatModel<TColor>  
  194.         where TColor : struct, IColor  
  195.     {  
  196.         //成员   
  197.         private Image<TColor, Single> imageAccSum;          //累计图像  
  198.         private Image<TColor, Single> imageAccDiff;         //累计差值图像  
  199.         private int frameCount;                             //已经累计的背景帧数  
  200.         private Image<TColor, Single> previousFrame;        //在背景建模时使用的前一帧图像  
  201.         private Image<TColor, Byte> currentFrame;           //当前帧图像  
  202.         private double scale;                               //计算背景时所使用的缩放系数,大于平均值*scale倍数的像素认为是前景  
  203.         private Image<Gray, Byte> backgroundMask;           //计算得到的背景图像  
  204.         private Image<TColor, Single> imageTemp;            //临时图像  
  205.         private bool isStatDataReady;                       //是否已经准备好统计数据  
  206.         private Image<Gray, Single>[] imagesHi;             //背景模型中各通道的最大值图像  
  207.         private Image<Gray, Single>[] imagesLow;            //背景模型中各通道的最小值图像  
  208.   
  209.         /// <summary>   
  210.         /// 构造函数   
  211.         /// </summary>   
  212.         public BackgroundStatModelAccAvg()  
  213.         {  
  214.             imageAccSum = null;  
  215.             imageAccDiff = null;  
  216.             frameCount = 0;  
  217.             previousFrame = null;  
  218.             currentFrame = null;  
  219.             scale = 6d;  
  220.             backgroundMask = null;  
  221.             isStatDataReady = false;  
  222.             imagesHi = null;  
  223.             imagesLow = null;  
  224.         }  
  225.   
  226.         /// <summary>   
  227.         /// 设置或者获取计算前景时所用的阀值   
  228.         /// </summary>   
  229.         public double Scale  
  230.         {  
  231.             get  
  232.             {  
  233.                 return scale;  
  234.             }  
  235.             set  
  236.             {  
  237.                 scale = value > 0 ? value : 6d;  
  238.             }  
  239.         }  
  240.   
  241.         /// <summary>   
  242.         /// 更新背景模型   
  243.         /// </summary>   
  244.         /// <param name="image"></param>   
  245.         public void Update(Image<TColor, Byte> image)  
  246.         {  
  247.             if (frameCount==0)  
  248.             {  
  249.                 imageAccSum = new Image<TColor, Single>(image.Size);  
  250.                 imageAccSum.SetValue(0d);  
  251.                 imageAccDiff = new Image<TColor, float>(image.Size);  
  252.                 imageAccDiff.SetValue(0d);  
  253.             }  
  254.             imageTemp = image.ConvertScale<Single>(1d, 0d); //将图像转换成浮点型  
  255.             imageAccSum.Acc(imageTemp);  
  256.             if (previousFrame != null)  
  257.                 imageAccDiff.Acc(imageTemp.AbsDiff(previousFrame));  
  258.             previousFrame = imageTemp.Copy();  
  259.             frameCount++;  
  260.         }  
  261.   
  262.         /// <summary>   
  263.         /// 获取或者设置当前帧   
  264.         /// </summary>   
  265.         public Image<TColor, Byte> CurrentFrame  
  266.         {  
  267.             get  
  268.             {  
  269.                 return currentFrame;  
  270.             }  
  271.             set  
  272.             {  
  273.                 currentFrame = value;  
  274.                 CalcBackgroundMask();  
  275.             }  
  276.         }  
  277.   
  278.         /// <summary>   
  279.         /// 计算统计数据   
  280.         /// </summary>   
  281.         public void CalcStatData()  
  282.         {  
  283.             //计算出最高及最低阀值图像   
  284.             Image<TColor, Single> imageAvg = imageAccSum.ConvertScale<Single>(1d / frameCount, 0d);  
  285.             Image<TColor, Single> imageAvgDiff = imageAccDiff.ConvertScale<Single>(1d / frameCount, 1d);    //将平均值加1,为了确保总是存在差异  
  286.             Image<TColor, Single> imageHi = imageAvg.Add(imageAvgDiff.ConvertScale<Single>(scale, 0d));  
  287.             Image<TColor, Single> imageLow = imageAvg.Sub(imageAvgDiff.ConvertScale<Single>(scale, 0d));  
  288.             imagesHi = imageHi.Split();  
  289.             imagesLow = imageLow.Split();  
  290.             isStatDataReady = true;  
  291.             //释放资源   
  292.             imageAvg.Dispose();  
  293.             imageAvgDiff.Dispose();  
  294.             imageHi.Dispose();  
  295.             imageLow.Dispose();  
  296.         }  
  297.   
  298.         /// <summary>   
  299.         /// 计算背景   
  300.         /// </summary>   
  301.         private void CalcBackgroundMask()  
  302.         {  
  303.             if (imageAccSum == null || imageAccDiff == null || imageAccSum.Size != currentFrame.Size)  
  304.                 throw new ArgumentException("在计算背景时发生参数错误。可能是:还没有建立背景模型;或者当前帧的尺寸与背景尺寸不一致。");  
  305.             if (!isStatDataReady)  
  306.                 CalcStatData();  
  307.             imageTemp = currentFrame.ConvertScale<Single>(1d, 0d);  
  308.             Image<Gray, Single>[] images = imageTemp.Split();  
  309.             //计算背景图像   
  310.             if (backgroundMask == null)  
  311.                 backgroundMask = new Image<Gray, byte>(currentFrame.Size);  
  312.             backgroundMask.SetZero();  
  313.             for (int i = 0; i < currentFrame.NumberOfChannels; i++)  
  314.                 backgroundMask._Or(images[i].InRange(imagesLow[i], imagesHi[i]));  
  315.             //释放资源   
  316.             for (int i = 0; i < images.Length; i++)  
  317.                 images[i].Dispose();  
  318.         }  
  319.   
  320.         /// <summary>   
  321.         /// 获取背景   
  322.         /// </summary>   
  323.         public Image<Gray, Byte> BackgroundMask  
  324.         {  
  325.             get  
  326.             {  
  327.                 return backgroundMask;  
  328.             }  
  329.         }  
  330.   
  331.         /// <summary>   
  332.         /// 获取前景   
  333.         /// </summary>   
  334.         public Image<Gray, Byte> ForegroundMask  
  335.         {  
  336.             get  
  337.             {  
  338.                 return backgroundMask.Not();  
  339.             }  
  340.         }  
  341.   
  342.         /// <summary>   
  343.         /// 释放资源   
  344.         /// </summary>   
  345.         public void Dispose()  
  346.         {  
  347.             if (imageAccSum != null)  
  348.                 imageAccSum.Dispose();  
  349.             if (imageAccDiff != null)  
  350.                 imageAccDiff.Dispose();  
  351.             if (previousFrame != null)  
  352.                 previousFrame.Dispose();  
  353.             if (currentFrame != null)  
  354.                 currentFrame.Dispose();  
  355.             if (backgroundMask != null)  
  356.                 backgroundMask.Dispose();  
  357.             if (isStatDataReady)  
  358.             {  
  359.                 for (int i = 0; i < imagesHi.Length; i++)  
  360.                 {  
  361.                     imagesHi[i].Dispose();  
  362.                     imagesLow[i].Dispose();  
  363.                 }  
  364.             }  
  365.         }  
  366.     }  
  367.   
  368.     /// <summary>   
  369.     /// 使用均值漂移来建立背景模型   
  370.     /// </summary>   
  371.     /// <typeparam name="TColor"></typeparam>  
  372.     public class BackgroundStatModelRunningAvg<TColor> : IBackgroundStatModel<TColor>  
  373.         where TColor : struct, IColor  
  374.     {  
  375.         //成员   
  376.         private Image<TColor, Single> imageAcc;             //累计图像  
  377.         private Image<TColor, Single> imageAccDiff;         //累计差值图像  
  378.         private int frameCount;                             //已经累计的背景帧数  
  379.         private Image<TColor, Single> previousFrame;        //在背景建模时使用的前一帧图像  
  380.         private Image<TColor, Byte> currentFrame;           //当前帧图像  
  381.         private double scale;                               //计算背景时所使用的缩放系数,大于平均值*scale倍数的像素认为是前景  
  382.         private double alpha;                               //计算均值漂移时使用的权值  
  383.         private Image<Gray, Byte> backgroundMask;           //计算得到的背景图像  
  384.         private Image<TColor, Single> imageTemp;            //临时图像  
  385.         private bool isStatDataReady;                       //是否已经准备好统计数据  
  386.         private Image<Gray, Single>[] imagesHi;             //背景模型中各通道的最大值图像  
  387.         private Image<Gray, Single>[] imagesLow;            //背景模型中各通道的最小值图像  
  388.   
  389.         /// <summary>   
  390.         /// 构造函数   
  391.         /// </summary>   
  392.         public BackgroundStatModelRunningAvg()  
  393.         {  
  394.             imageAcc = null;  
  395.             imageAccDiff = null;  
  396.             frameCount = 0;  
  397.             previousFrame = null;  
  398.             currentFrame = null;  
  399.             scale = 6d;  
  400.             alpha = 0.5d;  
  401.             backgroundMask = null;  
  402.             isStatDataReady = false;  
  403.             imagesHi = null;  
  404.             imagesLow = null;  
  405.         }  
  406.   
  407.         /// <summary>   
  408.         /// 设置或者获取计算前景时所用的阀值   
  409.         /// </summary>   
  410.         public double Scale  
  411.         {  
  412.             get  
  413.             {  
  414.                 return scale;  
  415.             }  
  416.             set  
  417.             {  
  418.                 scale = value > 0 ? value : 6d;  
  419.             }  
  420.         }  
  421.   
  422.         /// <summary>   
  423.         /// 设置或者获取计算均值漂移是使用的权值   
  424.         /// </summary>   
  425.         public double Alpha  
  426.         {  
  427.             get  
  428.             {  
  429.                 return alpha;  
  430.             }  
  431.             set  
  432.             {  
  433.                 alpha = value > 0 && value < 1 ? value : 0.5d;  
  434.             }  
  435.         }  
  436.   
  437.         /// <summary>   
  438.         /// 更新背景模型   
  439.         /// </summary>   
  440.         /// <param name="image"></param>   
  441.         public void Update(Image<TColor, Byte> image)  
  442.         {  
  443.             imageTemp = image.ConvertScale<Single>(1d, 0d); //将图像转换成浮点型  
  444.             if (imageAcc == null)  
  445.             {  
  446.                 imageAcc = imageTemp.Copy();  
  447.             }  
  448.             else  
  449.                 imageAcc.RunningAvg(imageTemp, alpha);  
  450.             if (previousFrame != null)  
  451.             {  
  452.                 if (imageAccDiff == null)  
  453.                     imageAccDiff = imageTemp.AbsDiff(previousFrame);  
  454.                 else  
  455.                     imageAccDiff.RunningAvg(imageTemp.AbsDiff(previousFrame), alpha);  
  456.             }  
  457.             previousFrame = imageTemp.Copy();  
  458.             frameCount++;  
  459.         }  
  460.   
  461.         /// <summary>   
  462.         /// 获取或者设置当前帧   
  463.         /// </summary>   
  464.         public Image<TColor, Byte> CurrentFrame  
  465.         {  
  466.             get  
  467.             {  
  468.                 return currentFrame;  
  469.             }  
  470.             set  
  471.             {  
  472.                 currentFrame = value;  
  473.                 CalcBackgroundMask();  
  474.             }  
  475.         }  
  476.   
  477.         /// <summary>   
  478.         /// 计算统计数据   
  479.         /// </summary>   
  480.         public void CalcStatData()  
  481.         {  
  482.             //计算出最高及最低阀值图像   
  483.             Image<TColor, Single> imageHi = imageAcc.Add(imageAccDiff.ConvertScale<Single>(scale, 0d));  
  484.             Image<TColor, Single> imageLow = imageAcc.Sub(imageAccDiff.ConvertScale<Single>(scale, 0d));  
  485.             imagesHi = imageHi.Split();  
  486.             imagesLow = imageLow.Split();  
  487.             isStatDataReady = true;  
  488.             //释放资源   
  489.             imageHi.Dispose();  
  490.             imageLow.Dispose();  
  491.         }  
  492.   
  493.         /// <summary>   
  494.         /// 计算背景   
  495.         /// </summary>   
  496.         private void CalcBackgroundMask()  
  497.         {  
  498.             if (imageAcc == null || imageAccDiff == null || imageAcc.Size != currentFrame.Size)  
  499.                 throw new ArgumentException("在计算背景时发生参数错误。可能是:还没有建立背景模型;或者当前帧的尺寸与背景尺寸不一致。");  
  500.             if (!isStatDataReady)  
  501.                 CalcStatData();  
  502.             imageTemp = currentFrame.ConvertScale<Single>(1d, 0d);  
  503.             Image<Gray, Single>[] images = imageTemp.Split();  
  504.             //计算背景图像   
  505.             if (backgroundMask == null)  
  506.                 backgroundMask = new Image<Gray, byte>(currentFrame.Size);  
  507.             backgroundMask.SetZero();  
  508.             for (int i = 0; i < currentFrame.NumberOfChannels; i++)  
  509.                 backgroundMask._Or(images[i].InRange(imagesLow[i], imagesHi[i]));  
  510.             //释放资源   
  511.             for (int i = 0; i < images.Length; i++)  
  512.                 images[i].Dispose();  
  513.         }  
  514.   
  515.         /// <summary>   
  516.         /// 获取背景   
  517.         /// </summary>   
  518.         public Image<Gray, Byte> BackgroundMask  
  519.         {  
  520.             get  
  521.             {  
  522.                 return backgroundMask;  
  523.             }  
  524.         }  
  525.   
  526.         /// <summary>   
  527.         /// 获取前景   
  528.         /// </summary>   
  529.         public Image<Gray, Byte> ForegroundMask  
  530.         {  
  531.             get  
  532.             {  
  533.                 return backgroundMask.Not();  
  534.             }  
  535.         }  
  536.   
  537.         /// <summary>   
  538.         /// 释放资源   
  539.         /// </summary>   
  540.         public void Dispose()  
  541.         {  
  542.             if (imageAcc != null)  
  543.                 imageAcc.Dispose();  
  544.             if (imageAccDiff != null)  
  545.                 imageAccDiff.Dispose();  
  546.             if (previousFrame != null)  
  547.                 previousFrame.Dispose();  
  548.             if (currentFrame != null)  
  549.                 currentFrame.Dispose();  
  550.             if (backgroundMask != null)  
  551.                 backgroundMask.Dispose();  
  552.             if (isStatDataReady)  
  553.             {  
  554.                 for (int i = 0; i < imagesHi.Length; i++)  
  555.                 {  
  556.                     imagesHi[i].Dispose();  
  557.                     imagesLow[i].Dispose();  
  558.                 }  
  559.             }  
  560.         }  
  561.     }  
  562.   
  563.     /// <summary>   
  564.     /// 使用标准方差来建立背景模型   
  565.     /// </summary>   
  566.     /// <typeparam name="TColor"></typeparam>   
  567.     public class BackgroundStatModelSquareAcc<TColor> : IBackgroundStatModel<TColor>  
  568.         where TColor : struct, IColor  
  569.     {  
  570.         //成员   
  571.         private Image<TColor, Single> imageAccSum;          //累计图像  
  572.         private Image<TColor, Single> imageAccSquare;       //累计平方图像  
  573.         private int frameCount;                             //已经累计的背景帧数  
  574.         private Image<TColor, Single> previousFrame;        //在背景建模时使用的前一帧图像  
  575.         private Image<TColor, Byte> currentFrame;           //当前帧图像  
  576.         private double scale;                               //计算背景时所使用的缩放系数,大于平均值*scale倍数的像素认为是前景  
  577.         private Image<Gray, Byte> backgroundMask;           //计算得到的背景图像  
  578.         private Image<TColor, Single> imageTemp;            //临时图像  
  579.         private bool isStatDataReady;                       //是否已经准备好统计数据  
  580.         private Image<Gray, Single>[] imagesHi;             //背景模型中各通道的最大值图像  
  581.         private Image<Gray, Single>[] imagesLow;            //背景模型中各通道的最小值图像  
  582.   
  583.         /// <summary>   
  584.         /// 构造函数   
  585.         /// </summary>   
  586.         public BackgroundStatModelSquareAcc()  
  587.         {  
  588.             imageAccSum = null;  
  589.             imageAccSquare = null;  
  590.             frameCount = 0;  
  591.             previousFrame = null;  
  592.             currentFrame = null;  
  593.             scale = 6d;  
  594.             backgroundMask = null;  
  595.             isStatDataReady = false;  
  596.             imagesHi = null;  
  597.             imagesLow = null;  
  598.         }  
  599.   
  600.         /// <summary>   
  601.         /// 设置或者获取计算前景时所用的阀值   
  602.         /// </summary>   
  603.         public double Scale  
  604.         {  
  605.             get  
  606.             {  
  607.                 return scale;  
  608.             }  
  609.             set  
  610.             {  
  611.                 scale = value > 0 ? value : 6d;  
  612.             }  
  613.         }  
  614.   
  615.         /// <summary>   
  616.         /// 更新背景模型   
  617.         /// </summary>   
  618.         /// <param name="image"></param>   
  619.         public void Update(Image<TColor, Byte> image)  
  620.         {  
  621.             if (frameCount == 0)  
  622.             {  
  623.                 imageAccSum = new Image<TColor, Single>(image.Size);  
  624.                 imageAccSum.SetZero();  
  625.                 imageAccSquare = new Image<TColor, float>(image.Size);  
  626.                 imageAccSquare.SetZero();  
  627.             }  
  628.             imageTemp = image.ConvertScale<Single>(1d, 0d); //将图像转换成浮点型  
  629.             imageAccSum.Acc(imageTemp);  
  630.             CvInvoke.cvSquareAcc(imageTemp.Ptr, imageAccSquare.Ptr, IntPtr.Zero);  
  631.             previousFrame = imageTemp.Copy();  
  632.             frameCount++;  
  633.         }  
  634.   
  635.         /// <summary>   
  636.         /// 获取或者设置当前帧   
  637.         /// </summary>   
  638.         public Image<TColor, Byte> CurrentFrame  
  639.         {  
  640.             get  
  641.             {  
  642.                 return currentFrame;  
  643.             }  
  644.             set  
  645.             {  
  646.                 currentFrame = value;  
  647.                 CalcBackgroundMask();  
  648.             }  
  649.         }  
  650.   
  651.         /// <summary>   
  652.         /// 计算统计数据   
  653.         /// </summary>   
  654.         public void CalcStatData()  
  655.         {  
  656.             //计算出标准差、最高及最低阀值图像   
  657.             Image<TColor, Single> imageAvg = imageAccSum.ConvertScale<Single>(1d / frameCount, 0d);  
  658.             Image<TColor, Single> imageSd = imageAccSquare.ConvertScale<Single>(1d / frameCount, 0d);  
  659.             imageSd.Sub(imageAvg.Pow(2d));  
  660.             imageSd = imageSd.Pow(0.5d);  
  661.             Image<TColor, Single> imageHi = imageAvg.Add(imageSd.ConvertScale<Single>(scale, 0d));  
  662.             Image<TColor, Single> imageLow = imageAvg.Sub(imageSd.ConvertScale<Single>(scale, 0d));  
  663.             imagesHi = imageHi.Split();  
  664.             imagesLow = imageLow.Split();  
  665.             isStatDataReady = true;  
  666.             //释放资源   
  667.             imageAvg.Dispose();  
  668.             imageSd.Dispose();  
  669.             imageHi.Dispose();  
  670.             imageLow.Dispose();  
  671.         }  
  672.   
  673.         /// <summary>   
  674.         /// 计算背景   
  675.         /// </summary>   
  676.         private void CalcBackgroundMask()  
  677.         {  
  678.             if (imageAccSum == null || imageAccSquare == null || imageAccSum.Size != currentFrame.Size)  
  679.                 throw new ArgumentException("在计算背景时发生参数错误。可能是:还没有建立背景模型;或者当前帧的尺寸与背景尺寸不一致。");  
  680.             if (!isStatDataReady)  
  681.                 CalcStatData();  
  682.             imageTemp = currentFrame.ConvertScale<Single>(1d, 0d);  
  683.             Image<Gray, Single>[] images = imageTemp.Split();  
  684.             //计算背景图像   
  685.             if (backgroundMask == null)  
  686.                 backgroundMask = new Image<Gray, byte>(currentFrame.Size);  
  687.             backgroundMask.SetZero();  
  688.             for (int i = 0; i < currentFrame.NumberOfChannels; i++)  
  689.                 backgroundMask._Or(images[i].InRange(imagesLow[i], imagesHi[i]));  
  690.             //释放资源   
  691.             for (int i = 0; i < images.Length; i++)  
  692.                 images[i].Dispose();  
  693.         }  
  694.   
  695.         /// <summary>   
  696.         /// 获取背景   
  697.         /// </summary>   
  698.         public Image<Gray, Byte> BackgroundMask  
  699.         {  
  700.             get  
  701.             {  
  702.                 return backgroundMask;  
  703.             }  
  704.         }  
  705.   
  706.         /// <summary>   
  707.         /// 获取前景   
  708.         /// </summary>   
  709.         public Image<Gray, Byte> ForegroundMask  
  710.         {  
  711.             get  
  712.             {  
  713.                 return backgroundMask.Not();  
  714.             }  
  715.         }  
  716.   
  717.         /// <summary>   
  718.         /// 释放资源   
  719.         /// </summary>   
  720.         public void Dispose()  
  721.         {  
  722.             if (imageAccSum != null)  
  723.                 imageAccSum.Dispose();  
  724.             if (imageAccSquare != null)  
  725.                 imageAccSquare.Dispose();  
  726.             if (previousFrame != null)  
  727.                 previousFrame.Dispose();  
  728.             if (currentFrame != null)  
  729.                 currentFrame.Dispose();  
  730.             if (backgroundMask != null)  
  731.                 backgroundMask.Dispose();  
  732.             if (isStatDataReady)  
  733.             {  
  734.                 for (int i = 0; i < imagesHi.Length; i++)  
  735.                 {  
  736.                     imagesHi[i].Dispose();  
  737.                     imagesLow[i].Dispose();  
  738.                 }  
  739.             }  
  740.         }  
  741.     }  
  742.   
  743.     /// <summary>   
  744.     /// 使用标准协方差来建立背景模型   
  745.     /// </summary>   
  746.     /// <typeparam name="TColor"></typeparam>   
  747.     public class BackgroundStatModelMultiplyAcc<TColor> : IBackgroundStatModel<TColor>  
  748.         where TColor : struct, IColor  
  749.     {  
  750.         //成员   
  751.         private Image<TColor, Single> imageAccSum;          //累计图像  
  752.         private Image<TColor, Single> imageAccMultiply;     //累计平方图像  
  753.         private int frameCount;                             //已经累计的背景帧数  
  754.         private Image<TColor, Single> previousFrame;        //在背景建模时使用的前一帧图像  
  755.         private Image<TColor, Byte> currentFrame;           //当前帧图像  
  756.         private double scale;                               //计算背景时所使用的缩放系数,大于平均值*scale倍数的像素认为是前景  
  757.         private Image<Gray, Byte> backgroundMask;           //计算得到的背景图像  
  758.         private Image<TColor, Single> imageTemp;            //临时图像  
  759.         private bool isStatDataReady;                       //是否已经准备好统计数据  
  760.         private Image<Gray, Single>[] imagesHi;             //背景模型中各通道的最大值图像  
  761.         private Image<Gray, Single>[] imagesLow;            //背景模型中各通道的最小值图像  
  762.   
  763.         /// <summary>   
  764.         /// 构造函数   
  765.         /// </summary>   
  766.         public BackgroundStatModelMultiplyAcc()  
  767.         {  
  768.             imageAccSum = null;  
  769.             imageAccMultiply = null;  
  770.             frameCount = 0;  
  771.             previousFrame = null;  
  772.             currentFrame = null;  
  773.             scale = 6d;  
  774.             backgroundMask = null;  
  775.             isStatDataReady = false;  
  776.             imagesHi = null;  
  777.             imagesLow = null;  
  778.         }  
  779.   
  780.         /// <summary>   
  781.         /// 设置或者获取计算前景时所用的阀值   
  782.         /// </summary>   
  783.         public double Scale  
  784.         {  
  785.             get  
  786.             {  
  787.                 return scale;  
  788.             }  
  789.             set  
  790.             {  
  791.                 scale = value > 0 ? value : 6d;  
  792.             }  
  793.         }  
  794.   
  795.         /// <summary>   
  796.         /// 更新背景模型   
  797.         /// </summary>   
  798.         /// <param name="image"></param>   
  799.         public void Update(Image<TColor, Byte> image)  
  800.         {  
  801.             if (frameCount == 0)  
  802.             {  
  803.                 imageAccSum = new Image<TColor, Single>(image.Size);  
  804.                 imageAccSum.SetZero();  
  805.                 imageAccMultiply = new Image<TColor, float>(image.Size);  
  806.                 imageAccMultiply.SetZero();  
  807.             }  
  808.             imageTemp = image.ConvertScale<Single>(1d, 0d); //将图像转换成浮点型  
  809.             imageAccSum.Acc(imageTemp);  
  810.             if (previousFrame != null)  
  811.                 CvInvoke.cvMultiplyAcc(previousFrame.Ptr, imageTemp.Ptr, imageAccMultiply.Ptr, IntPtr.Zero);  
  812.             previousFrame = imageTemp.Copy();  
  813.             frameCount++;  
  814.         }  
  815.   
  816.         /// <summary>   
  817.         /// 获取或者设置当前帧   
  818.         /// </summary>   
  819.         public Image<TColor, Byte> CurrentFrame  
  820.         {  
  821.             get  
  822.             {  
  823.                 return currentFrame;  
  824.             }  
  825.             set  
  826.             {  
  827.                 currentFrame = value;  
  828.                 CalcBackgroundMask();  
  829.             }  
  830.         }  
  831.   
  832.         /// <summary>   
  833.         /// 计算统计数据   
  834.         /// </summary>   
  835.         public void CalcStatData()  
  836.         {  
  837.             //计算出标准协方差、最高及最低阀值图像   
  838.             Image<TColor, Single> imageAvg = imageAccSum.ConvertScale<Single>(1d / frameCount, 0d);  
  839.             Image<TColor, Single> imageScov = imageAccMultiply.ConvertScale<Single>(1d / frameCount, 0d);  
  840.             imageScov.Sub(imageAvg.Pow(2d));  
  841.             imageScov = imageScov.Pow(0.5d);  
  842.             Image<TColor, Single> imageHi = imageAvg.Add(imageScov.ConvertScale<Single>(scale, 0d));  
  843.             Image<TColor, Single> imageLow = imageAvg.Sub(imageScov.ConvertScale<Single>(scale, 0d));  
  844.             imagesHi = imageHi.Split();  
  845.             imagesLow = imageLow.Split();  
  846.             isStatDataReady = true;  
  847.             //释放资源   
  848.             imageAvg.Dispose();  
  849.             imageScov.Dispose();  
  850.             imageHi.Dispose();  
  851.             imageLow.Dispose();  
  852.         }  
  853.   
  854.         /// <summary>   
  855.         /// 计算背景   
  856.         /// </summary>   
  857.         private void CalcBackgroundMask()  
  858.         {  
  859.             if (imageAccSum == null || imageAccMultiply == null || imageAccSum.Size != currentFrame.Size)  
  860.                 throw new ArgumentException("在计算背景时发生参数错误。可能是:还没有建立背景模型;或者当前帧的尺寸与背景尺寸不一致。");  
  861.             if (!isStatDataReady)  
  862.                 CalcStatData();  
  863.             imageTemp = currentFrame.ConvertScale<Single>(1d, 0d);  
  864.             Image<Gray, Single>[] images = imageTemp.Split();  
  865.             //计算背景图像   
  866.             if (backgroundMask == null)  
  867.                 backgroundMask = new Image<Gray, byte>(currentFrame.Size);  
  868.             backgroundMask.SetZero();  
  869.             for (int i = 0; i < currentFrame.NumberOfChannels; i++)  
  870.                 backgroundMask._Or(images[i].InRange(imagesLow[i], imagesHi[i]));  
  871.             //释放资源   
  872.             for (int i = 0; i < images.Length; i++)  
  873.                 images[i].Dispose();  
  874.         }  
  875.   
  876.         /// <summary>   
  877.         /// 获取背景   
  878.         /// </summary>   
  879.         public Image<Gray, Byte> BackgroundMask  
  880.         {  
  881.             get  
  882.             {  
  883.                 return backgroundMask;  
  884.             }  
  885.         }  
  886.   
  887.         /// <summary>   
  888.         /// 获取前景   
  889.         /// </summary>   
  890.         public Image<Gray, Byte> ForegroundMask  
  891.         {  
  892.             get  
  893.             {  
  894.                 return backgroundMask.Not();  
  895.             }  
  896.         }  
  897.   
  898.         /// <summary>   
  899.         /// 释放资源   
  900.         /// </summary>   
  901.         public void Dispose()  
  902.         {  
  903.             if (imageAccSum != null)  
  904.                 imageAccSum.Dispose();  
  905.             if (imageAccMultiply != null)  
  906.                 imageAccMultiply.Dispose();  
  907.             if (previousFrame != null)  
  908.                 previousFrame.Dispose();  
  909.             if (currentFrame != null)  
  910.                 currentFrame.Dispose();  
  911.             if (backgroundMask != null)  
  912.                 backgroundMask.Dispose();  
  913.             if (isStatDataReady)  
  914.             {  
  915.                 for (int i = 0; i < imagesHi.Length; i++)  
  916.                 {  
  917.                     imagesHi[i].Dispose();  
  918.                     imagesLow[i].Dispose();  
  919.                 }  
  920.             }  
  921.         }  
  922.     }  
  923.   
  924.     /// <summary>   
  925.     /// 背景统计模型   
  926.     /// </summary>   
  927.     public class BackgroundStatModelBase<TColor> : IBackgroundStatModel<TColor>  
  928.         where TColor : struct, IColor  
  929.     {  
  930.         //成员变量   
  931.         IBackgroundStatModel<TColor> bgModel;  
  932.         BackgroundStatModelType type;  
  933.   
  934.         /// <summary>   
  935.         /// 构造函数   
  936.         /// </summary>   
  937.         /// <param name="type">背景模型类型</param>  
  938.         public BackgroundStatModelBase(BackgroundStatModelType type)  
  939.         {  
  940.             this.type = type;  
  941.             switch (type)  
  942.             {  
  943.                 case BackgroundStatModelType.FrameDiff:  
  944.                     bgModel = new BackgroundStatModelFrameDiff<TColor>();  
  945.                     break;  
  946.                 case BackgroundStatModelType.AccAvg:  
  947.                     bgModel = new BackgroundStatModelAccAvg<TColor>();  
  948.                     break;  
  949.                 case BackgroundStatModelType.RunningAvg:  
  950.                     bgModel = new BackgroundStatModelRunningAvg<TColor>();  
  951.                     break;  
  952.                 case BackgroundStatModelType.SquareAcc:  
  953.                     bgModel = new BackgroundStatModelSquareAcc<TColor>();  
  954.                     break;  
  955.                 case BackgroundStatModelType.MultiplyAcc:  
  956.                     bgModel = new BackgroundStatModelMultiplyAcc<TColor>();  
  957.                     break;  
  958.                 default:  
  959.                     throw new ArgumentException("不存在的背景模型""type");  
  960.             }  
  961.         }  
  962.   
  963.         /// <summary>   
  964.         /// 获取背景模型类型   
  965.         /// </summary>   
  966.         public BackgroundStatModelType BackgroundStatModelType  
  967.         {  
  968.             get  
  969.             {  
  970.                 return type;  
  971.             }  
  972.         }  
  973.   
  974.         /// <summary>   
  975.         /// 更新背景模型   
  976.         /// </summary>   
  977.         /// <param name="image"></param>   
  978.         public void Update(Image<TColor, Byte> image)  
  979.         {  
  980.             bgModel.Update(image);  
  981.         }  
  982.   
  983.         /// <summary>   
  984.         /// 计算统计数据   
  985.         /// </summary>   
  986.         public void CalcStatData()  
  987.         {  
  988.             bgModel.CalcStatData();  
  989.         }  
  990.   
  991.         /// <summary>   
  992.         /// 设置或者获取当前帧   
  993.         /// </summary>   
  994.         public Image<TColor, Byte> CurrentFrame  
  995.         {  
  996.             get  
  997.             {  
  998.                 return bgModel.CurrentFrame;  
  999.             }  
  1000.             set  
  1001.             {  
  1002.                 bgModel.CurrentFrame = value;  
  1003.             }  
  1004.         }  
  1005.   
  1006.         /// <summary>   
  1007.         /// 获取背景   
  1008.         /// </summary>   
  1009.         public Image<Gray, Byte> BackgroundMask  
  1010.         {  
  1011.             get  
  1012.             {  
  1013.                 return bgModel.BackgroundMask;  
  1014.             }  
  1015.         }  
  1016.   
  1017.         /// <summary>   
  1018.         /// 获取前景   
  1019.         /// </summary>   
  1020.         public Image<Gray, Byte> ForegroundMask  
  1021.         {  
  1022.             get  
  1023.             {  
  1024.                 return bgModel.ForegroundMask;  
  1025.             }  
  1026.         }  
  1027.   
  1028.         /// <summary>   
  1029.         /// 释放资源   
  1030.         /// </summary>   
  1031.         public void Dispose()  
  1032.         {  
  1033.             if (bgModel != null)  
  1034.                 bgModel.Dispose();  
  1035.         }  
  1036.     }  
  1037.   
  1038.     /// <summary>   
  1039.     /// 背景模型类型   
  1040.     /// </summary>   
  1041.     public enum BackgroundStatModelType  
  1042.     {  
  1043.         FrameDiff,      //帧差   
  1044.         AccAvg,         //平均背景   
  1045.         RunningAvg,     //均值漂移   
  1046.         MultiplyAcc,    //计算协方差   
  1047.         SquareAcc       //计算方差   
  1048.     }  
  1049. }  

3.编码本背景模型
    编码本的基本思路是这样的:针对每个像素在时间轴上的变动,建立多个(或者一个)包容近期所有变化的Box(变动范围);在检测时,用当前像素与Box去比较,如果当前像素落在任何Box的范围内,则为背景。
    在OpenCv中已经实现了编码本背景模型,不过实现方式与《学习OpenCv》中提到的方式略有不同,主要有:(1)使用单向链表来容纳Code Element;(2)清除消极的Code Element时,并未重置t。OpenCv中的以下函数与编码本背景模型相关:
cvCreateBGCodeBookModel  建立背景模型
cvBGCodeBookUpdate       更新背景模型
cvBGCodeBookClearStale   清除消极的Code Element
cvBGCodeBookDiff         计算得到背景与前景(注意:该函数仅仅设置背景像素为0,而对前景像素未处理,因此在调用前需要将所有的像素先置为前景)
cvReleaseBGCodeBookModel 释放资源
    在EmguCv中只实现了一部分编码本背景模型,在类BGCodeBookModel<TColor>中,可惜它把cvBGCodeBookDiff给搞忘记了 -_-

下面的代码演示了如何使用编码本背景模型:


[cpp] view plaincopyprint?
  1. //(1)初始化对象   
  2. if (rbCodeBook.Checked)  
  3. {  
  4.     if (bgCodeBookModel != null)  
  5.     {  
  6.         bgCodeBookModel.Dispose();  
  7.         bgCodeBookModel = null;  
  8.     }  
  9.     bgCodeBookModel = new BGCodeBookModel<Bgr>();  
  10. }  
  11. //(2)背景建模或者前景检测   
  12. bool stop = false;  
  13. while (!stop)  
  14. {  
  15.     Image<Bgr, Byte> image = capture.QueryFrame().Clone();  //当前帧  
  16.     bool isBgModeling, isFgDetecting;                       //是否正在建模,是否正在前景检测  
  17.     lock (lockObject)  
  18.     {  
  19.         stop = !isVideoCapturing;  
  20.         isBgModeling = isBackgroundModeling;  
  21.         isFgDetecting = isForegroundDetecting;  
  22.     }  
  23.     //得到设置的参数   
  24.     SettingParam param = (SettingParam)this.Invoke(new GetSettingParamDelegate(GetSettingParam));  
  25.     //code book   
  26.     if (param.ForegroundDetectType == ForegroundDetectType.CodeBook)  
  27.     {  
  28.         if (bgCodeBookModel != null)  
  29.         {  
  30.             //背景建模   
  31.             if (isBgModeling)  
  32.             {  
  33.                 bgCodeBookModel.Update(image);  
  34.                 //背景建模一段时间之后,清理陈旧的条目 (因为清理操作不会重置t,所以这里用求余数的办法来决定清理的时机)  
  35.                 if (backgroundModelFrameCount % CodeBookClearPeriod == CodeBookClearPeriod - 1)  
  36.                     bgCodeBookModel.ClearStale(CodeBookStaleThresh, Rectangle.Empty, null);  
  37.                 backgroundModelFrameCount++;  
  38.                 pbBackgroundModel.Image = bgCodeBookModel.BackgroundMask.Bitmap;  
  39.                 //如果达到最大背景建模次数,停止背景建模   
  40.                 if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  41.                     this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  42.             }  
  43.             //前景检测   
  44.             if (isFgDetecting)  
  45.             {  
  46.                 Image<Gray, Byte> imageFg = new Image<Gray, byte>(image.Size);  
  47.                 imageFg.SetValue(255d);     //CodeBook在得出前景时,仅仅将背景像素置零,所以这里需要先将所有的像素都假设为前景  
  48.                 CvInvoke.cvBGCodeBookDiff(bgCodeBookModel.Ptr, image.Ptr, imageFg.Ptr, Rectangle.Empty);  
  49.                 pbBackgroundModel.Image = imageFg.Bitmap;  
  50.             }  
  51.         }  
  52.     }  
  53.     //更新视频图像   
  54.     pbVideo.Image = image.Bitmap;  
  55. }  
  56. //(3)释放对象   
  57. if (bgCodeBookModel != null)  
  58. {  
  59.     try  
  60.     {  
  61.         bgCodeBookModel.Dispose();  
  62.     }  
  63.     catch { }  
  64. }  

4.高级背景统计模型
    在OpenCv还实现了两种高级的背景统计模型,它们为别是:(1)FGD——复杂背景下的前景物体检测(Foreground object detection from videos containing complex background);(2)MOG——高斯混合模型(Mixture Of Gauss)。包括以下函数:
CvCreateFGDetectorBase  建立前景检测对象
CvFGDetectorProcess     更新前景检测对象
CvFGDetectorGetMask     获取前景
CvFGDetectorRelease     释放资源
    EmguCv将其封装到类FGDetector<TColor>中。我个人觉得OpenCv在实现这个模型的时候做得不太好,因为它将背景建模和前景检测糅合到一起了,无论你是否愿意,在建模的过程中也会检测前景,而只希望前景检测的时候,同时也会建模。我比较喜欢将背景建模和前景检测进行分离的设计。

调用的过程很简单,代码如下:

[cpp] view plaincopyprint?
  1. //(1)创建对象   
  2. if (rbMog.Checked)  
  3. {  
  4.     if (fgDetector != null)  
  5.     {  
  6.         fgDetector.Dispose();  
  7.         fgDetector = null;  
  8.     }  
  9.     fgDetector = new FGDetector<Bgr>(FORGROUND_DETECTOR_TYPE.FGD);  
  10. }  
  11. else if (rbFgd.Checked)  
  12. {  
  13.     if (fgDetector != null)  
  14.     {  
  15.         fgDetector.Dispose();  
  16.         fgDetector = null;  
  17.     }  
  18.     fgDetector = new FGDetector<Bgr>(FORGROUND_DETECTOR_TYPE.MOG);  
  19. }  
  20. //背景建模及前景检测   
  21. bool stop = false;  
  22. while (!stop)  
  23. {  
  24.     Image<Bgr, Byte> image = capture.QueryFrame().Clone();  //当前帧  
  25.     bool isBgModeling, isFgDetecting;                       //是否正在建模,是否正在前景检测  
  26.     lock (lockObject)  
  27.     {  
  28.         stop = !isVideoCapturing;  
  29.         isBgModeling = isBackgroundModeling;  
  30.         isFgDetecting = isForegroundDetecting;  
  31.     }  
  32.     //得到设置的参数   
  33.     SettingParam param = (SettingParam)this.Invoke(new GetSettingParamDelegate(GetSettingParam));  
  34.     if (param.ForegroundDetectType == ForegroundDetectType.Fgd || param.ForegroundDetectType == ForegroundDetectType.Mog)  
  35.     {  
  36.         if (fgDetector != null && (isBgModeling || isFgDetecting))  
  37.         {  
  38.             //背景建模   
  39.             fgDetector.Update(image);  
  40.             backgroundModelFrameCount++;  
  41.             pbBackgroundModel.Image = fgDetector.BackgroundMask.Bitmap;  
  42.             //如果达到最大背景建模次数,停止背景建模   
  43.             if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  44.                 this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  45.             //前景检测   
  46.             if (isFgDetecting)  
  47.             {  
  48.                 pbBackgroundModel.Image = fgDetector.ForgroundMask.Bitmap;  
  49.             }  
  50.         }  
  51.     }  
  52.     //更新视频图像   
  53.     pbVideo.Image = image.Bitmap;  
  54. }  
  55. //(3)释放资源   
  56. if (fgDetector != null)  
  57. {  
  58.     try  
  59.     {  
  60.         fgDetector.Dispose();  
  61.     }  
  62.     catch { }  
  63. }  

前景检测
    在建立好背景模型之后,通过对当前图像及背景的某种比较,我们可以得出前景。在上面的介绍中,已经包含了对前景的代码,在此不再重复。一般情况下,得到的前景包含了很多噪声,为了消除噪声,我们可以对前景图像进行开运算及闭运算,然后再丢弃比较小的轮廓。

本文的代码

[cpp] view plaincopyprint?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Linq;  
  7. using System.Text;  
  8. using System.Windows.Forms;  
  9. using System.Diagnostics;  
  10. using System.Runtime.InteropServices;  
  11. using System.Threading;  
  12. using Emgu.CV;  
  13. using Emgu.CV.CvEnum;  
  14. using Emgu.CV.Structure;  
  15. using Emgu.CV.UI;  
  16. using Emgu.CV.VideoSurveillance;  
  17.   
  18. namespace ImageProcessLearn  
  19. {  
  20.     public partial class FormForegroundDetect : Form  
  21.     {  
  22.         //成员变量   
  23.         Capture capture = null;                         //视频捕获对象  
  24.         Thread captureThread = null;                    //视频捕获线程  
  25.         private bool isVideoCapturing = true;           //是否正在捕获视频  
  26.         private bool isBackgroundModeling = false;      //是否正在背景建模  
  27.         private int backgroundModelFrameCount = 0;      //已经建模的视频帧数  
  28.         private bool isForegroundDetecting = false;     //是否正在进行前景检测  
  29.         private object lockObject = new object();       //用于锁定的对象  
  30.   
  31.         //各种前景检测方法对应的对象   
  32.         BGCodeBookModel<Bgr> bgCodeBookModel = null;    //编码本前景检测  
  33.         private const int CodeBookClearPeriod = 40;     //编码本的清理周期,更新这么多次背景之后,清理掉很少使用的陈旧条目  
  34.         private const int CodeBookStaleThresh = 20;     //在清理编码本时,使用的阀值(stale大于该阀值的条目将被删除)  
  35.         FGDetector<Bgr> fgDetector = null;              //Mog或者Fgd检测  
  36.         BackgroundStatModelFrameDiff<Bgr> bgModelFrameDiff = null;      //帧差  
  37.         BackgroundStatModelAccAvg<Bgr> bgModelAccAvg = null;            //平均背景  
  38.         BackgroundStatModelRunningAvg<Bgr> bgModelRunningAvg = null;    //均值漂移  
  39.         BackgroundStatModelSquareAcc<Bgr> bgModelSquareAcc = null;      //标准方差  
  40.         BackgroundStatModelMultiplyAcc<Bgr> bgModelMultiplyAcc = null;  //标准协方差  
  41.   
  42.   
  43.         public FormForegroundDetect()  
  44.         {  
  45.             InitializeComponent();  
  46.         }  
  47.   
  48.         //窗体加载时   
  49.         private void FormForegroundDetect_Load(object sender, EventArgs e)  
  50.         {  
  51.             //设置Tooltip   
  52.             toolTip.Active = true;  
  53.             toolTip.SetToolTip(rbMog, "高斯混合模型(Mixture Of Gauss)");  
  54.             toolTip.SetToolTip(rbFgd, "复杂背景下的前景物体检测(Foreground object detection from videos containing complex background)");  
  55.             toolTip.SetToolTip(txtMaxBackgroundModelFrameCount, "在背景建模时,使用的最大帧数,超出该值之后,将自动停止背景建模。\r\n对于帧差,总是只捕捉当前帧作为背景。\r\n如果设为零,背景检测将不会自动停止。");  
  56.               
  57.             //打开摄像头视频捕获线程   
  58.             capture = new Capture(0);  
  59.             captureThread = new Thread(new ParameterizedThreadStart(CaptureWithEmguCv));  
  60.             captureThread.Start(null);  
  61.         }  
  62.   
  63.         //窗体关闭前   
  64.         private void FormForegroundDetect_FormClosing(object sender, FormClosingEventArgs e)  
  65.         {  
  66.             //终止视频捕获   
  67.             isVideoCapturing = false;  
  68.             if (captureThread != null)  
  69.                 captureThread.Abort();  
  70.             if (capture != null)  
  71.                 capture.Dispose();  
  72.             //释放对象   
  73.             if (bgCodeBookModel != null)  
  74.             {  
  75.                 try  
  76.                 {  
  77.                     bgCodeBookModel.Dispose();  
  78.                 }  
  79.                 catch { }  
  80.             }  
  81.             if (fgDetector != null)  
  82.             {  
  83.                 try  
  84.                 {  
  85.                     fgDetector.Dispose();  
  86.                 }  
  87.                 catch { }  
  88.             }  
  89.             if (bgModelFrameDiff != null)  
  90.                 bgModelFrameDiff.Dispose();  
  91.             if (bgModelAccAvg != null)  
  92.                 bgModelAccAvg.Dispose();  
  93.             if (bgModelRunningAvg != null)  
  94.                 bgModelRunningAvg.Dispose();  
  95.             if (bgModelSquareAcc != null)  
  96.                 bgModelSquareAcc.Dispose();  
  97.             if (bgModelMultiplyAcc != null)  
  98.                 bgModelMultiplyAcc.Dispose();  
  99.         }  
  100.   
  101.         //EmguCv视频捕获   
  102.         private void CaptureWithEmguCv(object objParam)  
  103.         {  
  104.             if (capture == null)  
  105.                 return;  
  106.             bool stop = false;  
  107.             while (!stop)  
  108.             {  
  109.                 Image<Bgr, Byte> image = capture.QueryFrame().Clone();  //当前帧  
  110.                 bool isBgModeling, isFgDetecting;                       //是否正在建模,是否正在前景检测  
  111.                 lock (lockObject)  
  112.                 {  
  113.                     stop = !isVideoCapturing;  
  114.                     isBgModeling = isBackgroundModeling;  
  115.                     isFgDetecting = isForegroundDetecting;  
  116.                 }  
  117.                 //得到设置的参数   
  118.                 SettingParam param = (SettingParam)this.Invoke(new GetSettingParamDelegate(GetSettingParam));  
  119.                 //code book   
  120.                 if (param.ForegroundDetectType == ForegroundDetectType.CodeBook)  
  121.                 {  
  122.                     if (bgCodeBookModel != null && (isBgModeling || isFgDetecting))  
  123.                     {  
  124.                         //背景建模   
  125.                         if (isBgModeling)  
  126.                         {  
  127.                             bgCodeBookModel.Update(image);  
  128.                             //背景建模一段时间之后,清理陈旧的条目   
  129.                             if (backgroundModelFrameCount % CodeBookClearPeriod == CodeBookClearPeriod - 1)  
  130.                                 bgCodeBookModel.ClearStale(CodeBookStaleThresh, Rectangle.Empty, null);  
  131.                             backgroundModelFrameCount++;  
  132.                             pbBackgroundModel.Image = bgCodeBookModel.BackgroundMask.Bitmap;  
  133.                             //如果达到最大背景建模次数,停止背景建模  
  134.                             if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  135.                                 this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  136.                         }  
  137.                         //前景检测   
  138.                         if (isFgDetecting)  
  139.                         {  
  140.                             Image<Gray, Byte> imageFg = new Image<Gray, byte>(image.Size);  
  141.                             imageFg.SetValue(255d);     //CodeBook在得出前景时,仅仅将背景像素置零,所以这里需要先将所有的像素都假设为前景  
  142.                             CvInvoke.cvBGCodeBookDiff(bgCodeBookModel.Ptr, image.Ptr, imageFg.Ptr, Rectangle.Empty);  
  143.                             pbBackgroundModel.Image = imageFg.Bitmap;  
  144.                         }  
  145.                     }  
  146.                 }  
  147.                 //fgd or mog   
  148.                 else if (param.ForegroundDetectType == ForegroundDetectType.Fgd || param.ForegroundDetectType == ForegroundDetectType.Mog)  
  149.                 {  
  150.                     if (fgDetector != null && (isBgModeling || isFgDetecting))  
  151.                     {  
  152.                         //背景建模   
  153.                         fgDetector.Update(image);  
  154.                         backgroundModelFrameCount++;  
  155.                         pbBackgroundModel.Image = fgDetector.BackgroundMask.Bitmap;  
  156.                         //如果达到最大背景建模次数,停止背景建模   
  157.                         if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  158.                             this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  159.                         //前景检测   
  160.                         if (isFgDetecting)  
  161.                         {  
  162.                             pbBackgroundModel.Image = fgDetector.ForgroundMask.Bitmap;  
  163.                         }  
  164.                     }  
  165.                 }  
  166.                 //帧差   
  167.                 else if (param.ForegroundDetectType == ForegroundDetectType.FrameDiff)  
  168.                 {  
  169.                     if (bgModelFrameDiff != null)  
  170.                     {  
  171.                         //背景建模   
  172.                         if (isBgModeling)  
  173.                         {  
  174.                             bgModelFrameDiff.Update(image);  
  175.                             backgroundModelFrameCount++;  
  176.                             this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel)); //对于帧差,只需要捕获当前帧作为背景即可  
  177.                         }  
  178.                         //前景检测   
  179.                         if (isFgDetecting)  
  180.                         {  
  181.                             bgModelFrameDiff.Threshold = param.Threshold;  
  182.                             bgModelFrameDiff.CurrentFrame = image;  
  183.                             pbBackgroundModel.Image = bgModelFrameDiff.ForegroundMask.Bitmap;  
  184.                         }  
  185.                     }  
  186.                 }  
  187.                 //平均背景   
  188.                 else if (param.ForegroundDetectType == ForegroundDetectType.AccAvg)  
  189.                 {  
  190.                     if (bgModelAccAvg!=null)  
  191.                     {  
  192.                         //背景建模   
  193.                         if (isBgModeling)  
  194.                         {  
  195.                             bgModelAccAvg.Update(image);  
  196.                             backgroundModelFrameCount++;  
  197.                             //如果达到最大背景建模次数,停止背景建模  
  198.                             if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  199.                                 this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  200.                         }  
  201.                         //前景检测   
  202.                         if (isFgDetecting)  
  203.                         {  
  204.                             bgModelAccAvg.CurrentFrame = image;  
  205.                             pbBackgroundModel.Image = bgModelAccAvg.ForegroundMask.Bitmap;  
  206.                         }  
  207.                     }  
  208.                 }  
  209.                 //均值漂移   
  210.                 else if (param.ForegroundDetectType == ForegroundDetectType.RunningAvg)  
  211.                 {  
  212.                     if (bgModelRunningAvg != null)  
  213.                     {  
  214.                         //背景建模   
  215.                         if (isBgModeling)  
  216.                         {  
  217.                             bgModelRunningAvg.Update(image);  
  218.                             backgroundModelFrameCount++;  
  219.                             //如果达到最大背景建模次数,停止背景建模  
  220.                             if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  221.                                 this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  222.                         }  
  223.                         //前景检测   
  224.                         if (isFgDetecting)  
  225.                         {  
  226.                             bgModelRunningAvg.CurrentFrame = image;  
  227.                             pbBackgroundModel.Image = bgModelRunningAvg.ForegroundMask.Bitmap;  
  228.                         }  
  229.                     }  
  230.                 }  
  231.                 //计算方差   
  232.                 else if (param.ForegroundDetectType == ForegroundDetectType.SquareAcc)  
  233.                 {  
  234.                     if (bgModelSquareAcc != null)  
  235.                     {  
  236.                         //背景建模   
  237.                         if (isBgModeling)  
  238.                         {  
  239.                             bgModelSquareAcc.Update(image);  
  240.                             backgroundModelFrameCount++;  
  241.                             //如果达到最大背景建模次数,停止背景建模  
  242.                             if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  243.                                 this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  244.                         }  
  245.                         //前景检测   
  246.                         if (isFgDetecting)  
  247.                         {  
  248.                             bgModelSquareAcc.CurrentFrame = image;  
  249.                             pbBackgroundModel.Image = bgModelSquareAcc.ForegroundMask.Bitmap;  
  250.                         }  
  251.                     }  
  252.                 }  
  253.                 //协方差   
  254.                 else if (param.ForegroundDetectType == ForegroundDetectType.MultiplyAcc)  
  255.                 {  
  256.                     if (bgModelMultiplyAcc != null)  
  257.                     {  
  258.                         //背景建模   
  259.                         if (isBgModeling)  
  260.                         {  
  261.                             bgModelMultiplyAcc.Update(image);  
  262.                             backgroundModelFrameCount++;  
  263.                             //如果达到最大背景建模次数,停止背景建模  
  264.                             if (param.MaxBackgroundModelFrameCount != 0 && backgroundModelFrameCount > param.MaxBackgroundModelFrameCount)  
  265.                                 this.Invoke(new NoParamAndReturnDelegate(StopBackgroundModel));  
  266.                         }  
  267.                         //前景检测   
  268.                         if (isFgDetecting)  
  269.                         {  
  270.                             bgModelMultiplyAcc.CurrentFrame = image;  
  271.                             pbBackgroundModel.Image = bgModelMultiplyAcc.ForegroundMask.Bitmap;  
  272.                         }  
  273.                     }  
  274.                 }  
  275.                 //更新视频图像   
  276.                 pbVideo.Image = image.Bitmap;  
  277.             }  
  278.         }  
  279.   
  280.         //用于在工作线程中更新结果的委托及方法   
  281.         private delegate void AddResultDelegate(string result);  
  282.         private void AddResultMethod(string result)  
  283.         {  
  284.             //txtResult.Text += result;   
  285.         }  
  286.   
  287.         //用于在工作线程中获取设置参数的委托及方法   
  288.         private delegate SettingParam GetSettingParamDelegate();  
  289.         private SettingParam GetSettingParam()  
  290.         {  
  291.             ForegroundDetectType type = ForegroundDetectType.FrameDiff;  
  292.             if (rbFrameDiff.Checked)  
  293.                 type = ForegroundDetectType.FrameDiff;  
  294.             else if (rbAccAvg.Checked)  
  295.                 type = ForegroundDetectType.AccAvg;  
  296.             else if (rbRunningAvg.Checked)  
  297.                 type = ForegroundDetectType.RunningAvg;  
  298.             else if (rbMultiplyAcc.Checked)  
  299.                 type = ForegroundDetectType.MultiplyAcc;  
  300.             else if (rbSquareAcc.Checked)  
  301.                 type = ForegroundDetectType.SquareAcc;  
  302.             else if (rbCodeBook.Checked)  
  303.                 type = ForegroundDetectType.CodeBook;  
  304.             else if (rbMog.Checked)  
  305.                 type = ForegroundDetectType.Mog;  
  306.             else  
  307.                 type = ForegroundDetectType.Fgd;  
  308.             int maxFrameCount = 0;  
  309.             int.TryParse(txtMaxBackgroundModelFrameCount.Text, out maxFrameCount);  
  310.             double threshold = 15d;  
  311.             double.TryParse(txtThreshold.Text, out threshold);  
  312.             if (threshold <= 0)  
  313.                 threshold = 15d;  
  314.             return new SettingParam(type, maxFrameCount, threshold);  
  315.         }  
  316.   
  317.         //没有参数及返回值的委托   
  318.         private delegate void NoParamAndReturnDelegate();  
  319.   
  320.         //开始背景建模   
  321.         private void btnStartBackgroundModel_Click(object sender, EventArgs e)  
  322.         {  
  323.             if (rbCodeBook.Checked)  
  324.             {  
  325.                 if (bgCodeBookModel != null)  
  326.                 {  
  327.                     bgCodeBookModel.Dispose();  
  328.                     bgCodeBookModel = null;  
  329.                 }  
  330.                 bgCodeBookModel = new BGCodeBookModel<Bgr>();  
  331.             }  
  332.             else if (rbMog.Checked)  
  333.             {  
  334.                 if (fgDetector != null)  
  335.                 {  
  336.                     fgDetector.Dispose();  
  337.                     fgDetector = null;  
  338.                 }  
  339.                 fgDetector = new FGDetector<Bgr>(FORGROUND_DETECTOR_TYPE.FGD);  
  340.             }  
  341.             else if (rbFgd.Checked)  
  342.             {  
  343.                 if (fgDetector != null)  
  344.                 {  
  345.                     fgDetector.Dispose();  
  346.                     fgDetector = null;  
  347.                 }  
  348.                 fgDetector = new FGDetector<Bgr>(FORGROUND_DETECTOR_TYPE.MOG);  
  349.             }  
  350.             else if (rbFrameDiff.Checked)  
  351.             {  
  352.                 if (bgModelFrameDiff != null)  
  353.                 {  
  354.                     bgModelFrameDiff.Dispose();  
  355.                     bgModelFrameDiff = null;  
  356.                 }  
  357.                 bgModelFrameDiff = new BackgroundStatModelFrameDiff<Bgr>();  
  358.             }  
  359.             else if (rbAccAvg.Checked)  
  360.             {  
  361.                 if (bgModelAccAvg != null)  
  362.                 {  
  363.                     bgModelAccAvg.Dispose();  
  364.                     bgModelAccAvg = null;  
  365.                 }  
  366.                 bgModelAccAvg = new BackgroundStatModelAccAvg<Bgr>();  
  367.             }  
  368.             else if (rbRunningAvg.Checked)  
  369.             {  
  370.                 if (bgModelRunningAvg != null)  
  371.                 {  
  372.                     bgModelRunningAvg.Dispose();  
  373.                     bgModelRunningAvg = null;  
  374.                 }  
  375.                 bgModelRunningAvg = new BackgroundStatModelRunningAvg<Bgr>();  
  376.             }  
  377.             else if (rbSquareAcc.Checked)  
  378.             {  
  379.                 if (bgModelSquareAcc != null)  
  380.                 {  
  381.                     bgModelSquareAcc.Dispose();  
  382.                     bgModelSquareAcc = null;  
  383.                 }  
  384.                 bgModelSquareAcc = new BackgroundStatModelSquareAcc<Bgr>();  
  385.             }  
  386.             else if (rbMultiplyAcc.Checked)  
  387.             {  
  388.                 if (bgModelMultiplyAcc != null)  
  389.                 {  
  390.                     bgModelMultiplyAcc.Dispose();  
  391.                     bgModelMultiplyAcc = null;  
  392.                 }  
  393.                 bgModelMultiplyAcc = new BackgroundStatModelMultiplyAcc<Bgr>();  
  394.             }  
  395.             backgroundModelFrameCount = 0;  
  396.             isBackgroundModeling = true;  
  397.             btnStartBackgroundModel.Enabled = false;  
  398.             btnStopBackgroundModel.Enabled = true;  
  399.             btnStartForegroundDetect.Enabled = false;  
  400.             btnStopForegroundDetect.Enabled = false;  
  401.         }  
  402.   
  403.         //停止背景建模   
  404.         private void btnStopBackgroundModel_Click(object sender, EventArgs e)  
  405.         {  
  406.             StopBackgroundModel();  
  407.         }  
  408.   
  409.         //停止背景建模   
  410.         private void StopBackgroundModel()  
  411.         {  
  412.             lock (lockObject)  
  413.             {  
  414.                 isBackgroundModeling = false;  
  415.             }  
  416.             btnStartBackgroundModel.Enabled = true;  
  417.             btnStopBackgroundModel.Enabled = false;  
  418.             btnStartForegroundDetect.Enabled = true;  
  419.             btnStopForegroundDetect.Enabled = false;  
  420.         }  
  421.   
  422.         //开始前景检测   
  423.         private void btnStartForegroundDetect_Click(object sender, EventArgs e)  
  424.         {  
  425.             isForegroundDetecting = true;  
  426.             btnStartBackgroundModel.Enabled = false;  
  427.             btnStopBackgroundModel.Enabled = false;  
  428.             btnStartForegroundDetect.Enabled = false;  
  429.             btnStopForegroundDetect.Enabled = true;  
  430.         }  
  431.   
  432.         //停止前景检测   
  433.         private void btnStopForegroundDetect_Click(object sender, EventArgs e)  
  434.         {  
  435.             lock (lockObject)  
  436.             {  
  437.                 isForegroundDetecting = false;  
  438.             }  
  439.             btnStartBackgroundModel.Enabled = true;  
  440.             btnStopBackgroundModel.Enabled = false;  
  441.             btnStartForegroundDetect.Enabled = true;  
  442.             btnStopForegroundDetect.Enabled = false;  
  443.         }  
  444.     }  
  445.   
  446.     //前景检测方法枚举   
  447.     public enum ForegroundDetectType  
  448.     {  
  449.         FrameDiff,  
  450.         AccAvg,  
  451.         RunningAvg,  
  452.         MultiplyAcc,  
  453.         SquareAcc,  
  454.         CodeBook,  
  455.         Mog,  
  456.         Fgd  
  457.     }  
  458.   
  459.     //设置参数   
  460.     public struct SettingParam  
  461.     {  
  462.         public ForegroundDetectType ForegroundDetectType;  
  463.         public int MaxBackgroundModelFrameCount;  
  464.         public double Threshold;  
  465.   
  466.         public SettingParam(ForegroundDetectType foregroundDetectType, int maxBackgroundModelFrameCount, double threshold)  
  467.         {  
  468.             ForegroundDetectType = foregroundDetectType;  
  469.             MaxBackgroundModelFrameCount = maxBackgroundModelFrameCount;  
  470.             Threshold = threshold;  
  471.         }  
  472.     }  
  473. }  

   另外,细心的读者发现我忘记贴OpenCvInvoke类的实现代码了,这里补上。多谢指正。

[cpp] view plaincopyprint?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Drawing;  
  6. using System.Runtime.InteropServices;  
  7. using Emgu.CV.Structure;  
  8. using Emgu.CV.CvEnum;  
  9.   
  10. namespace ImageProcessLearn  
  11. {  
  12.     /// <summary>   
  13.     /// 声明一些没有包含在EmguCv中的OpenCv函数   
  14.     /// </summary>   
  15.     public static class OpenCvInvoke  
  16.     {  
  17.         //自适应动态背景检测   
  18.         [DllImport("cvaux200.dll")]  
  19.         public static extern void cvChangeDetection(IntPtr prev_frame, IntPtr curr_frame, IntPtr change_mask);  
  20.   
  21.         //均值漂移分割   
  22.         [DllImport("cv200.dll")]  
  23.         public static extern void cvPyrMeanShiftFiltering(IntPtr src, IntPtr dst, double spatialRadius, double colorRadius, int max_level, MCvTermCriteria termcrit);  
  24.   
  25.         //开始查找轮廓   
  26.         [DllImport("cv200.dll")]  
  27.         public static extern IntPtr cvStartFindContours(IntPtr image, IntPtr storage, int header_size, RETR_TYPE mode, CHAIN_APPROX_METHOD method, Point offset);  
  28.   
  29.         //查找下一个轮廓   
  30.         [DllImport("cv200.dll")]  
  31.         public static extern IntPtr cvFindNextContour(IntPtr scanner);  
  32.   
  33.         //用新轮廓替换scanner指向的当前轮廓   
  34.         [DllImport("cv200.dll")]  
  35.         public static extern void cvSubstituteContour(IntPtr scanner, IntPtr new_contour);  
  36.   
  37.         //结束轮廓查找   
  38.         [DllImport("cv200.dll")]  
  39.         public static extern IntPtr cvEndFindContour(ref IntPtr scanner);  
  40.     }  
  41. }  

后记
    值得注意的是,本文提到的OpenCv函数目前属于CvAux系列,以后也许会加入到正式的图像处理Cv系列,也许以后会消失。最重要的是它们还没有正式的文档。

    其实关于背景模型的方法还有很多,比如《Video-object segmentation using multi-sprite background subtraction》可以在摄像机运动的情况下建立背景《Nonparametric background generation》利用mean-shift算法处理动态的背景模型,如果我的时间和能力允许,也许会去尝试实现它们。另外,《Wallflower: Principles and practice of background maintenance》比较了各种背景建模方式的差异,我希望能够尝试翻译出来。

    感谢您耐心看完本文,希望对您有所帮助。



原创粉丝点击