OpenCV学习之光流法跟踪物体C++

光流法(Optical Flow)早在1950年就提出

光流法具体实现有很多,OpenCV中就有

  1. calcOpticalFlowPyrLK
  2. calcOpticalFlowFarneback

本篇主要展示使用稠密光流法calcOpticalFlowFarneback展现一个完整的视频追踪例子。

主要目的

使用OpenCV calcOpticalFlowFarneback方法进行物体的跟踪。

思路

  1. 使用光流法计算光流
  2. 过滤光流,找到光流聚集的位置
  3. 通过光流点,生成mask 图像,使用find contours方法找到运动物体边缘
  4. 使用矩形等框出物体

代码效果图:

代码

主要部分代码

[C++]
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
/*
Created by starsplendid on 15/6/18.
Copyright (c) 2015年 starsplendid. All rights reserved.
代码基础来源:http://blog.sina.com.cn/s/blog_662c78590100yyeg.html
*
*/

#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/opencv.hpp>
#include <iostream>
#include <fstream>
using namespace cv;
using namespace std;

// 其他函数,见下部分

int main()
{


Mat prevgray, gray, flow, cflow, frame,binaryPic,binaryPic2;
vector<vector<Point> > contours;

do
{
VideoCapture cap("out.avi");

if (!cap.isOpened())
{
cout<< "Error Acquireing video feed\n";
return -1;
}
while (cap.get(CV_CAP_PROP_POS_FRAMES)< cap.get(CV_CAP_PROP_FRAME_COUNT)-1) {
cap>>frame;
cvtColor(frame, gray, CV_BGR2GRAY);

if (prevgray.data)
{
calcOpticalFlowFarneback(prevgray, gray, flow, 0.5, 3,15, 3, 5, 1.2, 0);
cvtColor(prevgray, cflow, CV_GRAY2RGB);

float diff_XY= 0.0 ;
// 用来计算平均 flow 速度
int numOfSelectedPoint = 0 ;
float sumX = 0.0 ;
float sumY = 0.0 ;

binaryPic = Mat::zeros(prevgray.size(), prevgray.type());
for (int y = 0; y < cflow.rows; y += 10 )
{
for (int x = 0; x < cflow.cols; x += 10)
{
Point2f fxy = flow.at<Point2f>(y, x);
/*
选取光流明显的点
*/

diff_XY =abs(fxy.x)+abs(fxy.y);
if (diff_XY > 6) {
binaryPic.at<uchar>(y,x) = 128 ;

numOfSelectedPoint++ ;
sumX += fxy.x ;
sumY += fxy.y ;
}

arrowedLine(cflow, Point(x, y), Point(cvRound(x+fxy.x), cvRound(y+fxy.y)), CV_RGB(0,255,0));

circle(cflow, Point(x,y), 2, CV_RGB(255, 0, 0), -1);
}
}

Mat dilateElement = getStructuringElement( MORPH_RECT,Size(10,10));

dilate(binaryPic,binaryPic2,dilateElement);
// binaryPic2 to binary
threshold(binaryPic2, binaryPic2, 127.0, 255.0, THRESH_BINARY);

drawBiggestContours(binaryPic2,cflow,sumX/numOfSelectedPoint,sumY/numOfSelectedPoint);


imshow("FLOW", cflow);
}
if (waitKey(100) >= 0)
{
break;
}

swap(prevgray, gray);
}
cap.release();

} while (1);
return 0;
}
其余部分函数
[C++]
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
string intToString(int number){


std::stringstream ss;
ss << number;
return ss.str();
}
string floatToString(float number){


std::stringstream ss;
ss << number;
return ss.str();
}

int drawBiggestContours(Mat& inputMat,Mat& orignialMat,float vx , float vy){
int largest_area=0;
int largest_contour_index=0;
Rect bounding_rect;
vector<vector<Point>> contours; // Vector for storing contour
vector<Vec4i> hierarchy;

findContours( inputMat, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image

for( int i = 0; i< contours.size(); i++ ) // iterate through each contour.
{
double a=contourArea( contours[i],false);//  Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i;//Store the index of largest contour
bounding_rect=boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
}
Scalar color( 255,255,255);
drawContours( orignialMat, contours,largest_contour_index, color, 1, 8, hierarchy ); // Draw the largest contour using previously stored index.
rectangle(orignialMat, bounding_rect,Scalar(0,255,0),2, 8,0);

// 画中心点,并且标注速度 参考:http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/moments/moments.html
Scalar color1(0,0,255);
Moments mu = moments(contours[largest_contour_index],false);
float x = mu.m10/mu.m00;
float y =mu.m01/mu.m00 ;
circle( orignialMat, Point2f(x , y ), 4, CV_RGB(0,255,255), -1, 8, 0 );
arrowedLine(orignialMat, Point2f(x, y), Point2f(x+vx, y+vy), CV_RGB(0,255,255),2);

putText(orignialMat,"at ("+intToString(x)+","+intToString(y)+") with vector ("+floatToString(vx)+","+floatToString(vy)+")",bounding_rect.br(),1,1,Scalar(0,255,0),2);
return 0 ;
}

参考资料

  1. Moments 找中心点
  2. 找最大的contour
知识共享许可协议