本文的主要内容来自2009 Advanced Video and Signal Based Surveillance会议的一篇论文“Real-Time Moving Object Detection for Video Surveillance”,要看原文内容请参考文后给出的链接。申明二点:① 本文是根据提到的论文翻译过来的,但不完全与原文相同;②代码实现部分,在detect函数部分,逻辑有问题,没达到预期的要求,勿吐槽。废话少说,下面开始来介绍该论文。

初步查阅该文献,是由于网上的一篇博文,对该文进行了大肆的褒扬,遂对该文产生了一定的兴趣,这或许也和自己的背景相关,一直以来也在从事这方面的研究和工作。论文的思想很简单,大致描述如下:将图像分成4×4互不重叠的patches,然后对每一patch进行dct(离散余弦变换)变换(DCT与ICA和PCA的区别请参考相关文献),接着提取dct系数的低频成分作为特征,进行背景建模;而对于新输入的图像帧,则做同样处理,与抽取的背景模型特征进行比较,判断是否相似,采取空间邻域机制对噪声进行控制,达到准确前景提取的目的。下面根据论文的框架对每一部分进行详细介绍。

1)背景建模(Background Modelling)

背景模型是由多个dct系数向量组成的,不同空间的背景Patches可能有不同的coefficient vectors。对于每一patche,按照DCT公式进行变换,如式(1):

变换后,则可以得到DCT系数矩阵,如图1所示:

根据DCT变换的特点,抽取位置为(1,2)、(1,3)、(2,1)、(2,2)、(3,1)五个系数构成系数矩阵,作为背景模型。对每一Patch依次这样处理,则完成了背景模型的建立。

2)背景模型自适应 (Background Adaptation)

考虑到场景的动态变化以及噪声的影响,根据上面建立的背景模型难以对噪声和动态场景具有适应性,为了满足动态场景的需求,有必要对背景模型的自适应进行深入的研究。对于一个 newly coming patch,提取系数向量(Coefficient Vector),与背景模型进行比较,判断是否相似,相似的判断依据是两个向量的夹角是否大于某一阈值,如果匹配,则找到最匹配的模型,并对该模型对应的权重系数进行如下更新:

其中Tinc和Tdec是常量,alphai是该模型对应的权重,每个模型初始化的权重为Tinc。如果没有匹配上,这后面的就是重点,这时就需要判断该Patch在上一帧的邻域范围内是否有最可能的前景(Almost Foreground)Patch,如果没有,则判为Almost Foreground Patch,并将其融入背景模型中。

3)前景检测(Foreground Detection)

前景Patch的判断与前景的背景自适应差不多,这里不再细说。只是提下,文章中将长期滞留在场景中的运动目标融入了背景模型,这样能提高算法的性能,当然有特殊需求的(如遗留物检测等)可能需要保留滞留在场景中的物体。

最后,加点个人见解。我们常规的对背景进行建模都是在空间域进行的,而作者将图像分成Patches,对每一Patch都采用DCT变换,对每一块在频率域内进行建模,在思路上也是一大创新;另外,作者没有保留全部DCT系数,而是抽取了变换后的表示低频信息的系数(这样能减少细节信息,保留结构信息,提高对噪声和光照的影响)对背景进行建模,减少了计算量。当然,这篇文章也存在不足,基于Patch的检测对于检测精度要求较高的场合是不适应,而且在对于一些本来就不相连的目标,通过Patch-based的检测后,可能就粘连在了一起,尤其是对于还后面多目标跟踪或目标识别等影响还是比较大的。

本人也对原文算法进行了实验,但能力有限,算法实现过程中,存在一些问题,有兴趣的朋友可以进行分析下(问题主要在DctDetect类的detect()函数中),当然也可以通过文后的链接来直接下载。

头文件DctDetect.hpp如下:

  1. #pragma once
  2. #include <opencv2/core/core.hpp>
  3. #include <opencv2/highgui/highgui.hpp>
  4. #include <opencv2/imgproc/imgproc.hpp>
  5. #include <cmath>
  6. #include <iostream>
  7. using namespace std;
  8. using namespace cv;
  9. //Bounding Boxes
  10. struct BoundingBox : public cv::Rect {
  11. BoundingBox(){}
  12. BoundingBox(cv::Rect r): cv::Rect(r){}
  13. public:
  14. int status; // 状态:0 表示背景,1表示前景,2表示可能前景
  15. int count; // 标注为前景的次数
  16. int prev_status; // 上次的状态
  17. };
  18. typedef struct _Elem
  19. {
  20. vector<float> m_data;
  21. float m_weight;
  22. }ELEM;  // 定义新的数据结构
  23. typedef vector<ELEM> DATA;  // 冲定义数据类型
  24. class DctDetect
  25. {
  26. public:
  27. DctDetect(void);
  28. DctDetect(Mat& frame );
  29. void detect( Mat& frame); // 检测
  30. Mat& getForeground(){ return m_foreground;}
  31. ~DctDetect(void);
  32. private:
  33. void calcDct(Mat& frame, vector<float>& coff);
  34. float calcDist( vector<float>& coff1, vector<float>& coff2);
  35. void buildGrid( Mat& frame, Rect& box);
  36. float dotProduct( vector<float>& coff1, vector<float>& coff2);
  37. bool checkNeighbor(int r,int c);
  38. void chageGridStatus();
  39. private:
  40. int m_height;
  41. int m_width;
  42. Rect m_rect;
  43. int m_frmNum;
  44. int m_gridRows; // 模型的行数
  45. int m_gridCols; // 模型的列数
  46. Mat m_foreground;
  47. float m_threshold;  // 阈值
  48. float m_inc;
  49. float m_dec;
  50. vector<vector<BoundingBox>> m_grid;
  51. vector<vector<DATA>> m_model;
  52. };

实现DctDetect.cpp文件如下:

  1. #include "DctDetect.h"
  2. DctDetect::DctDetect(void)
  3. {
  4. }
  5. DctDetect::DctDetect(Mat& frame )
  6. {
  7. m_frmNum = 0;
  8. m_gridCols = 0;
  9. m_gridRows = 0;
  10. m_inc = 1.0;
  11. m_dec = 0.1;
  12. //m_threshold = 0.50;
  13. m_threshold = sqrtf(3.0)/2.0;  // cos(45°)= sqrtf(2.0)/2
  14. m_height = frame.rows;
  15. m_width = frame.cols;
  16. m_rect.x = 0;
  17. m_rect.y = 0;
  18. m_rect.width = 4;
  19. m_rect.height = 4;
  20. m_foreground.create( m_height, m_width, CV_8UC1 );
  21. buildGrid(frame, m_rect);
  22. vector<float> coff;
  23. ELEM _elem;
  24. vector<ELEM> _data;
  25. vector<DATA> v_data;
  26. for ( int i=0; i< m_gridRows; ++i )
  27. {
  28. v_data.clear();
  29. for ( int j=0; j< m_gridCols; ++j )
  30. {
  31. _data.clear();
  32. calcDct(frame(m_grid[i][j]), coff );
  33. _elem.m_data = coff;
  34. _elem.m_weight = m_inc;
  35. _data.push_back( _elem );
  36. v_data.push_back(_data);
  37. }
  38. m_model.push_back(v_data);
  39. }
  40. }
  41. void DctDetect::buildGrid(Mat& frame, Rect& box)
  42. {
  43. int width =  box.width;
  44. int height = box.height;
  45. BoundingBox bbox;
  46. vector<BoundingBox> inGrid;
  47. for (int y=1;y<frame.rows-height;y+= height )
  48. {
  49. inGrid.clear();
  50. m_gridCols = 0;
  51. for (int x=1;x<frame.cols-width;x+=width)
  52. {
  53. bbox.x = x;
  54. bbox.y = y;
  55. bbox.width = width;
  56. bbox.height = height;
  57. bbox.status = -1;
  58. bbox.prev_status = 0;
  59. bbox.count = 0;
  60. inGrid.push_back(bbox);
  61. m_gridCols++;
  62. }
  63. m_grid.push_back(inGrid);
  64. m_gridRows++;
  65. }
  66. }
  67. // 计算DCT系数
  68. void DctDetect::calcDct(Mat& frame, vector<float>& coff)
  69. {
  70. if ( frame.empty() )
  71. return;
  72. Mat temp;
  73. if ( 1 == frame.channels())
  74. frame.copyTo( temp);
  75. else
  76. cvtColor( frame, temp, CV_BGR2GRAY);
  77. Mat tempMat( frame.rows, frame.cols, CV_64FC1);
  78. Mat tempDct( frame.rows, frame.cols, CV_64FC1);
  79. temp.convertTo( tempMat, tempMat.type());
  80. dct( tempMat, tempDct, CV_DXT_FORWARD );    // DCT变换
  81. coff.clear();
  82. coff.push_back((float)tempDct.at<double>(0,1) );  // 取值 ( 0,1 )、( 0,2 )、( 1,0 )、( 1,1 )、( 2,0 )
  83. coff.push_back((float)tempDct.at<double>(0,2) );
  84. coff.push_back((float)tempDct.at<double>(1,0) );
  85. coff.push_back((float)tempDct.at<double>(1,1) );
  86. coff.push_back((float)tempDct.at<double>(2,0) );
  87. if ( !temp.empty())
  88. temp.release();
  89. if ( !tempMat.empty())
  90. tempMat.release();
  91. if ( !tempDct.empty())
  92. tempDct.release();
  93. }
  94. // 计算距离
  95. float DctDetect::calcDist(vector<float>& coff1, vector<float>& coff2)
  96. {
  97. float d1 = norm( coff1 );
  98. float d2 = norm( coff2 );
  99. float d3 = dotProduct( coff1,coff2 );
  100. if ( d2 <0.0001 )
  101. return 1.0;
  102. else
  103. return d3/(d1*d2);
  104. }
  105. // 点积
  106. float DctDetect::dotProduct( vector<float>& coff1, vector<float>& coff2)
  107. {
  108. size_t i = 0, n = coff1.size();
  109. assert(coff1.size() == coff2.size());
  110. float s = 0.0f;
  111. const float *ptr1 = &coff1[0], *ptr2 = &coff2[0];
  112. for( ; i < n; i++ )
  113. s += (float)ptr1[i]*ptr2[i];
  114. return s;
  115. }
  116. // 检测邻域是否有前景,有则返回true
  117. bool DctDetect::checkNeighbor(int r,int c)
  118. {
  119. int count = 0;
  120. if ( (r-1) >=0 && m_grid[r-1][c].prev_status == 1)  // 上面patch
  121. count++;
  122. if ( (c+1) < m_gridCols && m_grid[r][c+1].prev_status == 1)  // 右边patch
  123. count++;
  124. if ( (r+1) < m_gridRows && m_grid[r+1][c].prev_status == 1)  // 下面patch
  125. count++;
  126. if ( (c-1) >= 0 && m_grid[r][c-1].prev_status == 1)  // 左边patch
  127. count++;
  128. if ( count > 1 )
  129. return true;
  130. else
  131. return false;
  132. }
  133. void DctDetect::detect(Mat& frame)
  134. {
  135. m_foreground = 0;
  136. float dist = 0.0f;
  137. vector<float> coff;
  138. ELEM _elem;  // 单个数据
  139. vector<ELEM> _data; // 模型数据
  140. for ( int i=0; i< m_gridRows; ++i )
  141. {
  142. for ( int j=0; j< m_gridCols; ++j )
  143. {
  144. calcDct(frame(m_grid[i][j]), coff );
  145. _data = m_model[i][j];
  146. int mNum = _data.size(); // 模型的个数
  147. float fmax = FLT_MIN;
  148. int idx = -1;
  149. for ( int k=0; k<mNum; ++k )
  150. {
  151. dist = calcDist( coff, _data[k].m_data );
  152. if ( dist > fmax )
  153. {
  154. fmax = dist;
  155. idx = j;
  156. }
  157. } // 匹配完成
  158. if ( fmax > m_threshold )  // 匹配上
  159. {
  160. for ( int k=0; k<mNum; ++k )
  161. {
  162. if ( idx ==j )  // 匹配上的模型权重增加
  163. m_model[i][j][k].m_weight +=m_inc ;
  164. else
  165. m_model[i][j][k].m_weight -=m_dec;
  166. }
  167. }
  168. else  // 如果没有匹配上,则检测上次邻域内是否有前景
  169. {
  170. bool isNeighbor = checkNeighbor(i,j);
  171. if ( isNeighbor )  // 如果邻域内有前景,则标注为前景区域
  172. {
  173. m_foreground(m_grid[i][j]) =255;
  174. m_grid[i][j].count +=1;
  175. }
  176. else
  177. {
  178. m_grid[i][j].status = 1;
  179. _data = m_model[i][j]; // 加入背景模型
  180. _elem.m_data = coff;
  181. _elem.m_weight = m_inc;
  182. _data.push_back( _elem );
  183. m_model[i][j]= _data;
  184. }
  185. }
  186. // 剔除背景中值为负数的模型
  187. vector<ELEM> _temp;
  188. _data = m_model[i][j];
  189. mNum = _data.size();
  190. for ( int k=0; k<mNum; ++k )
  191. {
  192. if ( _data[k].m_weight<0)
  193. continue;
  194. else
  195. {
  196. if ( _data[k].m_weight>20.0 )
  197. _data[k].m_weight = 20.0;
  198. _temp.push_back( _data[k] );
  199. }
  200. }
  201. _data.clear();
  202. _data.insert( _data.begin(), _temp.begin(), _temp.end());
  203. m_model[i][j]= _data;
  204. }  // end for j
  205. } // end for i
  206. chageGridStatus();
  207. }
  208. void DctDetect::chageGridStatus()
  209. {
  210. for ( int i=0; i<m_gridRows; ++i )
  211. {
  212. for ( int j=0; j<m_gridCols; ++j )
  213. {
  214. m_grid[i][j].prev_status = m_grid[i][j].status ;
  215. m_grid[i][j].status = 0;
  216. }
  217. }
  218. }
  219. DctDetect::~DctDetect(void)
  220. {
  221. }

论文下载地址:Real-Time Moving Object Detection for Video Surveillance

程序代码下载地址:基于DCT系数背景建模与运动目标检测算法V1.0

基于DCT系数的实时监控中运动目标检测的更多相关文章

  1. 基于邮件系统的远程实时监控系统的实现 Python版

    人生苦短,我用Python~ 界内的Python宣传标语,对Python而言,这是种标榜,实际上,Python确实是当下最好用的开发语言之一. 在相继学习了C++/C#/Java之后,接触Python ...

  2. 【计算机视觉】基于样本一致性的背景减除运动目标检测算法(SACON)

    SACON(SAmple CONsensus)算法是基于样本一致性的运动目标检测算法.该算法通过对每个像素进行样本一致性判断来判定像素是否为背景. 算法框架图 由上图可知,该算法主要分为四个主要部分, ...

  3. 基于DCT的图片数字水印实验

    1. 实验类别 设计型实验:MATLAB设计并实现基于DCT的图像数字水印算法. 2. 实验目的 了解基于DCT的图像数字水印技术,掌握基于DCT系数关系的图像水印算法原理,设计并实现一种基于DCT的 ...

  4. 优化IPOL网站中基于DCT(离散余弦变换)的图像去噪算法(附源代码)。

    在您阅读本文前,先需要告诉你的是:即使是本文优化过的算法,DCT去噪的计算量依旧很大,请不要向这个算法提出实时运行的苛刻要求. 言归正传,在IPOL网站中有一篇基于DCT的图像去噪文章,具体的链接地址 ...

  5. 项目-基于视频压缩的实时监控系统--tiny6410

    项目-基于视频压缩的实时监控系统--tiny6410 @国嵌linux学习笔记. 1. 构造服务端结构体 server struct server { int epfd; //保存epoll指针 st ...

  6. 性能测试 基于Python结合InfluxDB及Grafana图表实时监控Android系统和应用进程

    基于Python结合InfluxDB及Grafana图表实时监控Android系统和应用进程   By: 授客 QQ:1033553122     1. 测试环境 2. 实现功能 3. 使用前提 4. ...

  7. Python 基于Python结合pykafka实现kafka生产及消费速率&主题分区偏移实时监控

    基于Python结合pykafka实现kafka生产及消费速率&主题分区偏移实时监控   By: 授客 QQ:1033553122   1.测试环境 python 3.4 zookeeper- ...

  8. 搭建属于你的家庭网络实时监控–HTML5在嵌入式系统中的应用&#183;高级篇

    *本文已刊登在<无线电>2014年第6期 <搭建属于你的在线实时採集系统>中已经对HTML5平台有了初步的认识,并基于此向大家展示了怎样将採集到的数据上传至网络.实现实时观測. ...

  9. Python中Celery 的基本用法以及Django 结合 Celery 的使用和实时监控进程

    celery是什么 1 celery是一个简单,灵活且可靠的,处理大量消息的分布式系统 2 专注于实时处理的异步任务队列 3 同时也支持任务调度 执行流程 Celery 基本使用 tasks.py i ...

随机推荐

  1. CSP201703-1:分蛋糕

    引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...

  2. 数据库Mysql的学习(一)-启动和进入

    数据库:按照数据结构来组织储存和管理数据的仓库. Mysql是关系型数据库管理系统 Mysql安装好之后... mysql的启动 1:通过控制面板里的”服务“找到mysql右键启动即可 2:开始菜单搜 ...

  3. POJ 2184 Cow Exhabition

    "Fat and docile, big and dumb, they look so stupid, they aren't much fun..." - Cows with G ...

  4. Bus of Characters(栈和队列)

    In the Bus of Characters there are nn rows of seat, each having 22 seats. The width of both seats in ...

  5. POJ 2229 计数DP

    dp[i]代表是数字i的最多组合数如果i是一个奇数,i的任意一个组合都包含1,所以dp[i] = dp[i-1] 如果i是一个偶数,分两种情况讨论,一种是序列中包含1,因此dp[i]=dp[i-1]一 ...

  6. 在用js拼接html时,给元素加不上事件的问题

    问题描述:有时,发起ajax请求成功后,需要用js去拼接一小段html字符串,然后给某些元素添加事件时,事件总是加不上. 解决办法:在success 回调函数内,给元素添加事件绑定. 代码如下: $. ...

  7. headers的描述

    Cache-Control 作用: 这个是非常重要的规则. 这个用来指定Response-Request遵循的缓存机制.各个指令含义如下 Cache-Control:Public   可以被任何缓存所 ...

  8. 关于new delete的说明

    1. 删除空指针不会有问题,因为C++的标准规定在delete时首先会判断指针是否为空,为空就不再处理,所以也就不会有问题. 2. delete一个非空指针之后,并不会将该指针自动置为空.此时如果重复 ...

  9. tcp发送缓冲区中的数据都是由产生数据的进程给推送到ip层还是有定时任务触发?

    和几个变量有非常大的关系 发送缓冲区的大小,如何单独设置一个socket的发送缓冲区 socketopt 发送缓冲区中的数据,如果被拥塞窗口限制住了,那么这些数据可能就放在tcpbuffer里的,此时 ...

  10. IO复用、多进程和多线程三种并发编程模型

    I/O复用模型 I/O复用原理:让应用程序可以同时对多个I/O端口进行监控以判断其上的操作是否可以进行,达到时间复用的目的.在书上看到一个例子来解释I/O的原理,我觉得很形象,如果用监控来自10根不同 ...