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

来源:互联网 发布:江民杀毒 知乎 编辑:程序博客网 时间:2024/05/22 14:44

什么是光流?

维基百科解释: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 = 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方向的速度值合成了一个速度值并进行画图。

0 0
原创粉丝点击