OpenCV 帧差法运动物体追踪

最近迷上了物体跟踪的玩意,于是乎找了好多视频去看,翻来覆去呢就是用到了轮廓检测的api,还有耐心的不断的调试。

原理呢是帧差法(就是一帧一帧的比较)非HSV分离
看看视频 软件的工作流程

代码呢,emmm,真的是不好理解。

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv/cv.h>
#include<opencv/highgui.h>

using namespace std;
using namespace cv;

const static int SEAITVITV_VALUE = 20; //阈值操作的起始范围 可手动调试
const static int BLUR_SIZE = 10;//blur 滤波Size大小

int theObject[2] = { 0,0 };//发现目标的坐标 x,y

Rect objectBoundingRect = Rect(0, 0, 0, 0);//目标跳动的矩形

string intTostring(int num)
{
//int 类型 转为 string
stringstream ss;
ss << num;
return ss.str();
}

void searchForMovement(Mat thresgoldImage, Mat &cameraFeed)
{ //寻找移动物

Mat temp;
bool objectDetected;
//形参的阈值图像传给中间变量
thresgoldImage.copyTo(temp);
//向量 用来存放轮廓点坐标 Point
vector < vector<Point> > contours;
//向量 用来存放层次
vector<Vec4i> hierarchy;
//调用api 来获取层次和轮廓点
findContours(temp, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//判断轮廓点的个数是否为0
if (contours.size() > 0) objectDetected = true;
else {
objectDetected = false;
}

if (objectDetected)
{
//找最大的轮廓点
vector<vector<Point> > largesContours;
largesContours.push_back(contours.at(contours.size() - 1));
objectBoundingRect = boundingRect(largesContours.at(0));
//约找出接近中心位置的点
int xpos = objectBoundingRect.x + objectBoundingRect.width / 2;
int ypos = objectBoundingRect.y + objectBoundingRect.width / 2;
//传给全局变量暂放坐标
theObject[0] = xpos; theObject[1] = ypos;

}
int x = theObject[0];
int y = theObject[1];
//画一个圆圈
circle(cameraFeed, Point(x, y), 20, Scalar(0, 255, 0), 2);
//画十字架
line(cameraFeed, Point(x, y), Point(x, y - 25),Scalar(0,255,0),2);
line(cameraFeed, Point(x, y), Point(x, y + 25), Scalar(0, 255, 0), 2);
line(cameraFeed, Point(x, y), Point(x-25, y ), Scalar(0, 255, 0), 2);
line(cameraFeed, Point(x, y), Point(x+25, y ), Scalar(0, 255, 0), 2);
//输出坐标
putText(cameraFeed, "tracking at:(" + intTostring(x) + "," + intTostring(y) + ")", Point(x, y), 1, 1, Scalar(0, 255, 0));
}

int main()
{
//按键是否调试
bool debugMode = false;
//按键是否跟踪
bool trackingEnable = false;
//是否暂停视频
bool pause = false;
//视频取帧的frame1 frame2
Mat frame1, frame2;
//上者灰度图像
Mat gray1, gray2;
//两者的不同图像
Mat differenceImg;
//视频类
VideoCapture capture;
//阈值后的图像
Mat thres;
while (1)//不断的播放视频 进行调试
{
capture.open("3.mp4");
if (!capture.isOpened())
{
//打开视频失败
return -2;
}
//int total = capture.get(CV_CAP_PROP_FRAME_COUNT);
//cout << total << endl;
//获取当前帧 与总帧进行比较 从而循环
while (capture.get(CV_CAP_PROP_POS_FRAMES) < capture.get(CV_CAP_PROP_FRAME_COUNT) - 1)
{
//捕获frame1
capture.read(frame1);
//转为灰度图
cvtColor(frame1, gray1, COLOR_BGR2GRAY);
//捕获frame2
capture.read(frame2);
//转为灰度图
cvtColor(frame2, gray2, COLOR_BGR2GRAY);
//调用api 找出两者不同的区域 存入differenceImg
absdiff(gray1, gray2, differenceImg);
//阈值操作
threshold(differenceImg, thres, SEAITVITV_VALUE, 255, THRESH_BINARY);
//滤波 去噪点
blur(thres, thres, Size(BLUR_SIZE, BLUR_SIZE));
//再进行一遍阈值
threshold(thres, thres, SEAITVITV_VALUE, 255, THRESH_BINARY);
//是否进入调试模式
if (debugMode == true) {
imshow("thres", thres);
}
else {
//销毁窗口
destroyWindow("thres");
}
//是否进入追踪捕获模式
if (trackingEnable) {
searchForMovement( thres, frame1);
}
//显示原图像
imshow("frame", frame1);
switch (waitKey(50))
{
case 27:
return 0;
case 't':
case 'T':
{
trackingEnable = !trackingEnable;
if (trackingEnable == false) cout << "没有捕获" << endl;
else cout << "捕获开始" << endl;
break;
}
case 'd':
case 'D':
{
debugMode = !debugMode;
if (debugMode == false) cout << "调试模式退出\n";
else cout << "进入调试模式" << endl;
break;
}
case 112:
pause = !pause;
if (pause == true)
{
cout << "暂停" << endl;
while (pause == true)
{
switch (waitKey())
{
case 112:
pause = false;
break;
}
}
}
else cout << "开始" << endl;
break;
}
}
//播放完后释放类
capture.release();
}
}