后续将简单介绍光流法的一些简单实现包,包括opencv下的光流算法与matlab下的光流算法。该节主要介绍opencv下的光流实现。
Opencv的光流实现由好几个方法可以(也就是说有好几个函数可以用),每个函数当然也对应着不同的原理,那么它的效果以及算法的速度等等就会有一些差别。主要包括以下几种:
calcOpticalFlowPyrLK
calcOpticalFlowFarneback
calcOpticalFlowBM
calcOpticalFlowHS
calcOpticalFlowSF
参见opencv文档介绍
这里先简单介绍下calcOpticalFlowPyrLK函数
calcOpticalFlowPyrLK函数使用形式:
calcOpticalFlowPyrLK(prevImg,nextImg,prevPts,nextPts,status,err)
这个函数还有一些可选参数,一大堆,详细的说明可以看opencv介绍:
这里只是贴出了主要的几个输入输出:
prevImg:就是你需要输入计算光流的前一帧图像
nextImg就是下一帧图像(可以看到一次光流就是在两针图像之间找不同)。
prevPts是前一帧图像中的特征点,这个特征点必须自己去找,所以在使用calcOpticalFlowPyrLK函数的时候,前面需要有一个找特征点的操作,那么一般就是找图像的角点,就是一个像素点与周围像素点都不同的那个点,这个角点特征点的寻找,opencv也提供夜歌函数:goodFeatureToTrack()(后面再介绍这个函数)。那么关于特征点有没有其他的方式呢?肯定是有的而且还很多吧。
nextPts参数就是计算特征点在第二幅图像中的新的位置,然后输出。特征点的新位置可能变化了,也可能没有变化,那么这种状态就存放在后一个参数status中。err就是新旧两个特征点位置的误差了,也是一个输出矩阵。
其他参数默认吧。
关于特征角点检测函数goodFeatureToTrack()
goodFeaturesToTrack(image,corners,maxCorners,qualityLevel,minDistance)
函数的参数:
image输入图像;
corners输出的特征点系列,每一个元素就是一个特征点的位置。
maxCorners规定的特征点最大数目,比如一副图像你可以找到很多特征点,但是只是取前maxCorners个具有最大特征的那些点作为最后的特征点,至于怎么判断哪些点的特征更好了,opencv自有一个机制,随便一个方法,比如计算一下这个点与周围一定领域的点的灰度相差求和,认为这个和越大的那些点是不是越属于特征点。
qualityLevel是一个特征点的取到水平,其实也是控制特征点的选取的,看结果适当选取吧。
minDistance是特征点与点之间的最小距离,一般我们如果想特征点尽量分散一些,太密集了肯定不好,那么我们可以通过这个参数。
比如说一个例子如下:
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat I1;
Mat I2;
vector features;
int maxCout = 50;//定义最大个数
double minDis = 20;//定义最小距离
double qLevel = 0.01;//定义质量水平
I1 = imread("I1.jpg",0);//读取为灰度图像
goodFeaturesToTrack(I1,features,maxCout,qLevel,minDis);
for(int i=0;i//将特征点画一个小圆出来--粗细为2
circle(I1,features[i],3,Scalar(255),2);
}
imshow("features",I1);
waitKey(0);
return 0;
}
修改下 int maxCout = 100;//定义最大个数
double minDis = 10;//定义最小距离
下面通过这种特征点来以及L-K光流法来检测两幅相邻帧图像之间的移动点。首先自己准备两幅图吧,图中要有运动的目标才好,然后我们就这两幅图简单的看下里面的运动目标的特征点的运动吧,一个简单程序如下:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat I1;
Mat I2;
vector features;
vector features_after;
vector status;
vector<float> err;
int maxCout = 300;//定义最大个数
double minDis = 10;//定义最小距离
double qLevel = 0.01;//定义质量水平
//读取两个图像---相邻帧
I1 = imread("I1.jpg",0);//读取为灰度图像
I2 = imread("I2.jpg",0);
//检测第一帧的特征点
goodFeaturesToTrack(I1,features,maxCout,qLevel,minDis);
//计算出第二帧的特征点
calcOpticalFlowPyrLK(I1,I2,features,features_after,status,err);
//判别哪些属于运动的特征点
int k = 0;
for(int i=0;i//状态要是1,并且坐标要移动下的那些点
if(status[i]&&((abs(features[i].x-features_after[i].x)+
abs(features[i].y-features_after[i].y))>4))
{
features_after[k++] = features_after[i];
}
}
features_after.resize(k);//截取
cout<for(int i=0;i//将特征点画一个小圆出来--粗细为2
circle(I1,features_after[i],3,Scalar(255),2);
}
imshow("features",I1);
waitKey(0);
return 0;
}
我的这两幅树叶图是存在这一些变化的,尤其树存在的地方,由于需要动态的来回切换着看才能看出变化,所以这里不太好显示这两幅图的变化,这里吧他们相差的图做出来如下:
可以看到的是越白的地方就是存在着不一致,也就是存在着明显运动的地方。和上面角点检测的还是有点符合的。
当然,我这可能只是连续两帧运动情况下的跟踪的特征点,如果是一个视频(连续的很多帧),那么把每两帧之间的运动点连接起来,就可以发现运动的物体的整个轨迹了。像网上博客有人做过的贴几个:
Opencv学习笔记(九)光流法