本文主要是介绍运动估计之光流估计LK,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目的:完成图像点的跟踪
概念:如下面两帧图像I和J,存在像素点的移动,即上一帧I中蓝色像素点d在下一帧J中,其位置会有些轻微的变动,则该变动即为位移向量,也就是像素点的光流。
而要计算光流,需满足以下三个前提条件:
1.相邻帧之间的亮度恒定
2.相邻视频帧的取帧时间连续,或者相邻帧之间物体的运动比较“微小”
3.保持空间一致性,也就是同一子像素的像素点具有相同的运动
推导略
其中函数:
void calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg, InputArray prevPts, InputOutputArray nextPts, OutputArray status, OutputArray err, Size winSize=Size(21,21), int maxLevel=3, TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), int flags=0, double minEigThreshold=1e-4 )
1.prevImg:第一帧图像
2.nextImg:第二帧图像
3.prevPts:第一帧图像中所有特征点向量
4.nextPts: 第二帧图像中所有特征点向量
5.status:输出状态量,若相应点光流被发现,向量的每个元素被设置为1,否则,被设置为0
其他参数一般设为默认值,可参考:openCV
代码:
#include "opencv2/opencv.hpp"using namespace cv;
using namespace std;void main()
{char *fn = "C:\\mywork\\workspace\\opencv\\sources\\samples\\data\\vtest.avi";VideoCapture cap;//该类对视频进行读取操作以及调用摄像头Mat source, result, gray, lastGray; // gray, lastGray对应本帧和上一帧灰度图vector<Point2f> points[2], temp; // 对应上一帧和本帧的特征点,上一帧时是定的,本帧是预测结果vector<uchar> status; // 每一个特征点检测状态vector<float> err; // 每一个特征点计算误差cap.open(fn);// 打开一个视频文件或打开一个摄像头if (!cap.isOpened())// 判断视频读取或者摄像头调用是否成功,成功则返回true{cout << "无法打开视频源或视频文件" << endl;return;}for (;;){cap >> source;if (source.empty())break;cvtColor(source, gray, COLOR_BGR2GRAY);//转为灰度图,供后面使用if (points[0].size() < 10) // 若点数太少,则重新检测特征点{//1._image = gray:上面cvtColor转换来的单通道灰度图//2._corners = points[0]:保存检测出的角点//3.maxCorners = 200:角点数目最大值,若实际检测的角点超过此值,则只返回前// maxCorners个强角点//4.qualityLevel = 0.01: 角点的品质因子//5.minDistance = 20:对初选出的角点而言,若在其周围minDistance范围内存在其他//更强角点,则将此角点删除。//6.mask = Mat() 若指定,它的维度必须和输入图像一致,且在mask值为0处不进行// 角点检测//7. blockSize = 3:表示在计算角点时参与运算的区域大小,常用值为3,若图像分辨率// 高则使用较大一点的值//8. useHarrisDetector: 为True表示使用Harris角点检测,为False表示Shi Tomasi检测// 但Harris角点检测存在很多缺陷,其角点是像素级别的,速度较慢,// 而goodFeaturesToTrack不仅支持Harris角点检测,也支持Shi Tomasi算法角点检测,// 该函数也是像素级别的,在实际使用中可能并不满足要求,若要获取更精细的角点坐标, // 则可使用cornerSubPix()进一步细化处理,也就是精度达到亚像素级别。goodFeaturesToTrack(gray, points[0], 200, 0.01, 20, Mat(), 3, false, 0.04);}if (lastGray.empty()) //若上一帧为空{gray.copyTo(lastGray);//将本帧灰度图复制到上一帧灰度图矩阵中}//1.prevImg = lastGray:第一帧图像,上一帧图像//2.nextImg = gray:第二帧图像,当前图像//3.prevPts = points[0]:第一帧图像中所有特征点向量,上一帧图像中所有特征点向量//4.nextPts = points[1]: 第二帧图像中所有特征点向量,本帧图像中所有特征点向量//5.status:输出状态量,若相应点光流被发现,向量的每个元素被设置为1,否则,被设置为0//其他参数一般设为默认值calcOpticalFlowPyrLK(lastGray, gray, points[0], points[1], status, err);int counter = 0;for (int i = 0; i < points[1].size(); i++){double dist = norm(points[1][i] - points[0][i]); //求向量差的范数也就是长度if (status[i] && dist >= 2.0 && dist <= 20.0) // 将2.0到20.0范围内的特征点存储起来{points[0][counter] = points[0][i];points[1][counter++] = points[1][i];}}points[0].resize(counter); //根据给定的counter添加或删除元素points[1].resize(counter);source.copyTo(result); //将source图像矩阵拷贝到result中for (int i = 0; i < points[1].size(); i++){//result 要绘制线段的图像// points[0][i] 线段的起点// points[1][i] 线段的终点// Scalar(0,0,0xff) 线段的颜色 分别对应BGRline(result, points[0][i], points[1][i], Scalar(0,0,0xff));//result 为源图像指针//points[1][i] 为画圆的圆心坐标// 3为圆的半径// Scalar(0, 0xff, 0) 为BGR 颜色circle(result, points[1][i], 3, Scalar(0, 0xff, 0));}swap(points[0], points[1]); //交换矩阵swap(lastGray, gray); //交换矩阵imshow("视频源", source);imshow("结果", result);char key = waitKey(100); if (key == 27)break;}waitKey(0);
}
运行结果:
这篇关于运动估计之光流估计LK的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!