(3)利用剩下的这不到一半的跟踪点输入来预测bounding box在当前帧的位置和大小 tbb:
bbPredict(points, points2, lastbox, tbb);
(4)跟踪失败检测:如果FB error的中值大于10个像素(经验值),或者预测到的当前box的位置移出图像,则认为跟踪错误,此时不返回bounding box:
if (tracker.getFB()>10 || tbb.x>img2.cols || tbb.y>img2.rows || tbb.br().x < 1 || tbb.br().y <1)
(5)归一化img2(bb)对应的patch的size(放缩至patch_size = 15*15),存入pattern:
getPattern(img2(bb),pattern,mean,stdev);
(6)计算图像片pattern到在线模型M的保守相似度:
classifier.NNConf(pattern,isin,dummy,tconf);
(7)如果保守相似度大于阈值,则评估本次跟踪有效,否则跟踪无效:
if (tconf>classifier.thr_nn_valid) tvalid =true;
6.1.2、TLD跟踪模块的实现原理和trackf2f函数的实现:
TLD跟踪模块的实现是利用了Media Flow 中值光流跟踪和跟踪错误检测算法的结合。中值流跟踪方法是基于Forward-Backward Error和NNC的。原理很简单:从t时刻的图像的A点,跟踪到t+1时刻的图像B点;然后倒回来,从t+1时刻的图像的B点往回跟踪,假如跟踪到t时刻的图像的C点,这样就产生了前向和后向两个轨迹,比较t时刻中 A点和C点的距离,如果距离小于一个阈值,那么就认为前向跟踪是正确的;这个距离就是FB_error;
bool LKTracker::trackf2f(const Mat& img1, const Mat& img2, vector<Point2f> &points1, vector<cv::Point2f> &points2)
函数实现过程如下:
(1)先利用金字塔LK光流法跟踪预测前向轨迹:
calcOpticalFlowPyrLK( img1,img2, points1, points2, status, similarity, window_size, level, term_criteria, lambda, 0);
(2)再往回跟踪,产生后向轨迹:
calcOpticalFlowPyrLK( img2,img1, points2, pointsFB, FB_status,FB_error, window_size, level, term_criteria, lambda, 0);
(3)然后计算 FB-error:前向与 后向 轨迹的误差:
for( int i= 0; i<points1.size(); ++i )
FB_error[i] = norm(pointsFB[i]-points1[i]);
(4)再从前一帧和当前帧图像中(以每个特征点为中心)使用亚象素精度提取10x10象素矩形(使用函数getRectSubPix得到),匹配前一帧和当前帧中提取的10x10象素矩形,得到匹配后的映射图像(调用matchTemplate),得到每一个点的NCC相关系数(也就是相似度大小)。
normCrossCorrelation(img1, img2, points1, points2);
(5)然后筛选出 FB_error[i] <= median(FB_error) 和 sim_error[i] > median(sim_error) 的特征点(舍弃跟踪结果不好的特征点),剩下的是不到50%的特征点;
filterPts(points1, points2);
6.2、检测模块:detect(img2);
TLD的检测分类器有三部分:方差分类器模块、集合分类器模块和最近邻分类器模块;这三个分类器是级联的。当前帧img2的每一个扫描窗口依次通过上面三个分类器,全部通过才被认为含有前景目标。具体实现过程如下:
先计算img2的积分图,为了更快的计算方差:
integral(frame,iisum,iisqsum);
然后用高斯模糊,去噪:
GaussianBlur(frame,img,Size(9,9),1.5);
下一步就进入了方差检测模块:
6.2.1、方差分类器模块:getVar(grid[i],iisum,iisqsum) >= var
利用积分图计算每个待检测窗口的方差,方差大于var阈值(目标patch方差的50%)的,则认为其含有前景目标,通过该模块的进入集合分类器模块: