- 移动对象跟踪三要素
外观模型、图像表示、移动模型 - 稀疏光流-LK
亮度恒定、时间持续性或"微小移动"、空间一致性
Sparse Optical Flow(Lucas-Kanade)
calcOpticalFlowPyrLK + goodFeaturesToTrack(角点检测) - 稠密光流-HF
calcOpticalFlowFarneback - 输入第一帧图像、特征点检测选择、保存特征点、输入第二帧图像(开始跟踪)
跟踪特征点、删除损失特征点、保存跟踪特征点
用第二帧图像替换第一帧图像、用后续输入帧替换第二帧图像、选择新的特征点来替换损失特征点数据、保存特征点数据
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
VideoCapture capture;
Mat frame, gray, pre_gray;
RNG rng;
void detectFeatures(Mat& gray);//检测角点
vector<Point2f> corners; // shi-tomasi角点检测-特征数据
void drawFeatures(Mat& frame);//绘制角点
vector<Point2f> corners_init;//初始化特征数据
vector<Point2f> pyLk[2];//用于calcOpticalFlowPyrLK//保存当前帧和前一帧的角点位置
void KLTrackFeature(Mat& frame);//稀疏光流跟踪,KLT
static vector<uchar> status; // 特征点跟踪成功标志位//用于calcOpticalFlowPyrLK
static vector<float> errors; // 跟踪时候区域误差和//用于calcOpticalFlowPyrLK
void drawTrackLine(Mat& frame);//绘制跟踪线
//http://www.freesion.com/article/6081117469/
int main(int argc, char** argv)
{
capture.open("../path.avi");
if (!capture.isOpened())
{
cout << "could not load video..." << endl;
return -1;
}
namedWindow("Video_demo", WINDOW_AUTOSIZE);
while (capture.read(frame))
{
cvtColor(frame, gray, COLOR_BGR2GRAY);
if (pyLk[0].size() < 40) // 跟踪40个特征点,如果跟踪的时候损失了一些特征点,重新检测,追加
{
detectFeatures(gray);//在灰度视频中检测角点
pyLk[0].insert(pyLk[0].end(), corners.begin(), corners.end());//STL//追加带跟踪的特征点
corners_init.insert(corners_init.end(), corners.begin(), corners.end());
}
else
{
cout << "正在跟踪..." << endl;// 表示特征点没有损失,一直在跟踪
}
//判断前一帧是否有角点
if (pre_gray.empty())
gray.copyTo(pre_gray);//保存前一帧,第一帧过完就不保存了
KLTrackFeature(frame);// 稀疏光流跟踪,KLT
drawFeatures(frame);//在源视频上绘制角点
// 更新前一帧数据
gray.copyTo(pre_gray); // 只需要灰度图
imshow("Video_demo", frame);
char c = waitKey(50);
if (c == 27)
{
break;
}
}
capture.release();
waitKey(0);
return 0;
}
void detectFeatures(Mat& gray)
{
int max_corners = 500;
double qualityLevel = 0.01;
int minDistance = 10;
int blockSize = 3;
//shi-tomasi角点检测
goodFeaturesToTrack(gray,
corners,//输出的角点
max_corners,//限制角点的个数
qualityLevel,//qualityLevel设置点的返回质量(通常介于0.10和0.01之间,绝对不超过1.0)
minDistance,//设置相邻角之间的最小间距
Mat(),//mask
blockSize,//blockSize计算角点时需要考虑的区域大小
false);//使用Shi-Tomasi,如果为true则使用Harris
cout << "detect features : " << corners.size() << endl;//输出角点个数//不在跟踪的时候输出
return;
}
void drawFeatures(Mat& frame)
{
//绘制角点
//for (size_t i = 0; i < corners.size(); i++)
//{
// circle(frame, corners[i], 2, Scalar(0, 0, 255), 2, 8, 0);
//}
for (size_t i = 0; i < pyLk[0].size(); i++)
{
circle(frame, pyLk[0][i], 2, Scalar(0, 0, 255), 2, 8, 0);
}
return;
}
void KLTrackFeature(Mat& frame)//稀疏光流跟踪,KLT
{
// 稀疏光流跟踪,KLT
calcOpticalFlowPyrLK(pre_gray,//要跟踪的图像,8bit
gray, //在目标图像 跟踪 pre_gray 上的 pyLk 特征点
pyLk[0],// pre_gray 上的特征点(光流)的坐标位置;点坐标必须是单精度浮点数
pyLk[1], // 如果在 gray 上跟踪到了 pre_gray 上的 prevPts[i],则在 nextPts[i] 上保存该特征点现在的坐标,nextPts与prevPts尺寸相同
status,// 输出状态向量(无符号char)
errors);// 跟踪时候区域误差和
int k = 0; // 保存跟踪到的特征点数,最后将特征点的尺寸 resize 为 k
// 特征点过滤
for (int i = 0; i < pyLk[1].size(); i++)
{
// 距离,为了计算更快,没有用欧几里得距离,用的L1
double dist = abs(pyLk[0][i].x - pyLk[1][i].x) + abs(pyLk[0][i].y - pyLk[1][i].y);
if (dist > 2 && status[i]) // 跟踪到的特征点,且距离移动了2以上的
{
corners_init[k] = corners_init[i]; // 将跟踪到的移动了的特征点在vector中连续起来,剔掉损失的和静止不动的特征点(这些跟踪点在前面帧中)
pyLk[1][k++] = pyLk[1][i]; // 同上 (只是这些跟踪点在当前帧中)
}
}
// 保存特征点并绘制跟踪轨迹
corners_init.resize(k); // 重新设置尺寸, 下标k之后的clear掉
pyLk[1].resize(k);
drawTrackLine(frame);
swap(pyLk[1], pyLk[0]); // 交换,将此帧跟踪到的特征点作为下一帧的待跟踪点
return;
}
void drawTrackLine(Mat& frame)// 在跟踪到的且移动了的特征点(光流)的开始跟踪的位置 到 当前跟踪到的位置之间绘制线段
{
for (size_t i = 0; i < pyLk[1].size(); i++)
{
line(frame, corners_init[i], pyLk[1][i], Scalar(rng.uniform(0, 255)), 1, 8, 0);
circle(frame, pyLk[1][i], 2, Scalar(0, 0, 255), 2, 8, 0);
}
}
输出结果: