光流作为飞行昆虫对外界光学信息感知,处理的方式,其概念在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,以包含两幅图像信息的局部邻域的矩阵的特征值为量度,易于利用光流进行跟踪,具有局部性和仿射不变形。
文章应该是《Machine Learning for high-speed corner detection》,FAST特征顾名思义,就是快,它快于现有其它角点的检测速度,在一些对实时性要求较高的条件下,可以考虑使用该特征。FAST特征是基于特征点周围的图像灰度值,检测候选特征点周围一圈的像素值,若该像素点圆形邻域圆周上有3/4的点和该像素点灰度值差足够大,则认为该像素点为一个特征点。如下入所示:
头文件:
#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); }
主函数:
// 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; }