用光流法实现视频中特征点的跟踪
2016-04-28 17:31
519 查看
在开始跟踪前,首先要在初始帧中检测特征点,之后在下一帧中尝试跟踪这些点。你必须找到新的图像帧中这些点的位置。很明显的,由于我们处理的是视频序列,很有可能特征点所在的物体已经移动过(运动也有可能是相机引起的)。因此,你必须在特征点的先前位置附近进行搜索,以找到下一帧中它的新位置。这正是cv::calcOpticalFlowPyrLK函数所实现的工作。你输入两个连续的图像帧以及第一幅图像中检测到的特征点数组,该函数将返回一组新的特征点为位置。为了跟踪完整的序列,你需要在帧与帧之间重复这个过程,不可避免地你也会丢失其中一些点,于是被跟踪的特征点数目会减少。为了解决这个问题,我们可以不时地检测新的特征值。
结果
// OpticalFlow.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <opencv2\opencv.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace std; using namespace cv; //帧处理基类 class FrameProcessor { public: virtual void process(Mat &input,Mat &ouput)=0; }; class FeatureTracker:public FrameProcessor { 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.){} 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++) { //是否需要保留该点? if(acceptTrackedPoint(i)) { //进行保留 initial[k]=initial[i]; points[1][k++]=points[1][i]; } } //去除不成功的点 points[1].resize(k); initial.resize(k); //3.处理接收的跟踪点 handleTrackedPoints(frame,output); //4.当前帧的点和图像变为前一帧的点和图像 swap(points[1],points[0]); swap(gray_prev,gray); } //检测特征点 void detectFeaturePoints() { //检测特征 goodFeaturesToTrack(gray,//图像 features,//检测到的特征 max_count,//特征的最大数目 qlevel,//质量等级 minDist);//两个特征之间的最小距离 } //是否需要添加新的点 bool addNewPoints() { //如果点的数量太少 return points[0].size()<=10; } //决定哪些点应该跟踪 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); } } }; class VideoProcessor { private: VideoCapture caputure; //输出文件名 string Outputfile; int currentIndex; int digits; string extension; FrameProcessor *frameprocessor; //图像处理函数指针 void (*process)(Mat &,Mat &); bool callIt; string WindowNameInput; string WindowNameOutput; //延时 int delay; long fnumber; //第frameToStop停止 long frameToStop; //暂停标志 bool stop; //图像序列作为输入视频流 vector<string> images; //迭代器 public: VideoProcessor():callIt(true),delay(0),fnumber(0),stop(false),digits(0),frameToStop(-1){} //设置图像处理函数 void setFrameProcessor(void (*process)(Mat &,Mat &)){ frameprocessor = 0; this->process = process; CallProcess (); } //打开视频 bool setInput(string filename){ fnumber = 0; //若已打开,释放重新打开 caputure.release (); return caputure.open (filename); } //设置输入视频播放窗口 void displayInput(string wn){ WindowNameInput = wn; namedWindow (WindowNameInput); } //设置输出视频播放窗口 void displayOutput(string wn){ WindowNameOutput = wn; namedWindow (WindowNameOutput); } //销毁窗口 void dontDisplay(){ destroyWindow (WindowNameInput); destroyWindow (WindowNameOutput); WindowNameInput.clear (); WindowNameOutput.clear (); } //启动 void run(){ Mat frame; Mat output; if(!isOpened()) return; stop = false; while(!isStopped()){ //读取下一帧 if(!readNextFrame(frame)) break; if(WindowNameInput.length ()!=0) imshow (WindowNameInput,frame); //处理该帧 if(callIt){ if(process) process(frame,output); else if(frameprocessor) frameprocessor->process (frame,output); } else{ output = frame; } if(WindowNameOutput.length ()!=0) imshow (WindowNameOutput,output); //按键暂停,继续按键继续 if(delay>=0&&waitKey (delay)>=0) waitKey(0); //到达指定暂停键,退出 if(frameToStop>=0&&getFrameNumber()==frameToStop) stopIt(); } } //暂停键置位 void stopIt(){ stop = true; } //查询暂停标志位 bool isStopped(){ return stop; } //返回视频打开标志 bool isOpened(){ return caputure.isOpened ()||!images.empty (); } //设置延时 void setDelay(int d){ delay = d; } //读取下一帧 bool readNextFrame(Mat &frame){ if(images.size ()==0) return caputure.read (frame); else{ if(itImg!=images.end()){ frame = imread (*itImg); itImg++; return frame.data?1:0; } else return false; } } void CallProcess(){ callIt = true; } void dontCallProcess(){ callIt = false; } //设置停止帧 void stopAtFrameNo(long frame){ frameToStop = frame; } // 获得当前帧的位置 long getFrameNumber(){ long fnumber = static_cast<long>(caputure.get ((CV_CAP_PROP_POS_FRAMES))); return fnumber; } //获取帧率 double getFrameRate(){ return caputure.get(CV_CAP_PROP_FPS); } vector<string>::const_iterator itImg; bool setInput (const vector<string> &imgs){ fnumber = 0; caputure.release (); images = imgs; itImg = images.begin (); return true; } void setFrameProcessor(FrameProcessor *frameprocessor){ process = 0; this->frameprocessor = frameprocessor; CallProcess (); } }; int main() { //创建视频处理器实例 VideoProcessor processor; //创建特征跟踪器实例 FeatureTracker tracker; //打开视频文件 processor.setInput("walk.avi"); //设置帧处理器对象 processor.setFrameProcessor(&tracker); //声明显示窗口 processor.displayOutput("Tracked Features"); //以原始帧率播放视频 processor.setDelay(1000./processor.getFrameRate()); //开始处理过程 processor.run(); return 0; }
结果
相关文章推荐
- 15电气李书鹏4月28日VB作业(3)
- JAVA 判断一个字符串是不是一个合法的日期格式
- 【框架】Fresco加载图片
- 关于getch()函数
- 关于getch()函数
- 关于getch()函数
- C/C++中判断两个变量是否相等,相减是否为0、大于0或小于0时要特别注意机器误差带来的影响!
- 关于castle和Could not find the dialect in the configuration错误
- 面试感悟----一名3年工作经验的程序员应该具备的技能
- 三位数乘以一位数
- 回到页面顶部的几种解决方案(转载)
- ARM AMBA 外围设备 的datasheet
- mysql中连接符的问题
- 一个简单RPC,java实现
- iOS之常用第三方库的介绍
- 新特性预览之数据池与参数泵
- 用myclipse连接navicat(mysql数据库)
- 设计模式之多例模式
- 当你访问淘宝的时候,发生了什么?
- 2进制