今天和大家谈谈三帧差法来实现运动目标检测吧,其中运动检测画框实现追踪方法多种多样,大家可以自行百度,后面我也会一一实现,今天我先给大家玩玩三帧差法吧;;;;(注释非常清楚哦,程序也极其简单的)
帧差法是最为常用的运动目标检测和分割方法之一,基本原理就是在图像序列相邻两帧或三帧间采用基于像素的时间差分通过闭值化来提取出图像中的运动区域。首先,将相邻帧图像对应像素值相减得到差分图像,然后对差分图像二值化,在环境亮度变化不大的情况下,如果对应像素值变化小于事先确定的阂值时,可以认为此处为背景像素:如果图像区域的像素值变化很大,可以认为这是由于图像中运动物体引起的,将这些区域标记为前景像素,利用标记的像素区域可以确定运动目标在图像中的位置。由于相邻两帧间的时间间隔非常短,用前一帧图像作为当前帧的背景模型具有较好的实时性,其背景不积累,且更新速度快、算法简单、计算量小。算法的不足在于对环境噪声较为敏感,闽值的选择相当关键,选择过低不足以抑制图像中的噪声,过高则忽略了图像中有用的变化。对于比较大的、颜色一致的运动目标,有可能在目标内部产生空洞,无法完整地提取运动目标。
简单说一下程序思路哈,参考了一下opencv官网教程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#include<iostream> #include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<opencv2\imgproc\imgproc.hpp> using namespace cv; using namespace std; const unsigned char FORE_GROUD = 255; int thresh = 10; int main( int argc, char *argv[]) { VideoCapture video(argv[1]); //判断如果video是否可以打开 if (!video.isOpened()) return -1; //用于保存当前帧的图片 Mat currentBGRFrame; //用来保存上一帧和当前帧的灰度图片 Mat previousSecondGrayFrame; Mat previousFirstGrayFrame; Mat currentGaryFrame; //保存两次的帧差 Mat previousFrameDifference; //previousFrameFirst - previousFrameSecond的差分 Mat currentFrameDifference; //currentFrame - previousFrameFirst; //用来保存帧差的绝对值 Mat absFrameDifferece; //用来显示前景 Mat previousSegmentation; Mat currentSegmentation; Mat segmentation; //显示前景 namedWindow( "segmentation" ,1); createTrackbar( "阈值:" , "segmentation" ,&thresh,FORE_GROUD,NULL); //帧数 int numberFrame = 0; //形态学处理用到的算子 Mat morphologyKernel = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1)); for (;;) { //读取当前帧 video >> currentBGRFrame; //判断当前帧是否存在 if (!currentBGRFrame.data) break ; numberFrame++; //颜色空间的转换 cvtColor(currentBGRFrame,currentGaryFrame,COLOR_BGR2GRAY); if ( numberFrame == 1) { //保存当前帧的灰度图 previousSecondGrayFrame = currentGaryFrame.clone(); //显示视频 imshow( "video" ,currentBGRFrame); continue ; } else if ( numberFrame == 2) { //保存当前帧的灰度图 previousFirstGrayFrame = currentGaryFrame.clone(); //previousFirst - previousSecond subtract(previousFirstGrayFrame,previousSecondGrayFrame,previousFrameDifference,Mat(),CV_16SC1); //取绝对值 absFrameDifferece = abs (previousFrameDifference); //位深的改变 absFrameDifferece.convertTo(absFrameDifferece,CV_8UC1,1,0); //阈值处理 threshold(absFrameDifferece,previousSegmentation, double (thresh), double (FORE_GROUD),THRESH_BINARY); //显示视频 imshow( "video" ,currentBGRFrame); continue ; } else { //src1-src2 subtract(currentGaryFrame,previousFirstGrayFrame,currentFrameDifference,Mat(),CV_16SC1); //取绝对值 absFrameDifferece = abs (currentFrameDifference); //位深的改变 absFrameDifferece.convertTo(absFrameDifferece,CV_8UC1,1,0); //阈值处理 threshold(absFrameDifferece,currentSegmentation, double (thresh), double (FORE_GROUD),THRESH_BINARY); //与运算 bitwise_and(previousSegmentation,currentSegmentation,segmentation); //中值滤波 medianBlur(segmentation,segmentation,3); //形态学处理(开闭运算) //morphologyEx(segmentation,segmentation,MORPH_OPEN,morphologyKernel,Point(-1,-1),1,BORDER_REPLICATE); morphologyEx(segmentation,segmentation,MORPH_CLOSE,morphologyKernel,Point(-1,-1),2,BORDER_REPLICATE); //找边界 vector< vector<oint> > contours; vector<Vec4i> hierarchy; //复制segmentation Mat tempSegmentation = segmentation.clone(); findContours( segmentation, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) ); //CV_RETR_TREE vector< vector<oint> > contours_poly( contours.size() ); /*存储运动物体*/ vector<Rect> boundRect; boundRect.clear(); //画出运动物体 for ( int index = 0;index < contours.size() ;index++) { approxPolyDP( Mat(contours[index]), contours_poly[index], 3, true ); Rect rect = boundingRect( Mat(contours_poly[index]) ); rectangle(currentBGRFrame,rect,Scalar(0,255,255),2); } //显示视频 imshow( "video" ,currentBGRFrame); //前景检测 imshow( "segmentation" ,segmentation); //保存当前帧的灰度图 previousFirstGrayFrame = currentGaryFrame.clone(); //保存当前的前景检测 previousSegmentation = currentSegmentation.clone(); } if (waitKey(33) == 'q' ) break ; } return 0; } |
编译之后,运行./main.cpp **.avi
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/xiao__run/article/details/76832701