您的位置:首页 > 理论基础

【碎片知识(7)· 计算机视觉基础】基于光流估计的目标跟踪

2018-02-18 12:42 549 查看
1.1         问题需求及实现流程:
   要求:使用光流法跟踪给定视频或摄像头中的运动特点。
   检测流程:1)视频采集(取到视频中当前帧图像);2)图像预处理;3)提取特征点;4)使用光流法估计特征运动;5)相邻帧及特征点交换。
1.2         OpenCV实现
   OpenCV相关函数
Ø  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,第一帧图像。
  nextImg,第二帧图像。
  prevPts,第一帧图像中的所有特征向量。
  nextPts,第二帧图像中的所有特征向量。

  status,输出状态向量;如果相应点光流被发现,向量的每个单元被设置为1,否则,被置为0。
Ø  void goodFeatureToTrack(InputArray _image, OutputArray _corners,int maxCorners, double qualityLevel,double minDistance, InputArray _mask,int blockSize, bool useHarrisDeterctor,double harrisK)
  _image,8位或32位浮点型输入图像,单通道。
  _corners,保存检测出的角点。
  maxCorners,角点数目最大值,如果实际检测的角点超过此值,则只返回前maxCorners个强角点。
  qualityLevel,角点的品质因子,决定角点可信度。
  minDistance,此领域范围内如果存在更强角点,则删除此角点。

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

const int MAX_POINT_COUNT = 500;  // 全局变量限制单词循环中检测的特征点数量

bool needTolnit();

void main()
{
char *fn = "D:\\CommonSoftware\\OpenCV\\Workplace\\Example3_OpticalFlow\\vtest.avi";
VideoCapture cap;
Mat source, result, gray, lastGray;  // 原始图像、、上一帧灰度图像、本帧灰度图像
vector<Point2f> points[2];
// 定义一维向量数组points[2],分别存放上一帧和当前帧的特征点
// vector<Point2f> temp;
vector<uchar> status;
vector<float> err;

cap.open(fn);  // 打开视频
if (!cap.isOpened())  // 检测是否成功打开视频
{
cout << "Cannot open the source, check camera or file." << endl;
return;
}

for ( ; ; )  // 无条件循环
{
cap >> source;  // 将cap抓取的当前帧图像传递给原始图像
if (source.empty())  // 通过判断提取到的当前帧是否为空来判断是否到了视频结尾
{
break;
}

cvtColor(source, gray, COLOR_BGR2GRAY);  // 将当前帧原始图像转换为灰度图像

if (points[0].size() < 10)  // 如果上一帧中的特征点数量小于10个认为不可信,则重新检测
{
goodFeaturesToTrack(gray, points[0], MAX_POINT_COUNT, 0.01, 20);
}  // 从灰度图像gray中提取特征点放入points[0]中,角点品质因子为0.01,20的范围内仅保留最强角点

if (lastGray.empty())
{  // 如果上一帧灰度图像为空,即视频开头,将当前帧灰度图像复制到上一帧灰度图像中
gray.copyTo(lastGray);
}

calcOpticalFlowPyrLK(lastGray, gray, points[0], points[1], status, err);
// 从上一帧和当前帧灰度图像中的特征点计算光流,第i个特征点如光流发现则status[i]=1,否则为0
int counter = 0;

for (int i = 0; i < points[1].size(); i++)  // 当i小于当前帧特征点总数时,进行循环
{
double dist = norm(points[1][i] - points[0][i]);
// 计算当前帧第i个特征点与上一帧第i个特征点的距离
if (status[i] && dist >= 2.0 && dist <= 20.0)  // 对所有得到的特征点光流进行一个筛选
{  // 筛选依据是帧间光流被发现且帧间光流长度介于2到20之间
points[0][counter] = points[0][i];
points[1][counter++] = points[1][i];
}  // 将points[0]和points[1]分别的前counter个元素用过滤合格的元素代替
}

points[0].resize(counter);
points[1].resize(counter);  // 将前counter以后的元素去除

source.copyTo(result);  // 将原始图像复制到result当中
for (int i = 0; i < points[1].size(); i++)  // 当i小于滤除后当前帧中特征点总数
{
line(result, points[0][i], points[1][i], Scalar(0, 0, 0xff));
// 在result图像中用红色线将当前帧和上一帧中分别的第i个特征点连起来
circle(result, points[1][i], 3, Scalar (0, 0xff, 0));
// 在result图像中将所有筛选出的特征点用半径为3的绿色圈标识出来
}

swap(points[0], points[1]);
swap(lastGray, gray);
// 将当前帧和上一帧的特征点和图像数据交换,从而将当前帧放入上一帧中以进行后续循环

imshow("Source Image", source);
imshow("Result Image", result);

char key = waitKey(100);  // 等待100好眠
if (key == 27)  // Esc键退出
{
break;
}
}
}

bool needTolnit()
{
return true;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息