本文章代码基于《OpenCV 2 计算机视觉 编程手册》第10章 处理视频序列。同时对于书中的一些错误代码予以纠正。
由于该章内容基本自成体系,并以类封装了常用的视频读取、处理等操作,故共享于此。
#include <stdio.h>
#include <stdlib.h>
#include <iomanip>
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
//帧处理的接口
//在面向对象的上下文中,更适合使用帧处理类,而非帧处理函数
class FrameProcessor
{
public:
//处理方法
virtual void process(Mat &input, Mat &ouput) = 0;
};
class FeatureTracker:public FrameProcessor
{
private:
//当前灰度图像
Mat gray;
//之前灰度图像
Mat gray_prev;
//两幅图像之间的特征点 0->1
vector<Point2f> points[2];
//跟踪点的初始位置
vector<Point2f> initial;
//检测到的特征
vector<Point2f> features;
//需要跟踪的最大特征数目
int max_count;
//特征检测中的质量等级
double qlevel;
//两点之间的最小距离
double minDist;
//检测到的特征的状态
vector<uchar> status;
//跟踪过程中的错误
vector<float> err;
public:
FeatureTracker():max_count(500), qlevel(0.01), minDist(10.){}
//是否需要添加新的特征点
bool addNewPoints()
{
//如果点的数量太少
return points[0].size() <= 10;
}
//检测特征点
void detectFeaturePoints()
{
//检测特征
goodFeaturesToTrack(gray, //图像
features, //检测到的特征
max_count, //特征的最大数目
qlevel, //质量等级
minDist); //两个特征之间的最小距离
}
//决定哪些点应该跟踪
bool acceptTrackedPoint(int i)
{
return status[i] &&
//如果它移动了
((abs(points[0][i].x - points[1][i].x) + abs(points[0][i].y - points[1][i].y)) > 2);
}
//处理当前跟踪的点
void handleTrackedPoints(Mat &frame, Mat &output)
{
//遍历所有跟踪点
for (int i = 0; i < points[1].size(); i++)
{
//绘制直线与圆
line(output,
initial[i], //初始位置
points[1][i],//新位置
Scalar(255, 255, 255));
circle(output, points[1][i], 3, Scalar(255, 255, 255), -1);
}
}
void process(Mat &frame, Mat &output)
{
//转换为灰度图像
cvtColor(frame, gray, CV_BGR2GRAY);
frame.copyTo(output);
//1.如果需要添加新的特征点
if (addNewPoints())
{
//进行检测
detectFeaturePoints();
//添加检测到的特征到当前跟踪的特征中
points[0].insert(points[0].end(), features.begin(), features.end());
initial.insert(initial.end(), features.begin(), features.end());
}
//对于序列中的第一幅图像
if (gray_prev.empty())
{
gray.copyTo(gray_prev);
}
//2.跟踪特征点
calcOpticalFlowPyrLK(
gray_prev, gray, //两幅连续图
points[0], //图1中的输入点坐标
points[1], //图2中的输入点坐标
status, //跟踪成功
err); //跟踪失误
//2.遍历所有跟踪的点进行筛选
int k = 0;
for (int i = 0; i < points[1].size(); i++)
{
//是否需要保留该点