OpenCV | 稀疏光流跟踪 - KLT

一、API介绍

稀疏光流跟踪是在图像上的当前帧与前一帧的特征点比较,有变换的就标记出来。
calcOpticalFlowPyrLK() 函数

void 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 第一幅8位输入图像 ,你的标定图像的灰度图,前一帧。
  • nextImg 第二幅与preImg大小和类型相同的输入图像,也就是你想搜寻的图像的灰度图 。当前帧。
  • prevPts 输入的标定图像的特征点(可以是其他特征点检测方法找到的点) 。前一帧的特征点。
  • nextPts 输出场景的特征点 。当使用OPTFLOW_USE_INITIAL_FLOW 标志时,nextPts的vector必须与input的大小相同。当前帧的特征点。
  • status 输出状态向量(无符号char),如果在当前图像中能够光流得到标定的特征点位置改变,则设置status的对应位置为1,否则设置为0
  • err 输出错误向量;向量的每个元素被设为相应特征的一个错误,误差测量的类型可以在flags参数中设置;如果流不被发现然后错误未被定义(使用status(状态)参数找到此情形)。
  • winSize 每级金字塔的搜索窗口大小。
  • maxLevel 基于最大金字塔层次数。如果设置为0,则不使用金字塔(单级);如果设置为1,则使用两个级别,等等。如果金字塔被传递到input,那么算法使用的级别与金字塔同级别但不大于MaxLevel。
  • criteria 指定迭代搜索算法的终止准则(在指定的最大迭代次数标准值(criteria.maxCount)之后,或者当搜索窗口移动小于criteria.epsilon。)
  • flags 操作标志,可选参数:OPTFLOW_USE_INITIAL_FLOW:使用初始估计,存储在nextPts中;如果未设置标志,则将prevPts复制到- - nextPts并被视为初始估计。OPTFLOW_LK_GET_MIN_EIGENVALS:使用最小本征值作为误差度量(见minEigThreshold描述);如果未设置标志,则将原始周围的一小部分和移动的点之间的 L1 距离除以窗口中的像素数,作为误差度量。
  • minEigThreshold 算法所计算的光流方程的2x2标准矩阵的最小本征值(该矩阵称为[Bouguet00]中的空间梯度矩阵)÷ 窗口中的像素数。如果该值小于MinEigThreshold,则过滤掉相应的特征,相应的流也不进行处理。因此可以移除不好的点并提升性能。

当使用calcOpticalFlowPyrLK作为光流金字塔的算法时候,我们只需要知道以下的几点:

  1. calcOpticalFlowPyrLK必须和其他的角点识别算法进行搭配使用,比如我这里使用的goodFeaturesToTrack,将其他的角点识别算法中获得的角点作为光流算法的prevPts。

  2. status 的大小和当前需要识别的光流移动的特征点大小一样,所以我们可以判定当前的图像是否还能与标定图像进行光流的依据。

二、代码演示
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

Mat frame, gray, prev_frame, prev_gray;
vector<Point2f> corners; //shi-Tomasi 交点检测 存储特征数据
vector<Point2f> iniPoint; // 初始化特征数据
vector<Point2f> fpts[2]; // 保存当前帧和前一帧的特征点位置
vector<uchar> status;	// 特征点跟踪成功标志位
vector<float> errors; // 跟踪是区域误差和

void detectFeature(Mat& inFrame, Mat& inGray)
{
	double maxCorners = 5000;
	double qualityLevel = 0.01;
	double minDistance = 10; // 小于这个就属于同一个特征点
	double blockSize = 3;

	goodFeaturesToTrack(inGray, corners, maxCorners, qualityLevel, minDistance, Mat(), blockSize, false, 0.04);
	printf("detect features = %d\n", corners.size());
}

void drawFeature(Mat& inFrame)
{
	for (int i = 0; i < fpts[0].size(); i++)
	{
		circle(inFrame, fpts[0][i], 2, Scalar(0, 0, 255), 2, 16);
	}
}

void drawTrackLines()
{
	for (int i = 0; i < fpts[1].size(); i++)
	{
		line(frame, iniPoint[i], fpts[1][i], Scalar(0, 255, 0), 2, 8);
		circle(frame, fpts[1][i], 2, Scalar(0, 0, 255), 2, 8);
	}
}

void klTrackFeature()
{
	calcOpticalFlowPyrLK(prev_gray, 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]) // 两个点有位移了,并且判断成功了
		{
			iniPoint[k] = iniPoint[i];
			fpts[1][k++] = fpts[1][i]; // 当前帧中有位移的特征点
		}
	}

	// 保存特征点并绘制跟踪轨迹
	iniPoint.resize(k);
	fpts[1].resize(k);

	drawTrackLines();
	swap(fpts[1], fpts[0]);
}

int main()
{
	VideoCapture capture(0);
	if (!capture.isOpened())
	{
		puts("dont open video.");
		system("pause");
		return -1;
	}

	while (capture.read(frame))
	{
		flip(frame, frame, 1);
		cvtColor(frame, gray, COLOR_BGR2GRAY);
		
		if (prev_gray.empty()) // 第一帧
		{
			gray.copyTo(prev_gray);
		}

		if (fpts[0].size() < 40) // 特征点损失
		{
			detectFeature(frame, gray);
			fpts[0].insert(fpts[0].end(), corners.begin(), corners.end());
			iniPoint.insert(iniPoint.end(), corners.begin(), corners.end());
		}
		else
		{
			printf("跟踪\n");
		}
		
		klTrackFeature(); // 光流跟踪

		// 当前帧拷贝的前一帧,更新前一帧数据,用于光流跟踪
		gray.copyTo(prev_gray);
		frame.copyTo(prev_frame);
		
		imshow("input video", frame);

		if (27 == waitKey(60)) break;
	}

	capture.release();
	waitKey(0);
	return 0;
}
三、结果展示

移动摄像头之后:
在这里插入图片描述

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值