基于DCT系数的实时监控中运动目标检测
本文的主要内容来自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如下:
- #pragma once
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- #include <cmath>
- #include <iostream>
- using namespace std;
- using namespace cv;
- //Bounding Boxes
- struct BoundingBox : public cv::Rect {
- BoundingBox(){}
- BoundingBox(cv::Rect r): cv::Rect(r){}
- public:
- int status; // 状态:0 表示背景,1表示前景,2表示可能前景
- int count; // 标注为前景的次数
- int prev_status; // 上次的状态
- };
- typedef struct _Elem
- {
- vector<float> m_data;
- float m_weight;
- }ELEM; // 定义新的数据结构
- typedef vector<ELEM> DATA; // 冲定义数据类型
- class DctDetect
- {
- public:
- DctDetect(void);
- DctDetect(Mat& frame );
- void detect( Mat& frame); // 检测
- Mat& getForeground(){ return m_foreground;}
- ~DctDetect(void);
- private:
- void calcDct(Mat& frame, vector<float>& coff);
- float calcDist( vector<float>& coff1, vector<float>& coff2);
- void buildGrid( Mat& frame, Rect& box);
- float dotProduct( vector<float>& coff1, vector<float>& coff2);
- bool checkNeighbor(int r,int c);
- void chageGridStatus();
- private:
- int m_height;
- int m_width;
- Rect m_rect;
- int m_frmNum;
- int m_gridRows; // 模型的行数
- int m_gridCols; // 模型的列数
- Mat m_foreground;
- float m_threshold; // 阈值
- float m_inc;
- float m_dec;
- vector<vector<BoundingBox>> m_grid;
- vector<vector<DATA>> m_model;
- };
实现DctDetect.cpp文件如下:
- #include "DctDetect.h"
- DctDetect::DctDetect(void)
- {
- }
- DctDetect::DctDetect(Mat& frame )
- {
- m_frmNum = 0;
- m_gridCols = 0;
- m_gridRows = 0;
- m_inc = 1.0;
- m_dec = 0.1;
- //m_threshold = 0.50;
- m_threshold = sqrtf(3.0)/2.0; // cos(45°)= sqrtf(2.0)/2
- m_height = frame.rows;
- m_width = frame.cols;
- m_rect.x = 0;
- m_rect.y = 0;
- m_rect.width = 4;
- m_rect.height = 4;
- m_foreground.create( m_height, m_width, CV_8UC1 );
- buildGrid(frame, m_rect);
- vector<float> coff;
- ELEM _elem;
- vector<ELEM> _data;
- vector<DATA> v_data;
- for ( int i=0; i< m_gridRows; ++i )
- {
- v_data.clear();
- for ( int j=0; j< m_gridCols; ++j )
- {
- _data.clear();
- calcDct(frame(m_grid[i][j]), coff );
- _elem.m_data = coff;
- _elem.m_weight = m_inc;
- _data.push_back( _elem );
- v_data.push_back(_data);
- }
- m_model.push_back(v_data);
- }
- }
- void DctDetect::buildGrid(Mat& frame, Rect& box)
- {
- int width = box.width;
- int height = box.height;
- BoundingBox bbox;
- vector<BoundingBox> inGrid;
- for (int y=1;y<frame.rows-height;y+= height )
- {
- inGrid.clear();
- m_gridCols = 0;
- for (int x=1;x<frame.cols-width;x+=width)
- {
- bbox.x = x;
- bbox.y = y;
- bbox.width = width;
- bbox.height = height;
- bbox.status = -1;
- bbox.prev_status = 0;
- bbox.count = 0;
- inGrid.push_back(bbox);
- m_gridCols++;
- }
- m_grid.push_back(inGrid);
- m_gridRows++;
- }
- }
- // 计算DCT系数
- void DctDetect::calcDct(Mat& frame, vector<float>& coff)
- {
- if ( frame.empty() )
- return;
- Mat temp;
- if ( 1 == frame.channels())
- frame.copyTo( temp);
- else
- cvtColor( frame, temp, CV_BGR2GRAY);
- Mat tempMat( frame.rows, frame.cols, CV_64FC1);
- Mat tempDct( frame.rows, frame.cols, CV_64FC1);
- temp.convertTo( tempMat, tempMat.type());
- dct( tempMat, tempDct, CV_DXT_FORWARD ); // DCT变换
- coff.clear();
- coff.push_back((float)tempDct.at<double>(0,1) ); // 取值 ( 0,1 )、( 0,2 )、( 1,0 )、( 1,1 )、( 2,0 )
- coff.push_back((float)tempDct.at<double>(0,2) );
- coff.push_back((float)tempDct.at<double>(1,0) );
- coff.push_back((float)tempDct.at<double>(1,1) );
- coff.push_back((float)tempDct.at<double>(2,0) );
- if ( !temp.empty())
- temp.release();
- if ( !tempMat.empty())
- tempMat.release();
- if ( !tempDct.empty())
- tempDct.release();
- }
- // 计算距离
- float DctDetect::calcDist(vector<float>& coff1, vector<float>& coff2)
- {
- float d1 = norm( coff1 );
- float d2 = norm( coff2 );
- float d3 = dotProduct( coff1,coff2 );
- if ( d2 <0.0001 )
- return 1.0;
- else
- return d3/(d1*d2);
- }
- // 点积
- float DctDetect::dotProduct( vector<float>& coff1, vector<float>& coff2)
- {
- size_t i = 0, n = coff1.size();
- assert(coff1.size() == coff2.size());
- float s = 0.0f;
- const float *ptr1 = &coff1[0], *ptr2 = &coff2[0];
- for( ; i < n; i++ )
- s += (float)ptr1[i]*ptr2[i];
- return s;
- }
- // 检测邻域是否有前景,有则返回true
- bool DctDetect::checkNeighbor(int r,int c)
- {
- int count = 0;
- if ( (r-1) >=0 && m_grid[r-1][c].prev_status == 1) // 上面patch
- count++;
- if ( (c+1) < m_gridCols && m_grid[r][c+1].prev_status == 1) // 右边patch
- count++;
- if ( (r+1) < m_gridRows && m_grid[r+1][c].prev_status == 1) // 下面patch
- count++;
- if ( (c-1) >= 0 && m_grid[r][c-1].prev_status == 1) // 左边patch
- count++;
- if ( count > 1 )
- return true;
- else
- return false;
- }
- void DctDetect::detect(Mat& frame)
- {
- m_foreground = 0;
- float dist = 0.0f;
- vector<float> coff;
- ELEM _elem; // 单个数据
- vector<ELEM> _data; // 模型数据
- for ( int i=0; i< m_gridRows; ++i )
- {
- for ( int j=0; j< m_gridCols; ++j )
- {
- calcDct(frame(m_grid[i][j]), coff );
- _data = m_model[i][j];
- int mNum = _data.size(); // 模型的个数
- float fmax = FLT_MIN;
- int idx = -1;
- for ( int k=0; k<mNum; ++k )
- {
- dist = calcDist( coff, _data[k].m_data );
- if ( dist > fmax )
- {
- fmax = dist;
- idx = j;
- }
- } // 匹配完成
- if ( fmax > m_threshold ) // 匹配上
- {
- for ( int k=0; k<mNum; ++k )
- {
- if ( idx ==j ) // 匹配上的模型权重增加
- m_model[i][j][k].m_weight +=m_inc ;
- else
- m_model[i][j][k].m_weight -=m_dec;
- }
- }
- else // 如果没有匹配上,则检测上次邻域内是否有前景
- {
- bool isNeighbor = checkNeighbor(i,j);
- if ( isNeighbor ) // 如果邻域内有前景,则标注为前景区域
- {
- m_foreground(m_grid[i][j]) =255;
- m_grid[i][j].count +=1;
- }
- else
- {
- m_grid[i][j].status = 1;
- _data = m_model[i][j]; // 加入背景模型
- _elem.m_data = coff;
- _elem.m_weight = m_inc;
- _data.push_back( _elem );
- m_model[i][j]= _data;
- }
- }
- // 剔除背景中值为负数的模型
- vector<ELEM> _temp;
- _data = m_model[i][j];
- mNum = _data.size();
- for ( int k=0; k<mNum; ++k )
- {
- if ( _data[k].m_weight<0)
- continue;
- else
- {
- if ( _data[k].m_weight>20.0 )
- _data[k].m_weight = 20.0;
- _temp.push_back( _data[k] );
- }
- }
- _data.clear();
- _data.insert( _data.begin(), _temp.begin(), _temp.end());
- m_model[i][j]= _data;
- } // end for j
- } // end for i
- chageGridStatus();
- }
- void DctDetect::chageGridStatus()
- {
- for ( int i=0; i<m_gridRows; ++i )
- {
- for ( int j=0; j<m_gridCols; ++j )
- {
- m_grid[i][j].prev_status = m_grid[i][j].status ;
- m_grid[i][j].status = 0;
- }
- }
- }
- DctDetect::~DctDetect(void)
- {
- }
论文下载地址:Real-Time Moving Object Detection for Video Surveillance
程序代码下载地址:基于DCT系数背景建模与运动目标检测算法V1.0
转载于:https://www.cnblogs.com/ywsoftware/p/4502356.html
基于DCT系数的实时监控中运动目标检测相关推荐
- 基于目标追踪算法、web、gui开发的程序,可实时监控画面、检测目标、监听电脑配置
基于目标追踪算法.web.gui开发的程序,可实时监控画面.检测目标.监听电脑配置,此项目由软件+网页设计而成,请看项目展示.
- OpenCV实现远程视频监控与运动目标检测
远程视频监控与运动目标检测 本来是一次网络编程课的大作业,在做的过程中发现了蛮多问题,之后也一一调试完成,总的来说并不复杂,但也学到蛮多东西的,所以打算放上来 目的 随着图像处理技术和无线通信技术的发 ...
- 【Python】监控视频中运动目标检测的代码实现及效果展示
0.介绍 基于python,使用opencv库函数,实现监控视频中的运动目标检测,Mark一下! 干扰性和灵敏度的权衡,可通过调节代码中的参数(高斯模糊核.面积阈值.帧差间隔等)进行设置. 1.代码 ...
- 当复选框中打勾时后面自动显示y或者n_基于轮廓系数确定K-Means聚类中的K
一.概述 K-Means(K均值)是机器学习中一种常见的无监督算法,它能够将未知标签的数据,根据它们的特征分成不同组,每一组数据又称为"簇",每一簇的中心点称为"质心&q ...
- 基于邮件系统的远程实时监控系统的实现 Python版
人生苦短,我用Python~ 界内的Python宣传标语,对Python而言,这是种标榜,实际上,Python确实是当下最好用的开发语言之一. 在相继学习了C++/C#/Java之后,接触Python ...
- python实现实时监控_基于 Python 的交换机实时监控系统的设计与实现
从高校校园网运维工作实际出发,论文提出了一种基于 Python 语言+SNMP协议的网络交换机监测系统设计思路与实现方法.整个系统系统采用B/S模式,基于轻量级的web开发框架web.py实现.后端采 ...
- 基于树莓派的远程实时监控(内网穿透)
/*本文系湛江市第十七中学星火创客团队及岭南师范学院物联网俱乐部原创部分参赛项目,转载请保留声明 */ 一.项目背景与目标: 此项目基于一个师弟的留言:如何实现在学校的校园网(办公网)内实现内网穿透. ...
- [导入]基于Web的B/S结构实时监控系统[转]
文章编号:1009-0193(2002)01-0062-02 基于Web的B/S结构实时监控系统 尉学军,刘 跃 (贵州工业大学 电气工程学院,贵州 贵阳 550003) 摘 要:提出了怎样利用Web ...
- 基于android手机实时监控ipcam视频之一:RTSP
我以前做过一个这样的项目,基于android实现手机实时监控ipcam,ipcam厂商提供控件,该控件安装以后,在IE上面输入ipcam的ip地址,就可以实时查看ipcam的图像,这实时视频是通过HT ...
最新文章
- 学习《Linux设备模型浅析之设备篇》笔记(三)
- qdockwidget设置隐藏标题栏,重叠时tab标签位置,自动填充满整个窗口
- C#用 SendKyes 结合 Process 或 API FindWindow、SendMessage(PostMessage) 等控制外部程序
- 整数加扰java_生成随机顺序,但在java中有约束
- docker安装的环境
- python调用百度地图画轨迹图_[宜配屋]听图阁
- 知识库的构建 6-1 语义网 Semantic Web
- SQL Server数据库有关语法
- 来自一个从事单片机工作中遇到的真人真事,单片机从业者可以借鉴
- psql 命令行使用
- html制作古诗网页早发白帝城,《早发白帝城》古诗词
- python中md5加密的实现(hashlib)
- PS 2019 Mac版 自学入门系列(八)—— 替换背景
- 技术贴(1):将移动硬盘名从F盘改为E盘
- android studio 正式版本
- undeclared name问题,一定要仔细啊
- MATSIM使用教程
- 【STM32】实战3.1—用STM32与TB6600驱动器驱动42步进电机(一)
- Android实现收款成功金额的语音播报功能(Nice tone)
- datacom-IPV6
热门文章
- 无法连接iphone软件更新服务器_NX许可证错误:无法连接至许可证服务器系统。SPLM_LICENSE_SERVER错误[15]...
- esp8266连接不上服务器不稳定,esp8266 联网问题+链接服务器问题
- oracle for net,使用Oracle Developer Tools For Visual Studio .NET-.NET教程,数据库应用
- Tomcat 5 5-Manager App HOW-TO 翻译了部份 努力中
- clustered和nonclustered索引的区别
- Git、Github、Gitlab、Gitee、Git-ce的区别
- 真正的Go编译器与链接器在哪里?
- 安卓案例:注册用户免启动时的广告页面
- Java案例:编译器生成桥方法
- 复数基础——数组_1