• 基于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如下:

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

            实现DctDetect.cpp文件如下:

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

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

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

  • 相关阅读:
    常见的块级元素和内联(行内)元素
    全屏滚动插件fullPage的使用
    Git使用规范
    MinIO使用教程(待完成)
    [Flutter] CachedNetworkImage加载图片证书校验失败
    [Flutter] 扩展一个支持弹出菜单的IconButton
    [Node.js] 后端服务导出CSV数据流给前端下载
    下载 VMware Tools (VMware 16)
    [Flutter] 在Windows平台中使用VS2019调试win插件
    c#CSV文件生成
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/4502356.html
Copyright © 2020-2023  润新知