Opencv之二帧差法运动目标检测与轮廓提取

我不是女神ヾ 2022-04-11 12:53 397阅读 0赞

Opencv学习之——二帧差法运动目标检测与轮廓提取

  1. #include "highgui.h"
  2. #include "cv.h"
  3. #include "stdio.h"
  4. #include <time.h>
  5. #include <math.h>
  6. #include <string.h>
  7. const double MHI_DURATION=0.1;//运动跟踪的最大持续时间0.1s
  8. const double MAX_TIME_DELTA=0.5//最大时间增量0.5s
  9. const double MIN_TIME_DELTA=0.05;//最小时间增量0.05s
  10. const int N=3;
  11. const int CONTOUR_MAX_AERA=16;
  12. /*做帧差时要用到的图像缓冲*/
  13. IplImage **buf=0;
  14. int last=0;
  15. /*临时图像*/
  16. IplImage* mhi=0;//运动历史图像mhi
  17. CvConnectedComp* cur_comp,mincomp;
  18. /*typedef struct CvConnectedComp
  19. {
  20. double area; //区域的面积
  21. CvScalar value; //区域颜色的平均值
  22. CvRect rect; //是一个区域的外接矩形
  23. CvSeq * contour; //指向另一个序列的指针
  24. };*/
  25. /*定义一个内存存储器*/
  26. CvMemStorage* storage;
  27. /*二维坐标系下的点,类型为整型,通常以0点为原点,有x、y坐标*/
  28. CvPoint pt[4];
  29. /*当前画面索引*/
  30. int nCurFrameIndex=0;
  31. /*定义用来更新运动历史图像的函数*/
  32. /*img-输入视频帧;dst-检测结果*/
  33. void update(IplImage *img,IplImage *dst,int diff_threshold)
  34. {
  35. /*获得当前时间,单位是秒*/
  36. double timestamp=clock()/100;
  37. /*获得输入视频帧的尺寸,用存到size中*/
  38. CvSize size=cvSize(img->width,img->height);
  39. /*做帧差要用到的中间变量*/
  40. int i,idx1,idx2;
  41. /*当前帧与上一帧做帧差之后,得到的图像数据存储在nimg中*/
  42. IplImage* nimg;
  43. /*这步暂时没看懂- -!*/
  44. IplImage* pyr=cvCreateImage(cvSize((size.width&-2)/2,(size.height&-2)/2),8,1);
  45. /*定义一个内存存储器*/
  46. CvMemStorage* stor;
  47. /*创建一个可增长的序列seq*/
  48. CvSeq* seq;
  49. /*先进行数据的初始化*/
  50. /*如果历史图像为空,或者历史图像尺寸与输入的当前帧尺寸不吻合(这意味着打开了新的视频?)*/
  51. if(!mhi||mhi->width!=size.width||mhi->height!=size.height)
  52. {
  53. /*如果buf还未初始化,则为buf分配内存*/
  54. if(buf==0)
  55. {
  56. /*N=3*/
  57. buf=(IplImage**)malloc(N*sizeof(buf[0]));
  58. /*将指针s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定:memset(void *s,char ch,unsigned n)。此处作用相当于将buf内的元素全部置零*/
  59. memset(buf,0,N*sizeof(buf[0]));
  60. }
  61. /*若buf已经初始化了,也将buf置零*/
  62. for(i=0;i<N;i++)
  63. {
  64. cvReleaseImage(&buf[i]);
  65. buf[i]=cvCreateImage(size,IPL_DEPTH_8U,1);
  66. cvZero(buf[i]);
  67. }
  68. /*重新初始化运动历史图像mhi*/
  69. cvReleaseImage(&mhi);
  70. mhi=cvCreateImage(size,IPL_DEPTH_32F,1);
  71. cvZero(mhi);
  72. }
  73. /*将当前要处理的帧转化为灰度图,放到buf的最后一帧*/
  74. cvCvtColor(img,buf[last],CV_BGR2GRAY);
  75. /*这三部是为了做帧差,让buf[idx1]永远保存的是上一帧,buf[idx2]保存当前帧*/
  76. idx1=last;
  77. idx2=(last+1)%N;
  78. last=idx2;
  79. /*做帧差,函数 cvAbsDiff 计算两个数组差的绝对值*/
  80. nimg=buf[idx2];
  81. cvAbsDiff(buf[idx1],buf[idx2],nimg);
  82. /*帧差之后,将得到的图像二值化*/
  83. cvThreshold(nimg,nimg,50,255,CV_THRESH_BINARY);
  84. /*去掉超时的影像以更新运动历史图像*/
  85. cvUpdateMotionHistory(nimg,mhi,timestamp,MHI_DURATION);
  86. cvConvert(mhi,dst);
  87. /*中值滤波,消除小的噪声
  88. 函数cvPyrDown使用Gaussian金字塔分解对输入图像向下采样,去除噪声,图像是原图像的四分之一
  89. 函数cvDialate做膨胀操作,去除目标的不连续空洞
  90. 函数cvPyrUp使用Gaussian金字塔分解对输入图像向上采样,恢复图像,图象是原图像的四倍*/
  91. cvSmooth(dst,dst,CV_MEDIAN,3,0,0,0);
  92. cvPyrDown(dst,pyr,CV_GAUSSIAN_5x5);
  93. cvDilate(pyr,pyr,0,1);
  94. cvPyrUp(pyr,dst,CV_GAUSSIAN_5x5);
  95. /*创建轮廓*/
  96. stor=cvCreateMemStorage(0);
  97. seq=cvCreateSeq(CV_SEQ_ELTYPE_POINT,//从预定义的序列类型中选择一合适的类型
  98. sizeof(CvSeq),//此参数表示序列头部的大小;必须大于或等于sizeof(CvSeq)
  99. /*第三个参数是元素的大小,以字节计。这个大小必须与序列类型(由seq_flags指定)相一致,例如,对于一个点的序列,元素类型 CV_SEQ_ELTYPE_POINT应当被指定,参数elem_size必须等同于sizeof(CvPoint)。
  100. */
  101. sizeof(CvPoint),
  102. stor);//指向前面定义的内存存储器的指针
  103. /*找到所有轮廓*/
  104. cvFindContours(dst,//源二值图像
  105. stor,//返回轮廓的容器
  106. &seq,//输出参数,第一个外接轮廓的地址。
  107. sizeof(CvContour),
  108. CV_RETR_EXTERNAL,//mode:EXTERNAL——只查找最外的轮廓
  109. CV_CHAIN_APPROX_NONE,//轮廓近似的方法,具体见百度百科- -
  110. cvPoint(0,0));
  111. /*直接用CONTOUR中的矩形来画轮廓*/
  112. /*遍历seq序列*/
  113. for(;seq;seq=seq->h_next)
  114. {
  115. /*直接使用轮廓的矩形,调取rect会得到与x、y轴平行的矩形,并非最小矩形*/
  116. CvRect r=((CvContour*)cont)->rect;//将序列类型转换成轮廓类型的指针?
  117. /*矩形的面积小于轮廓面积的话,舍弃;矩形面积也不能过小*/
  118. if((r.height*r.width>CONTOUR_MAX_AERA)&&(r.height*r.width>2560))
  119. {
  120. /*cvRectangle函数通过对角线两个顶点,绘制矩形*/
  121. cvRectangle(img,//图像
  122. cvPoint(r.x,r.y),//一个顶点
  123. cvPoint(r.x + r.width, r.y + r.height),//另一个顶点
  124. CV_RGB(255,0,0),//线条颜色
  125. 1,//线条粗细程度
  126. CV_AA,//线条类型
  127. 0); //坐标点的小数点位数
  128. }
  129. }
  130. /*函数调用完毕,释放内存*/
  131. cvReleaseMemStorage(&stor);
  132. cvReleaseImage(&pyr);
  133. }
  134. /处理视频,主函数/
  135. int main(int argc,char**argv)
  136. {
  137. IplImage *motion=0;
  138. CvCapture *capture=0;
  139. /*读取视频帧*/
  140. capture=cvCaptureFromFile("D:\\视频\\01.mp4");
  141. if(capture)
  142. {
  143. cvNamedWindow("Motion",1);
  144. for(;;)
  145. {
  146. IplImage *image;
  147. /*使用cvGrabFrame函数抓取帧*/
  148. if(!cvGrabFrame(capture))
  149. break;
  150. /*使用cvRetrieveFrame函数取回被cvGrabFrame抓取的帧*/
  151. image=cvRetrieveFrame(capture);
  152. if(image)
  153. {
  154. /*如果motion并未初始化,说明这是第一帧。我们将motion初始化*/
  155. if(!motion)
  156. {
  157. motion=cvCreateImage(cvSize(image->width,image->height),8,1);
  158. cvZero(motion);
  159. /*需要保证内存存储的顺序和取出的帧相同*/
  160. motion->origin=image->origin;
  161. }
  162. }
  163. /*若取出了新的一帧,而且motion不为空,则更新画面*/
  164. update(image,motion,10);
  165. /*显示处理过的图像*/
  166. cvShowImage("Motion",image);
  167. /*10ms内检测到用户按了任意键,均退出*/
  168. if(cvWaitKey(10)>=0)
  169. break;
  170. }
  171. /*当上面这个for循环执行结束时,说明视频已经处理完成或者用户停止处理视频了*/
  172. cvReleaseCapture(&capture);
  173. cvDestroyWindow("Motion");
  174. }
  175. return 0;
  176. }

经过测试,这个程序能够成功检测并用红色方框圈出移动的车辆和行人。
待改进的地方有:①视频处理速度慢,导致视频处理速度只有视频正常播放速度的二分之一。
②对于行人的检测,画出的红色方框不稳定,不是将整个行人框出,经常会分别框出一个人的几个不同部位orz。
③当两个物体稍有重叠时,会将重叠物体当作一个物体圈出。

发表评论

表情:
评论列表 (有 0 条评论,397人围观)

还没有评论,来说两句吧...

相关阅读

    相关 OpenCV(三十):轮廓检测

    1.轮廓概念介绍 在计算机视觉和图像处理领域中,轮廓是指在图像中表示对象边界的连续曲线。它是由一系列相邻的点构成的,这些点在边界上连接起来形成一个封闭的路径。 轮廓层级