因为在理解LK光流opencv源码的过程中,没有找到相关的资料,因此将自己理解的过程及思考贴出来,希望可以记录并便于大家查阅,如果大家对代码有不同的理解或想法,欢迎评论讨论~
opencv--LK光流算法--源码解析
LK光流算法由Jean - Yves Bouguet提出,该算法是基于亮度恒定、时间连续、空间具有一致性的前提下,提出的一种基于特征点的追踪算法。该算法是将求两帧间特征点的最小误差值应用到图像上进而转换为求图像微分、差分的一种迭代算法。
在理解源码时,需要提前掌握LK光流算法的数学原理,否则阅读起来是比较繁难的。
- 光流算法用例
Mat old_frame, old_gray;
vector<Point2f> p0, p1;
// Take first frame and find corners in it
capture >> old_frame;
cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
//根据第一帧图像和参数设置,获得第一帧图像的特征点
goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04);
// Create a mask image for drawing purposes
Mat mask = Mat::zeros(old_frame.size(), old_frame.type());
while(true){
Mat frame, frame_gray;
capture >> frame;
if (frame.empty())
break;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
// calculate optical flow
vector<uchar> status;
vector<float> err;
//设置光流迭代终止标准,10为迭代次数,0.03为迭代终止标准
TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03);
//光流算法,输入前一帧,后一帧,前一帧特征点,后一帧特征点,特征点状态以及微分窗口大小等
calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);
vector<Point2f> good_new;
for(uint i = 0; i < p0.size(); i++)
{
// Select good points
if(status[i] == 1) {//如果特征点状态为1,则进行绘制
good_new.push_back(p1[i]);
// draw the tracks
line(mask,p1[i], p0[i], colors[i], 2);
circle(frame, p1[i], 5, colors[i], -1);
}
}
Mat img;
add(frame, mask, img);//将绘制出的mask叠加到frame上
imshow("Frame", img);
int keyboard = waitKey(30);
if (keyboard == 'q' || keyboard == 27)
break;
// Now update the previous frame and previous points
old_gray = frame_gray.clone();
p0 = good_new;
}
以上是opencv的官方用例摘自:d:\opencv\opencv3.4.7\opencv3.4.7\samples\cpp\tutorial_code\video\optical_flow\optical_flow.cpp
用例中最为核心的两句代码:goodFeaturesToTrack,calcOpticalFlowPyrLK;其中goodFeaturesToTrack负责查找特征点,calcOpticalFlowPyrLK是光流法的主体,我会以calcOpticalFlowPyrLK为开始,对代码进行解析。
2. calcOpticalFlowPyrLK函数详解
void cv::calcOpticalFlowPyrLK( InputArray _prevImg, InputArray _nextImg,
InputArray _prevPts, InputOutputArray _nextPts,
OutputArray _status, OutputArray _err,
Size winSize, int maxLevel,
TermCriteria criteria,
int flags, double minEigThreshold )
{
//初始化光流,包括winSize:微分窗口;maxLevel:金字塔层数;criteria:迭代终止条件
//flag;minEigThreshold:特征值求解的最小阈值,小于该值,G不可逆,特征点不可追踪
Ptr<cv::SparsePyrLKOpticalFlow> optflow = cv::SparsePyrLKOpticalFlow::create(winSize,maxLevel,criteria,flags,minEigThreshold);
//根据初始条件,计算特征点在下一帧中的追踪位置
optflow->calc(_prevImg,_nextImg,_prevPts,_nextPts,_status,_err);
//_prevImg:上一帧图像,_nextImg:下一帧图像
//_prevPts:_prevImg中的特征点,_nextPts:calc计算后,得出的特征点存放地址
//_status:特征点状态,能否找到该特征点,_err:衡量特征点相似度或误差度
}
其实内层的光流计算代码为:void SparsePyrLKOpticalFlowImpl::calc( InputArray _prevImg, InputArray _nextImg,
InputArray _prevPts, InputOutputArray _nextPts,
OutputArray _status, OutputArray _err)
- 2.1 calc函数详解--第一部分--输入变量处理
void SparsePyrLKOpticalFlowImpl::calc( InputArray _prevImg, InputArray _nextImg,
InputArray _prevPts, InputOutputArray _nextPts,
OutputArray _status, OutputArray _err)
{
CV_INSTRUMENT_REGION();
CV_OCL_RUN(ocl::isOpenCLActivated() &&
(_prevImg.isUMat() || _nextImg.isUMat()) &&
ocl::Image2D::isFormatSupported(CV_32F, 1, false),
ocl_calcOpticalFlowPyrLK(_prevImg, _nextImg, _prevPts, _nextPts, _status, _err))
// Disabled due to bad accuracy
CV_OVX_RUN(false,
openvx_pyrlk(_p

本文深入解析了LK光流算法的opencv源码实现,详细介绍了算法的数学原理、流程及核心函数calcOpticalFlowPyrLK的内部机制,适合对光流追踪感兴趣的研究者和开发者。
最低0.47元/天 解锁文章
1597





