下面是自己在看論文和這些大牛的分析過程中,對代碼進行了一些理解,但是由於自己接觸影像處理和機器視覺沒多久,另外由於自己編程能力比較弱,所以分析過程可能會有不少的錯誤,希望各位不吝指正。而且,因為編程很多地方不懂,所以注釋得非常亂,還海涵。
LKTracker.h
#include<tld_utils.h>#include <opencv2/opencv.hpp>//使用金字塔LK光流法跟蹤,所以類的成員變數很多都是OpenCV中calcOpticalFlowPyrLK()函數的參數class LKTracker{private: std::vector<cv::Point2f> pointsFB; cv::Size window_size; //每個金字塔層的搜尋視窗尺寸 int level; //最大的金字塔層數 std::vector<uchar> status; //數組。如果對應特徵的光流被發現,數組中的每一個元素都被設定為 1, 否則設定為 0 std::vector<uchar> FB_status; std::vector<float> similarity; //相似性 std::vector<float> FB_error; //Forward-Backward error方法,求FB_error的結果與原始位置的歐式距離 //做比較,把距離過大的跟蹤結果捨棄 float simmed; float fbmed; //TermCriteria模板類,取代了之前的CvTermCriteria,這個類是作為迭代演算法的終止條件的 //該類變數需要3個參數,一個是類型,第二個參數為迭代的最大次數,最後一個是特定的閾值。 //指定在每個金字塔層,為某點尋找光流的迭代過程的終止條件。 cv::TermCriteria term_criteria; float lambda; //某閾值??Lagrangian 乘子 // NCC 歸一化交叉相關,FB error與NCC結合,使跟蹤更穩定 交叉相關的映像匹配演算法?? //交叉相關法的作用是進行雲團移動的短時預測。選取連續兩個時次的GMS-5衛星雲圖,將雲圖地區劃分為32×32像素 //的映像子集,採用交叉相關法計算擷取兩幅雲圖的首選地區,根據前後雲圖匹配地區的位置和時間間隔,確 //定出每個映像子集的移動向量(速度和方向),並對映像子集的移動向量進行客觀分析,其後,基於檢驗後的雲 //圖移動向量集,利用後向軌跡方法對雲圖作短時外推預測。 void normCrossCorrelation(const cv::Mat& img1, const cv::Mat& img2, std::vector<cv::Point2f>& points1, std::vector<cv::Point2f>& points2); bool filterPts(std::vector<cv::Point2f>& points1,std::vector<cv::Point2f>& points2);public: LKTracker(); //特徵點的跟蹤?? bool trackf2f(const cv::Mat& img1, const cv::Mat& img2, std::vector<cv::Point2f> &points1, std::vector<cv::Point2f> &points2); float getFB(){return fbmed;}};
LKTracker.cpp
#include <LKTracker.h>using namespace cv;//金字塔LK光流法跟蹤//Media Flow 中值光流跟蹤 加 跟蹤錯誤偵測//建構函式,初始化成員變數LKTracker::LKTracker(){ ////該類變數需要3個參數,一個是類型,第二個參數為迭代的最大次數,最後一個是特定的閾值。 term_criteria = TermCriteria( TermCriteria::COUNT + TermCriteria::EPS, 20, 0.03); window_size = Size(4,4); level = 5; lambda = 0.5;}bool LKTracker::trackf2f(const Mat& img1, const Mat& img2, vector<Point2f> &points1, vector<cv::Point2f> &points2){ //TODO!:implement c function cvCalcOpticalFlowPyrLK() or Faster tracking function //Forward-Backward tracking //基於Forward-Backward Error的中值流跟蹤方法 //金字塔LK光流法跟蹤 //forward trajectory 前向軌跡跟蹤 calcOpticalFlowPyrLK( img1,img2, points1, points2, status, similarity, window_size, level, term_criteria, lambda, 0); //backward trajectory 後向軌跡跟蹤 calcOpticalFlowPyrLK( img2,img1, points2, pointsFB, FB_status,FB_error, window_size, level, term_criteria, lambda, 0); //Compute the real FB-error //原理很簡單:從t時刻的映像的A點,跟蹤到t+1時刻的映像B點;然後倒回來,從t+1時刻的映像的B點往回跟蹤, //假如跟蹤到t時刻的映像的C點,這樣就產生了前向和後向兩個軌跡,比較t時刻中 A點 和 C點 的距離,如果距離 //小於一個閾值,那麼就認為前向跟蹤是正確的;這個距離就是FB_error //計算 前向 與 後向 軌跡的誤差 for( int i= 0; i<points1.size(); ++i ){ FB_error[i] = norm(pointsFB[i]-points1[i]); //norm()求矩陣或向量的範數??絕對值? } //Filter out points with FB_error[i] <= median(FB_error) && points with sim_error[i] > median(sim_error) normCrossCorrelation(img1, img2, points1, points2); return filterPts(points1, points2);}//利用NCC把跟蹤預測的結果周圍取10*10的小圖片與原始位置周圍10*10的小圖片(使用函數getRectSubPix得到)進//行模板匹配(調用matchTemplate)void LKTracker::normCrossCorrelation(const Mat& img1,const Mat& img2, vector<Point2f>& points1, vector<Point2f>& points2) { Mat rec0(10,10,CV_8U); Mat rec1(10,10,CV_8U); Mat res(1,1,CV_32F); for (int i = 0; i < points1.size(); i++) { if (status[i] == 1) { //為1表示該特徵點跟蹤成功//從前一幀和當前幀映像中(以每個特徵點為中心?)提取10x10象素矩形,使用亞象素精度 getRectSubPix( img1, Size(10,10), points1[i],rec0 ); getRectSubPix( img2, Size(10,10), points2[i],rec1);//匹配前一幀和當前幀中提取的10x10象素矩形,得到匹配後的映射映像//CV_TM_CCOEFF_NORMED 歸一化相關係數匹配法//參數分別為:欲搜尋的映像。搜尋模板。比較結果的映射映像。指定匹配方法 matchTemplate( rec0,rec1, res, CV_TM_CCOEFF_NORMED); similarity[i] = ((float *)(res.data))[0]; //得到各個特徵點的相似性大小 } else { similarity[i] = 0.0; } } rec0.release(); rec1.release(); res.release();}//篩選出 FB_error[i] <= median(FB_error) 和 sim_error[i] > median(sim_error) 的特徵點//得到NCC和FB error結果的中值,分別去掉中值一半的跟蹤結果不好的點bool LKTracker::filterPts(vector<Point2f>& points1,vector<Point2f>& points2){ //Get Error Medians simmed = median(similarity); //找到相似性的中值 size_t i, k; for( i=k = 0; i<points2.size(); ++i ){ if( !status[i]) continue; if(similarity[i]> simmed){ //剩下 similarity[i]> simmed 的特徵點 points1[k] = points1[i]; points2[k] = points2[i]; FB_error[k] = FB_error[i]; k++; } } if (k==0) return false; points1.resize(k); points2.resize(k); FB_error.resize(k); fbmed = median(FB_error); //找到FB_error的中值 for( i=k = 0; i<points2.size(); ++i ){ if( !status[i]) continue; if(FB_error[i] <= fbmed){ / points1[k] = points1[i]; //再對上一步剩下的特徵點進一步篩選,剩下 FB_error[i] <= fbmed 的特徵點 points2[k] = points2[i]; k++; } } points1.resize(k); points2.resize(k); if (k>0) return true; else return false;}/* * old OpenCV stylevoid LKTracker::init(Mat img0, vector<Point2f> &points){ //Preallocate //pyr1 = cvCreateImage(Size(img1.width+8,img1.height/3),IPL_DEPTH_32F,1); //pyr2 = cvCreateImage(Size(img1.width+8,img1.height/3),IPL_DEPTH_32F,1); //const int NUM_PTS = points.size(); //status = new char[NUM_PTS]; //track_error = new float[NUM_PTS]; //FB_error = new float[NUM_PTS];}void LKTracker::trackf2f(..){ cvCalcOpticalFlowPyrLK( &img1, &img2, pyr1, pyr1, points1, points2, points1.size(), window_size, level, status, track_error, term_criteria, CV_LKFLOW_INITIAL_GUESSES); cvCalcOpticalFlowPyrLK( &img2, &img1, pyr2, pyr1, points2, pointsFB, points2.size(),window_size, level, 0, 0, term_criteria, CV_LKFLOW_INITIAL_GUESSES | CV_LKFLOW_PYR_A_READY | CV_LKFLOW_PYR_B_READY );}*/