opencv学习笔记四十一:稀疏光流跟踪

Lucas-Kanada光流假设:

  1. 场景中物体被跟踪的部分的亮度不变;
  2. 相邻帧之间的运动较小;
  3. 相邻的点保持相邻。

 LK算法只需要每个感兴趣点周围小窗口的局部信息,但是较大的运动会将点移除这个小窗口,从而造成算法无法再找到这些点。金字塔的LK算法可以解决这个问题,即从金字塔的最高层(细节最少)开始向金字塔的最低层(丰富的细节)进行跟踪。跟踪图像金字塔允许小窗口部或较大的运动。

在开始跟踪前,首先要在初始帧中检测特征点,之后在下一帧中尝试跟踪这些点。你必须找到新的图像帧中这些点的位置,因此,你必须在特征点的先前位置附近进行搜索,以找到下一帧中它的新位置。这正是cv::calcOpticalFlowPyrLK函数所实现的工作。你输入两个连续的图像帧以及第一幅图像中检测到的特征点数组,该函数将返回一组新的特征点为位置。为了跟踪完整的序列,你需要在帧与帧之间重复这个过程,不可避免地你也会丢失其中一些点,于是被跟踪的特征点数目会减少。为了解决这个问题,我们可以不时地检测新的特征值。

 calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,
                                        InputArray prevPts, InputOutputArray nextPts,
                                        OutputArray status, OutputArray err,
                                        Size winSize = Size(21,21), int maxLevel = 3,
                                        TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),
                                        int flags = 0, double minEigThreshold = 1e-4 );

prevImg:第一帧(跟踪图像的前一帧,一般是定位特征点) 
nextImg: 第二帧/当前帧 
prev_Pts:第一帧特征点集
next_Pts:计算输出的第二帧光流特征点集
status :状态标志位,如果对应特征的光流被发现,数组中的每一个元素都被设置为 1, 否则设置为 0。 
err:双精度数组,包含原始图像碎片与移动点之间的误差。
 

#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;

void detectFeatures(Mat &gray);
void drawFeatures(Mat &frame);
void KLTtracker();
void drawTrackLine();
vector<Point2f>features;//shi-Tomasi角点检测特征数据
vector<Point2f>iniPoints;//初始化特征数据
vector<Point2f>fpts[2];//保存当前帧和前一帧的特征点位置
vector<uchar>status;//特征点跟踪成功标志位
vector<float>errors;//跟踪误差
Mat frame,gray, pregray;

int main(int arc, char** argv) {
	VideoCapture capture(0); 	
	namedWindow("output", CV_WINDOW_AUTOSIZE);
	capture.read(frame);//因为摄像头打开会有延迟,所以提前打开一帧
	while (capture.read(frame)) {
		imshow("frame", frame);
		cvtColor(frame, gray, CV_BGR2GRAY);		
		if (fpts[0].size() < 10) {
			detectFeatures(gray);
			fpts[0] = features;
			iniPoints = features;
		}
		if (pregray.empty()) {
			gray.copyTo(pregray);
		}
		KLTtracker();
		drawFeatures(frame);	
		//光流跟踪后把当前帧当作前一帧,再与下一帧进行匹配跟踪
		gray.copyTo(pregray);
		imshow("output", frame);	
		char c = waitKey(50);
		if (c == 27) {
			break;
		}
	}
	capture.release();
	waitKey(0);
	return 0;
}
void detectFeatures(Mat &gray) {
	goodFeaturesToTrack(gray, features, 5000, 0.01, 10, Mat(), 3, false);
}
void drawFeatures(Mat &frame) {
	for (int i = 0; i < fpts[0].size(); i++) {
		circle(frame, fpts[0][i], 2, Scalar(0, 0, 255), 2);
	}
}
void KLTtracker() {
	calcOpticalFlowPyrLK(pregray, gray, fpts[0], fpts[1], status, errors);
	int k = 0;
	for (int i = 0; i < fpts[1].size(); i++) {
		double dist = abs(fpts[0][i].x - fpts[1][i].x) + abs(fpts[0][i].y - fpts[1][i].y);
		if (dist > 2 && status[i]) {
			iniPoints[k] = iniPoints[i];
			fpts[1][k++] = fpts[1][i];
		}
	}
	//保存特征点并绘制跟踪轨迹
	iniPoints.resize(k);
	fpts[1].resize(k);
	drawTrackLine();
	//fpts[0] = fpts[1];
	swap(fpts[1], fpts[0]);
}
void drawTrackLine() {
	for (int i = 0; i < fpts[1].size(); i++) {
		line(frame, iniPoints[i], fpts[1][i], Scalar(0, 255, 0), 2);
		circle(frame, features[i], 2, Scalar(0, 0, 255), 2);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_24946843/article/details/82732088