openCV光流法追踪运动物体
email:chentravelling@163.com
一、光流简单介绍
摘自:zouxy09
光流的概念是Gibson在1950年首先提出来的。它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的相应关系。从而计算出相邻帧之间物体的运动信息的一种方法。一般而言,光流是因为场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的。
研究光流场的目的就是为了从图片序列中近似得到不能直接得到的运动场。运动场,事实上就是物体在三维真实世界中的运动。光流场,是运动场在二维图像平面上(人的眼睛或者摄像头)的投影。
那通俗的讲就是通过一个图片序列,把每张图像中每一个像素的运动速度和运动方向找出来就是光流场。
那怎么找呢?咱们直观理解肯定是:第t帧的时候A点的位置是(x1, y1),那么我们在第t+1帧的时候再找到A点,假如它的位置是(x2,y2),那么我们就能够确定A点的运动了:(ux, vy) = (x2,
y2) - (x1,y1)。
那怎么知道第t+1帧的时候A点的位置呢? 这就存在非常多的光流计算方法了。
1981年,Horn和Schunck创造性地将二维速度场与灰度相联系,引入光流约束方程,得到光流计算的基本算法。人们基于不同的理论基础提出各种光流计算方法,算法性能各有不同。Barron等人对多种光流计算技术进行了总结。依照理论基础与数学方法的差别把它们分成四种:基于梯度的方法、基于匹配的方法、基于能量的方法、基于相位的方法。
近年来神经动力学方法也颇受学者重视。
OpenCV中实现了不少的光流算法。
1)calcOpticalFlowPyrLK
通过金字塔Lucas-Kanade 光流方法计算某些点集的光流(稀疏光流)。理解的话,能够參考这篇论文:”Pyramidal Implementation of the Lucas Kanade Feature TrackerDescription of the algorithm”
2)calcOpticalFlowFarneback
用Gunnar Farneback 的算法计算稠密光流(即图像上全部像素点的光流都计算出来)。
它的相关论文是:"Two-Frame Motion Estimation Based on PolynomialExpansion"
3)CalcOpticalFlowBM
通过块匹配的方法来计算光流。
4)CalcOpticalFlowHS
用Horn-Schunck 的算法计算稠密光流。
相关论文好像是这篇:”Determining Optical Flow”
5)calcOpticalFlowSF
这一个是2012年欧洲视觉会议的一篇文章的实现:"SimpleFlow: A Non-iterative, Sublinear Optical FlowAlgorithm",project站点是:http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/ 在OpenCV新版本号中有引入。
稠密光流须要使用某种插值方法在比較easy跟踪的像素之间进行插值以解决那些运动不明白的像素。所以它的计算开销是相当大的。而对于稀疏光流来说,在他计算时须要在被跟踪之前指定一组点(easy跟踪的点。比如角点),因此在使用LK方法之前我们须要配合使用cvGoodFeatureToTrack()来寻找角点,然后利用金字塔LK光流算法,对运动进行跟踪。
二、代码
#include <opencv2\opencv.hpp>
#include <iostream> using namespace std;
using namespace cv;
const int MAX_CORNERS = 100; int main()
{
IplImage* preImage = cvLoadImage("image133.pgm",CV_LOAD_IMAGE_GRAYSCALE);
IplImage* curImage = cvLoadImage("image134.pgm",CV_LOAD_IMAGE_GRAYSCALE);
IplImage* curImage_ = cvLoadImage("image134.pgm");
CvPoint2D32f* preFeatures = new CvPoint2D32f[ MAX_CORNERS ];//前一帧中特征点坐标(通过cvGoodFeaturesToTrack获得)
CvPoint2D32f* curFeatures = new CvPoint2D32f[ MAX_CORNERS ];//在当前帧中的特征点坐标(通过光流法获得)
double qlevel; //特征检測的指标
double minDist;//特征点之间最小容忍距离
vector<uchar> status; //特征点被成功跟踪的标志
vector<float> err; //跟踪时的特征点小区域误差和
CvSize img_sz = cvGetSize(preImage);
IplImage* eig_image = cvCreateImage( img_sz, IPL_DEPTH_32F, 1 );//缓冲区
IplImage* tmp_image = cvCreateImage( img_sz, IPL_DEPTH_32F, 1 );
int num = MAX_CORNERS;
cvGoodFeaturesToTrack(//获取特征点
preImage,
eig_image,
tmp_image,
preFeatures,
&num,
0.01,
5.0,
0,
3,
0,
0.04
);
CvSize pyr_sz = cvSize( curImage->width+8, curImage->height/3 );
IplImage* pyrA = cvCreateImage( pyr_sz, IPL_DEPTH_32F, 1 );
IplImage* pyrB = cvCreateImage( pyr_sz, IPL_DEPTH_32F, 1 ); char features_found[ MAX_CORNERS ];
float feature_errors[ MAX_CORNERS ]; cvCalcOpticalFlowPyrLK(//计算光流
preImage,
curImage,
pyrA,
pyrB,
preFeatures,
curFeatures,
MAX_CORNERS,
cvSize(10,10),
5,
features_found,
feature_errors,
cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, .3 ),
0
); for(int i=0;i<MAX_CORNERS;i++)//画线
{
cvLine(
curImage_,
Point(preFeatures[i].x,preFeatures[i].y),
Point(curFeatures[i].x,curFeatures[i].y),
CV_RGB(255,0,0),
4,
CV_AA,
0);
} cvShowImage("outPutImage",curImage_);
//cvSaveImage("outPutImage.pgm",curImage);
waitKey(0);
return 0;
}
终于结果:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">