转自http://blog.csdn.net/qianxin_dh/article/details/42810073
一.光流法回顾
光流作为飞行昆虫对外界光学信息感知,处理的方式,其概念在1950年有Gibson首次提出,80年代初期Horn和Schunck以及Lucas和Kanade做了奠基性工作,之后光流法被广泛研究,很多种方法被相继提出。1994年,Barron等对当时的有代表性光流法进行了详细的分类和评价,对光流法的发展起到了关键性作用。Barron按照理论基础和数学方法的区别把光流法分成五类:基于偏微分(梯度)的方法,基于特征匹配法的方法,基于能量的方法,基于相位的方法,其中最常用的方法为基于偏微分的方法和基于特征匹配的方法。2011年,S.Baker等研究发现大多数的光流法都是对数据项和平滑项的和的优化,并参考Barron-Fleet的分类法,根据数据项和平滑项及系数的不同取法分亮度约束法,变分法,一阶法,二阶法,空间约束法等等,并建立了Middlebury Flow数据库,包含图像序列,真实光流和对光流算法的评价。
根据光流计算的约束条件,可分为全局光流法和局部光流法,前者的代表是Horn & Schunck(HS)法及其衍生方法,后者的代表是Lucas & Kanade(LK)法及其衍生方法。使用全局光流法直接利用了整幅图像的稠密光流,免去了提取“good feature”的过程,但是由于计算量过大,影响了算法的实时性。此外,全局光流法的全局约束也会使误差在整幅图像上传播,影响光流估计准确性。相比之下,局部光流法可以在计算光流之前提取特征点,利用这些特征点的稀疏光流代表整幅图像的稠密光流,可以同时提高光流的准确性并减少计算时间。Chaos对使用光流进行视觉导航的8种系统进行了统计,LK法由于其计算的简便性及时间消耗少而最为常用。生物学家也指出果蝇也是靠特征点和光流进行导航。因此,特征点的选取对于稀疏光流的运动估计非常关键。
基于特征点的光流法只在第一帧提取特征点,然后计算特征点的光流,所以用于光流计算的特征点应该不仅可以代表图像信息,而且能够提高光流的准确性。传统的特征点是针对一副图像提取角点或纹理,如图像梯度的极值处以及Laplacian的零点,但它们可能是深度不连续点或是光滑表面上的亮度异常点,会导致光流计算不准确。SIFT或SURF特征点利用了高斯差分金字塔的局部极值点具有尺度不变性的特点,提取的特征点对图像信息具有很强的代表性。基于光流运动方程的Shi & Tomasi法提取的good feature,以包含两幅图像信息的局部邻域的矩阵的特征值为量度,易于利用光流进行跟踪,具有局部性和仿射不变形。
二.Fast特征点
文章应该是《Machine Learning for high-speed corner detection》,FAST特征顾名思义,就是快,它快于现有其它角点的检测速度,在一些对实时性要求较高的条件下,可以考虑使用该特征。FAST特征是基于特征点周围的图像灰度值,检测候选特征点周围一圈的像素值,若该像素点圆形邻域圆周上有3/4的点和该像素点灰度值差足够大,则认为该像素点为一个特征点。如下入所示:
三.代码
Fast特征和LK光流法相结合,实现对摄像头里运动物体的特征点跟踪。(配置:vs2008 + opencv2.3.1)
头文件:
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/core/core.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- #include <opencv2/features2d/features2d.hpp>
- #include <opencv2/video/tracking.hpp>
- class FeatureTracker
- {
- private:
- cv::Mat gray; //当前灰度图像
- cv::Mat gray_prev; //之前灰度图像
- //两幅图像间跟踪的特征点 0->1
- std::vector<cv::Point2f>points[2];
- //跟踪的点初始位置
- std::vector<cv::Point2f>initial;
- std::vector<cv::Point2f>features; //检测到的特征
- std::vector<uchar> status; //检测到的特征的状态
- std::vector<float> err; //跟踪过程中的错误
- public:
- FeatureTracker();
- ~FeatureTracker();
- void process(cv::Mat& frame ,cv::Mat& output);
- void detectFeaturePoints(cv::Mat& frame);
- bool addNewPoints();
- bool acceptTrackedPoint(int i);
- void handleTrackedPoints(cv::Mat& frame,cv::Mat& output);
- };
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/video/tracking.hpp>
class FeatureTracker
{
private:
cv::Mat gray; //当前灰度图像
cv::Mat gray_prev; //之前灰度图像
//两幅图像间跟踪的特征点 0->1
std::vector<cv::Point2f>points[2];
//跟踪的点初始位置
std::vector<cv::Point2f>initial;
std::vector<cv::Point2f>features; //检测到的特征
std::vector<uchar> status; //检测到的特征的状态
std::vector<float> err; //跟踪过程中的错误
public:
FeatureTracker();
~FeatureTracker();
void process(cv::Mat& frame ,cv::Mat& output);
void detectFeaturePoints(cv::Mat& frame);
bool addNewPoints();
bool acceptTrackedPoint(int i);
void handleTrackedPoints(cv::Mat& frame,cv::Mat& output);
};
函数实现:
- #include "FeatureTracker.h"
- FeatureTracker:: FeatureTracker()
- {
- }
- FeatureTracker:: ~FeatureTracker()
- {
- }
- void FeatureTracker::detectFeaturePoints(cv::Mat& frame)
- {
- std::vector<cv::KeyPoint> keyPoints;
- cv::FastFeatureDetector fast(10); //检测阈值
- fast.detect(frame,keyPoints);
- features.clear();
- for (int i=0;i<keyPoints.size();i++)
- {
- features.push_back(cv::Point2f(keyPoints[i].pt.x,keyPoints[i].pt.y));
- }
- }
- bool FeatureTracker::addNewPoints()
- {
- return points[0].size()<=10;
- }
- bool FeatureTracker::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 FeatureTracker::handleTrackedPoints(cv::Mat& frame,cv::Mat& output)
- {
- for(int i=0;i<points[1].size();i++){
- //绘制直线和圆
- cv::line(output,
- initial[i], //初始位置
- points[1][i], //新位置
- cv::Scalar(255,0,0));
- cv::circle(output,points[1][i],3,cv::Scalar(0,0,255),-1);
- }
- }
- void FeatureTracker::process(cv::Mat& frame,cv::Mat& output)
- {
- cv::cvtColor(frame,gray,CV_BGR2GRAY);
- frame.copyTo(output);
- if (addNewPoints())
- {
- detectFeaturePoints(frame);
- 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);
- //lk法跟踪特征点
- cv::calcOpticalFlowPyrLK(
- gray_prev,gray, //两幅连续图像
- points[0], //图1中的输入点坐标
- points[1], //</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:12px;">图2中的输入点坐标</span></span><span style="font-size:14px;">
- status, //跟踪成功
- err); //跟踪错误
- //遍历所有跟踪的点进行筛选
- int k=0;
- for(int i=0;i<points[1].size();i++){
- //是否需要保留该点
- if (acceptTrackedPoint(i))
- {
- //进行保留
- initial[k] = initial[i];
- points[1][k++] = points[1][i];
- }
- }
- //去除不成功的点
- points[1].resize(k);
- initial.resize(k);
- //处理接受的跟踪点
- handleTrackedPoints(frame,output);
- //当前帧的点和图像变为前一帧的点和图像
- std::swap(points[1],points[0]);
- cv::swap(gray_prev,gray);
- }
#include "FeatureTracker.h"
FeatureTracker:: FeatureTracker()
{
}
FeatureTracker:: ~FeatureTracker()
{
}
void FeatureTracker::detectFeaturePoints(cv::Mat& frame)
{
std::vector<cv::KeyPoint> keyPoints;
cv::FastFeatureDetector fast(10); //检测阈值
fast.detect(frame,keyPoints);
features.clear();
for (int i=0;i<keyPoints.size();i++)
{
features.push_back(cv::Point2f(keyPoints[i].pt.x,keyPoints[i].pt.y));
}
}
bool FeatureTracker::addNewPoints()
{
return points[0].size()<=10;
}
bool FeatureTracker::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 FeatureTracker::handleTrackedPoints(cv::Mat& frame,cv::Mat& output)
{
for(int i=0;i<points[1].size();i++){
//绘制直线和圆
cv::line(output,
initial[i], //初始位置
points[1][i], //新位置
cv::Scalar(255,0,0));
cv::circle(output,points[1][i],3,cv::Scalar(0,0,255),-1);
}
}
void FeatureTracker::process(cv::Mat& frame,cv::Mat& output)
{
cv::cvtColor(frame,gray,CV_BGR2GRAY);
frame.copyTo(output);
if (addNewPoints())
{
detectFeaturePoints(frame);
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);
//lk法跟踪特征点
cv::calcOpticalFlowPyrLK(
gray_prev,gray, //两幅连续图像
points[0], //图1中的输入点坐标
points[1], //</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:12px;">图2中的输入点坐标</span></span><span style="font-size:14px;">
status, //跟踪成功
err); //跟踪错误
//遍历所有跟踪的点进行筛选
int k=0;
for(int i=0;i<points[1].size();i++){
//是否需要保留该点
if (acceptTrackedPoint(i))
{
//进行保留
initial[k] = initial[i];
points[1][k++] = points[1][i];
}
}
//去除不成功的点
points[1].resize(k);
initial.resize(k);
//处理接受的跟踪点
handleTrackedPoints(frame,output);
//当前帧的点和图像变为前一帧的点和图像
std::swap(points[1],points[0]);
cv::swap(gray_prev,gray);
}
主函数:
- // OpticlaTracking.cpp
- //created by qianxin_dh
- #include "stdafx.h"
- #include "FeatureTracker.h"
- using namespace cv ;
- using namespace std;
- const char* winname="LK Tracking";
- int main()
- {
- VideoCapture capture;
- capture.open(1);
- namedWindow(winname,CV_WINDOW_AUTOSIZE);
- Mat frame;
- Mat output;
- FeatureTracker lk;
- while (capture>>frame)
- {
- lk.process(frame,output);
- imshow(winname, output);
- char c=waitKey(33);
- if (c==27) {
- break;
- }
- }
- return 0;
- }