运动物体跟踪与计数

运动物体跟踪与计数

Opencv有个算法:运动模板.理论可以参考<<学习opencv>>红色这本书,例程参考<<opencv基础>>绿色书上的例程.

一些经验:

1.前景检测:背景差分法,在例程上加入膨胀操作,可以将一些区域连接起来,另外要根据前景背景调整二值化的参数阈值,不然有的前景会检测不到.

2.计数原理:检测穿过某一区域的物体个数,某一团块中心进入前将该块保存到一个数组中,用于后续查找,没穿过前不断更新其特征,穿过后从表中查找是否有改团块,有则计数加1(计数过程中会有个别丢失,可以将帧数加入作为匹配条件,我的代码没用到帧数,有时间在搞一下)

3.方向判断:运动方向可借助运动模板中的运动历史图像计算.cvUpdateMotionHistory();利用角度可以知道运动方向

4.项目开发时,想用opencv中的blobTrack来做,但发现CPU使用太高

 

//======================MotionDetect.h================================================================================

 

#ifndef _MOTION_DETECT_H
#define _MOTION_DETECT_H

#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv/cvaux.h>
struct MyBlobTrack
{
 int m_nFrame;    //帧数;
 int m_nCentreX;  //中心x;
 int m_nCentreY;  //中心y;
};

class CMotionDetect
{
public:
 CMotionDetect();
 ~CMotionDetect();
 void InitMemory(CvSize size);
 void DrawContours(IplImage* pGrayImg,IplImage* pShowImg);
 void Process(IplImage* pSrc,IplImage* pDst);
 void DrawText(IplImage* pImg,char* text);
 void DrawCount(IplImage* pImg,int count);
 void AddBlobTrack(MyBlobTrack blob);
 bool FindBlobTrack(MyBlobTrack blob);
 void CheckBlobTrack(CvSize size,MyBlobTrack blob);
private:
 MyBlobTrack m_blobTrack[10]; //记录跟踪的团块;
 int m_blobIndex;
 IplImage *m_pMHI;    // MHI: motion history image
 IplImage *m_pOrient;   // orientation
 IplImage *m_pMask;    // valid orientation mask
 IplImage *m_pSegmask;   // motion segmentation map
 IplImage** m_ppImgBuf;
 
 CvMemStorage* m_pMemStorage;
 CvSeq* m_pSeq;
 int m_nLastFrame;
 int m_nCount;
};


#endif

 

//======================MotionDetect.cpp================================================================================

#include "stdafx.h"
#include "MotionDetect.h"


// various tracking parameters (in seconds)
const double MHI_DURATION = 0.5;
const double MAX_TIME_DELTA = 0.5;
const double MIN_TIME_DELTA = 0.05;
const int CONTOUR_MAX_AERA = 18;
CMotionDetect::CMotionDetect()
{
 m_pMHI = NULL;
 m_pOrient = NULL;   
 m_pMask = NULL;    
 m_pSegmask = NULL; 
 m_ppImgBuf = NULL;
 m_pMemStorage = NULL;
 m_pSeq = NULL;
 m_nLastFrame = 0;
 m_nCount = 0;
 m_blobIndex = 0;
}

CMotionDetect::~CMotionDetect()
{
 cvReleaseImage( &m_pMHI );
 m_pMHI = NULL;
 cvReleaseImage( &m_pOrient );
 m_pOrient = NULL;
 cvReleaseImage( &m_pSegmask );
 m_pSegmask = NULL;
 cvReleaseImage( &m_pMask );
 m_pMask = NULL;
 for(int i = 0; i < 2; i++ )
 {
  cvReleaseImage( &m_ppImgBuf[i] );
  m_ppImgBuf[i] = NULL;
 }
}

void CMotionDetect::InitMemory(CvSize size)
{
 if( m_ppImgBuf == 0 )
 {
  m_ppImgBuf = (IplImage**)malloc(2*sizeof(m_ppImgBuf[0]));
  memset( m_ppImgBuf, 0, 2*sizeof(m_ppImgBuf[0]));
 }

 for(int i = 0; i < 2; i++ )
 {
  cvReleaseImage( &m_ppImgBuf[i] );
  m_ppImgBuf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
  cvZero( m_ppImgBuf[i] );
 }
 cvReleaseImage( &m_pMHI );
 cvReleaseImage( &m_pOrient );
 cvReleaseImage( &m_pSegmask );
 cvReleaseImage( &m_pMask );

 m_pMHI = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 cvZero( m_pMHI ); // clear MHI at the beginning
 m_pOrient = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 m_pSegmask = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 m_pMask = cvCreateImage( size, IPL_DEPTH_8U, 1 );
}

void CMotionDetect::DrawContours(IplImage* pGrayImg,IplImage* pShowImg)
{
  // 下面的程序段用来找到轮廓;
 // Create dynamic structure and sequence.
 CvMemStorage *stor;
 CvSeq *cont;
 stor = cvCreateMemStorage(0);
 cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);
    // 找到所有轮廓;
    cvFindContours( pGrayImg, stor, &cont, sizeof(CvContour),
                    CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
/*
    for(;cont;cont = cont->h_next)
    {  
        // Number point must be more than or equal to 6 (for cvFitEllipse_32f).       
        if( cont->total < 6 ) 
            continue;

        // Draw current contour.
        cvDrawContours(pShowImg,cont,CV_RGB(255,0,0),CV_RGB(255,0,0),0,1, 8, cvPoint(0,0));
    }  // end of for-loop: "cont"
*/
    // 直接使用CONTOUR中的矩形来画轮廓
    for(;cont;cont = cont->h_next)
    {
              CvRect r = ((CvContour*)cont)->rect;
              if(r.height * r.width > CONTOUR_MAX_AERA && r.height > 5 && r.width>5) // 面积小的方形抛弃掉
              {
                  cvRectangle( pShowImg, cvPoint(r.x,r.y),
                          cvPoint(r.x + r.width, r.y + r.height),
                          CV_RGB(255,0,0), 1, CV_AA,0);
              }
    }
    // free memory
    cvReleaseMemStorage(&stor);
}

void CMotionDetect::DrawText(IplImage* pImg,char* text)
{
 CvFont font;//初始化字体格式;
 cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 2, 8);
 cvPutText( pImg, text, cvPoint(10,20), &font, cvScalar(0,0,255));
}

void CMotionDetect::DrawCount(IplImage* pImg,int count)
{
 cvLine(pImg,cvPoint(pImg->width/2,0),cvPoint(pImg->width/2,pImg->height),cvScalar(255,0,0));;
 char data[64];
 memset(data,0,64);
 sprintf(data,"Count:%d",count);
 DrawText(pImg,data);
}

void CMotionDetect::Process(IplImage* pSrc,IplImage* pDst)
{
 int idx1, idx2;
 IplImage* pSilh;
 CvRect comp_rect;
 double count;
 double angle;
 CvPoint center;
 double magnitude;         
 CvScalar color;
 double timestamp = clock()/100.; // get current time in seconds
 CvSize size = cvSize(pSrc->width,pSrc->height); // get current frame size
 
 if( !m_pMHI || m_pMHI->width != size.width || m_pMHI->height != size.height )
 {
  InitMemory(size);
 }
 cvCvtColor( pSrc, m_ppImgBuf[m_nLastFrame], CV_BGR2GRAY ); // convert frame to grayscale
 idx1 = m_nLastFrame;
 idx2 = (m_nLastFrame+1)%2;
 m_nLastFrame = idx2;
 // 做帧差;
 pSilh = m_ppImgBuf[idx2];
 cvAbsDiff( m_ppImgBuf[idx1], m_ppImgBuf[idx2], pSilh ); // get difference between frames
 // 对差图像做二值化;
 cvThreshold( pSilh, pSilh, 20, 255, CV_THRESH_BINARY ); // and threshold it
 // 向下采样,去掉噪声;
 IplImage* pyr = cvCreateImage( cvSize((size.width & -2)/2, (size.height & -2)/2), 8, 1 );
 cvPyrDown( pDst, pyr, 7 );
 cvDilate( pyr, pyr, 0, 1 );  // 做膨胀操作,消除目标的不连续空洞;
 cvPyrUp( pyr, pDst, 7 );
    cvReleaseImage( &pyr );
 cvUpdateMotionHistory( pSilh, m_pMHI, timestamp, MHI_DURATION ); // update MHI
 //cvCvtScale( m_pMHI, pDst, 255./MHI_DURATION,(MHI_DURATION - timestamp)*255./MHI_DURATION );   
 //cvCvtScale( m_pMHI, pDst, 255./MHI_DURATION, 0 );   
 cvCvtScale( m_pMHI, m_pMask, 255./MHI_DURATION,(MHI_DURATION - timestamp)*255./MHI_DURATION );   
 // 中值滤波,消除小的噪声;
 cvSmooth( pDst, pDst, CV_MEDIAN, 3, 0, 0, 0 );
 

 // 计算运动的梯度方向以及正确的方向掩模mask;
 // Filter size = 3
 cvCalcMotionGradient( m_pMHI, m_pMask, m_pOrient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3 );
 if( !m_pMemStorage )
  m_pMemStorage = cvCreateMemStorage(0);
 else
  cvClearMemStorage(m_pMemStorage);

 // 运动分割: 获得运动部件的连续序列;
 // segmask is marked motion components map. It is not used further
 m_pSeq = cvSegmentMotion( m_pMHI, m_pSegmask, m_pMemStorage, timestamp, MAX_TIME_DELTA );
 // iterate through the motion components,
 // One more iteration (i == -1) corresponds to the whole image (global motion)
 for( int i = 0; i < m_pSeq->total; i++ )
 {
  if( i < 0 )
  { // case of the whole image,对整幅图像做操作
   comp_rect = cvRect( 0, 0, size.width, size.height );
   color = CV_RGB(255,255,255);  // white color
   magnitude = 100;  // 画线长度以及圆半径的大小控制
  }
  else
  { // i-th motion component
   comp_rect = ((CvConnectedComp*)cvGetSeqElem( m_pSeq, i ))->rect;
   // 去掉小的部分
   if( comp_rect.width + comp_rect.height <110 )
    continue;
   color = CV_RGB(255,0,0);    // red color
   magnitude = 30;
   //if(seq->total > 0) MessageBox(NULL,"Motion Detected",NULL,0);
  }

  // select component ROI
  cvSetImageROI( pSilh, comp_rect );
  cvSetImageROI( m_pMHI, comp_rect );
  cvSetImageROI( m_pOrient, comp_rect );
  cvSetImageROI( m_pMask, comp_rect );

  // 在选择的区域内, 计算运动方向
  angle = cvCalcGlobalOrientation( m_pOrient, m_pMask, m_pMHI, timestamp,MHI_DURATION);
  // 在轮廓内计算点数
  // Norm(L1) = sum of total pixel values
  count = cvNorm( pSilh, 0, CV_L1, 0 );

  // The function cvResetImageROI releases image ROI
  cvResetImageROI( m_pMHI );
  cvResetImageROI( m_pOrient );
  cvResetImageROI( m_pMask );
  cvResetImageROI( pSilh );

  // check for the case of little motion
  if( count < comp_rect.width*comp_rect.height * 0.05 )  // five percent of pixel
   continue;

  // draw a clock with arrow indicating the direction
  center = cvPoint( (comp_rect.x + comp_rect.width/2),
   (comp_rect.y + comp_rect.height/2) );
  //计数
  if((angle >0 && angle <90) || (angle > 270 && angle < 360))
  {
   /*
   if(center.x > size.width/2 -3 && center.x < size.width/2 +3)
   {
    m_nCount++;
   }
   */
   MyBlobTrack blobTrack;
   blobTrack.m_nCentreX = comp_rect.x + comp_rect.width/2;
   blobTrack.m_nCentreY = comp_rect.y + comp_rect.height/2;
   CheckBlobTrack(size,blobTrack);
  }
  cvCircle( pSrc, center, cvRound(magnitude*1.2), color, 1, CV_AA, 0 );
  angle = 360.0 - angle;  // adjust for images with top-left origin
  cvLine( pSrc, center, cvPoint( cvRound( center.x + magnitude*cos(angle*CV_PI/180)),
       cvRound( center.y - magnitude*sin(angle*CV_PI/180))), color, 3, CV_AA, 0 );
 }
 DrawCount(pSrc,m_nCount);
}

void CMotionDetect::CheckBlobTrack(CvSize size,MyBlobTrack blob)
{
 if(blob.m_nCentreX > size.width/2 -20 && blob.m_nCentreX < size.width/2)
 {
  AddBlobTrack(blob);
 }
 else if(blob.m_nCentreX > size.width/2 && blob.m_nCentreX < size.width/2 + 20)
 {
  if(FindBlobTrack(blob))
  {
   m_nCount++;
  }
 }
}


void CMotionDetect::AddBlobTrack(MyBlobTrack blob)
{
 bool bFind = false;
 for(int i=0;i<10;i++)
 {
  if(m_blobTrack[i].m_nCentreX != 0 && abs(m_blobTrack[i].m_nCentreX - blob.m_nCentreX)< 15 && abs(m_blobTrack[i].m_nCentreY - blob.m_nCentreY)< 15 )
  {
   m_blobTrack[i].m_nCentreX = blob.m_nCentreX;
   m_blobTrack[i].m_nCentreY = blob.m_nCentreY;
   m_blobTrack[i].m_nFrame = blob.m_nFrame;
   bFind = true;
   break;
  }
 }
 if(bFind != true)//表示没有找到车辆;
 {
  m_blobTrack[m_blobIndex].m_nCentreX = blob.m_nCentreX;
  m_blobTrack[m_blobIndex].m_nCentreY = blob.m_nCentreY;
  m_blobTrack[m_blobIndex].m_nFrame = blob.m_nFrame;
  if(m_blobIndex==9)
  {
   m_blobIndex=0;
  }
  else
  {
   m_blobIndex++;
  }
 }    
}

bool CMotionDetect::FindBlobTrack(MyBlobTrack blob)
{
 for(int i=0;i<10;i++)
 {
  if(m_blobTrack[i].m_nCentreX != 0 && abs(m_blobTrack[i].m_nCentreX - blob.m_nCentreX)< 20 && abs(m_blobTrack[i].m_nCentreY - blob.m_nCentreY)< 30 )
  {
   //移除此块;
   m_blobTrack[i].m_nCentreX = 0;
   m_blobTrack[i].m_nCentreY = 0;
   m_blobTrack[i].m_nFrame = 0;
   return true;
  }
 }
 return false;
}

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值