您的位置:首页 > 运维架构

光流法简单介绍(结合Opencv2)

2015-09-29 19:22 471 查看

什么是光流?

维基百科解释:Optical flow or optic flow is the
pattern ofapparent motion of objects, surfaces, and edges in a visual scenecaused by the relative motion between an observer (an eye or acamera) and the scene.
  

名称来源:当人的眼睛观察运动物体时,物体的景象在人眼的视网膜上形成一系列连续变化的图像,这一系列连续变化的信息不断“流过”视网膜,好像一种光的“流”,故称之为光流。

光流算法的输入输出

光流算法分成稠密光流和稀疏光流,稠密光流(dense optical flow)对图像中的每个像素都进行速度的计算,而稀疏光流(sparse optical flow)是对图像中指定的一组点(常用角点)进行速度的计算。

例如,第t帧的时候A点的位置是(x1,
y1),那么我们在第t+1帧的时候再找到A点,假如它的位置是(x2,y2),那么我们就可以确定A点的速度了:(ux,
vy) = (x2, y2) - (x1,y1)(可以认为是一个矢量)。

再具体一点,拿Opencv中的函数作为例子。

calcOpticalFlowPyrLK(InputArray prevImg,
InputArray nextImg, InputArray prevPts, InputOutputArray nextPts,
OutputArray status, OutputArray err, Size winSize=Size(15,15),
int maxLevel=3, TermCriteriacriteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,
30, 0.01), double derivLambda=0.5, int flags=0)

这个函数使用金字塔LK方法,属于稀疏光流,前四个参数依次为:前一帧图片,后一帧图片,需要跟踪的点在前一帧图片的坐标,那些需要跟踪的点在后一帧中的坐标(函数的输出)。

calcOpticalFlowFarneback(InputArray prevImg,
InputArray nextImg, InputOutputArray flow, double pyrScale,
intlevels, int winsize, int iterations,
int polyN, double polySigma, int flags)

这个函数使用Farneback的方法,属于稠密光流,前两个参数与上面一致,第三个参数是函数的输出,一个与图片尺寸一致(每个像素都有对应的值),CV_32FC2类型的矩阵(有两个通道),也就是说对于每个像素,都有x方向和y方向的速度值。

程序验证

<pre name="code" class="cpp">#include <iostream>
#include "opencv2/opencv.hpp"
/*include all opencv2 hpp*/

using namespace cv;
using namespace std;

/*定义π值*/
static const double pi = 3.14159265358979323846;

/*读取两帧图片 使用calcOpticalFlowPyrLK 算出光流 并用向量表示

*/
int main(int argc, char** argv)
{
/*初始化主要用到的变量*/
Mat prevGray, gray,prevFrame,frame,show,flow;

prevFrame = imread("frame10.png");
frame =
4000
imread("frame11.png");
cvtColor(prevFrame, prevGray, COLOR_BGR2GRAY);
cvtColor(frame, gray, COLOR_BGR2GRAY);

/*最大特征点数量*/
const int MAX_COUNT = 300;

/*算法终止条件termcrit 参数:第一个是类型,第二个参数为迭代的最大次数,最后一个是特定的阈值*/
TermCriteria termcrit(TermCriteria::COUNT + TermCriteria::EPS, 20, 0.01);
/*搜索窗口*/
Size subPixWinSize(10, 10), winSize(31, 31);
/*声明一个泛型为Point的vector,名为points	长度为2 points[0] points[1]各为一个Point类的数组*/
vector<Point2f> points[2];

/*寻找角点*/
goodFeaturesToTrack(prevGray, points[0], MAX_COUNT, 0.01, 10);
/*如果对角点的精度有更高的要求,可以用cornerSubPix()函数将角点定位到子像素,从而取得亚像素级别的角点检测效果*/
cornerSubPix(gray, points[0], subPixWinSize, Size(-1, -1), termcrit);

vector<uchar> status;
vector<float> err;
/*PyrLK法计算光流*/
calcOpticalFlowPyrLK(prevGray, gray, points[0], points[1], status, err, winSize,5, termcrit, 0, 0.001);

/*Farneback法计算稠密光流 输出为flow CV32FC2 32位浮点型双通道矩阵 分别记录x y方向的速度*/
calcOpticalFlowFarneback(prevGray, gray, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
/*尝试将flow转为灰度图显示*/
Mat flowIntensity(flow.size(), CV_32FC1);
for (int i = 0; i < flow.rows; i++)
for (int j = 0; j < flow.cols; j++)
{
Vec2f intensity = flow.at<Vec2f>(i, j);
float maybeX = intensity.val[0];
float maybeY = intensity.val[1];
flowIntensity.at<float>(i, j) = sqrt(maybeX*maybeX + maybeY*maybeY);
}

/*画LK法向量图*/
for (int i = 0; i < points[1].size(); i++)
{
if (!status[i])
continue;
if (points[0][i] == points[1][i])
continue;
line(frame, points[0][i], points[1][i],Scalar(0,0,255),2);
line(prevFrame, points[0][i], points[1][i], Scalar(0, 0, 255), 2);

Point p, q;
p=points[0][i];
q = points[1][i];
double angle; angle = atan2((double)p.y - q.y, (double)p.x - q.x);

p.x = (int)(q.x + 9 * cos(angle + pi / 4));
p.y = (int)(q.y + 9 * sin(angle + pi / 4));
line(frame, p, q, Scalar(0, 0, 255), 2);
line(prevFrame, p, q, Scalar(0, 0, 255), 2);
p.x = (int)(q.x + 9 * cos(angle - pi / 4));
p.y = (int)(q.y + 9 * sin(angle - pi / 4));
line(frame, p, q, Scalar(0, 0, 255), 2);
line(prevFrame, p, q, Scalar(0, 0, 255), 2);

}

/*打开窗口*/

namedWindow("LK Demo", WINDOW_AUTOSIZE);
namedWindow("Farneback", WINDOW_AUTOSIZE);
show=frame;

for (;;)
{
imshow("LK Demo", show);
imshow("Farneback", flowIntensity);
char c = (char)waitKey(10);
switch (c)
{
/*按下p键 观看前一帧*/
case 'p':
show = prevFrame;
break;
/*按下n键 观看后一帧*/
case 'n':
show = frame;
break;
case 'q':
return 0;
default:
break;
}
}
return 0;
}




对于LK法,可以按下n键和p键对比前后帧图片点的位置。

对于Farneback,我把x和y方向的速度值合成了一个速度值并进行画图。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  光流法 opencv