OpenCV學習筆記(1)——LKdemos(KLT)解析
阿新 • • 發佈:2018-12-20
開啟lkdemo.cpp
#include "opencv2/video/tracking.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/videoio.hpp" #include "opencv2/highgui.hpp" #include <iostream> #include <ctype.h> using namespace cv; using namespace std; static void help() { // print a welcome message, and the OpenCV version cout << "\nThis is a demo of Lukas-Kanade optical flow lkdemo(),\n" "Using OpenCV version " << CV_VERSION << endl; cout << "\nIt uses camera by default, but you can provide a path to video as an argument.\n"; cout << "\nHot keys: \n" "\tESC - quit the program\n" "\tr - auto-initialize tracking\n" "\tc - delete all the points\n" "\tn - switch the \"night\" mode on/off\n" "To add/remove a feature point click it\n" << endl; } Point2f point; bool addRemovePt = false; //是否新增啊移動點 static void onMouse( int event, int x, int y, int /*flags*/, void* /*param*/ )//滑鼠回撥函式 { if( event == EVENT_LBUTTONDOWN ) { point = Point2f((float)x, (float)y); addRemovePt = true; } } int main( int argc, char** argv ) { VideoCapture cap; TermCriteria termcrit(TermCriteria::COUNT|TermCriteria::EPS,20,0.03); //終止條件:TermCriteria(int type, int maxCount, double epsilon); //int type; //!< the type of termination criteria: COUNT, EPS or COUNT + EPS // COUNT=1, //!< the maximum number of iterations or elements to compute 最大迭代次數或元素數量 // MAX_ITER=COUNT, //!< ditto 同上 // EPS=2 //!< the desired accuracy or change in parameters at which the iterative algorithm stops //精度或引數變化 // int maxCount; //!< the maximum number of iterations/elements //最大迭代次數:20 // double epsilon; //!< the desired accuracy //或者精度波動在0.03 Size subPixWinSize(10,10), winSize(31,31); const int MAX_COUNT = 500; bool needToInit = false; // bool nightMode = false; help(); cv::CommandLineParser parser(argc, argv, "{@input|0|}");//命令列解析器 string input = parser.get<string>("@input");//獲取當前字串存入input if( input.size() == 1 && isdigit(input[0]) )//如果輸入的是一個數字 cap.open(input[0] - '0'); else cap.open(input); if( !cap.isOpened() ) //如果未能成功開啟捕捉 { cout << "Could not initialize capturing...\n"; return 0; } namedWindow( "LK Demo", 1 );//定義視窗 setMouseCallback( "LK Demo", onMouse, 0 );//(1)視窗控制代碼(2)滑鼠事件(3)資料地址 Mat gray, prevGray, image, frame; vector<Point2f> points[2]; for(;;) { cap >> frame; if( frame.empty() ) break; frame.copyTo(image);//複製矩陣 cvtColor(image, gray, COLOR_BGR2GRAY);//灰度化 if( nightMode ) image = Scalar::all(0); if( needToInit ) { // automatic initialization goodFeaturesToTrack(gray, points[1], MAX_COUNT, 0.01, 10, Mat(), 3, 3, 0, 0.04); //The function can be used to initialize a point-based tracker of an object. //初始化基於點的追蹤器 // ①@param image Input 8-bit or floating-point 32-bit, single-channel image. <gray> // ②@param corners Output vector of detected corners. <points[1]> // ③@param maxCorners Maximum number of corners to return. If there are more corners than are found, // the strongest of them is returned. `maxCorners <= 0` implies that no limit on the maximum is set // and all detected corners are returned. <MAX_COUNT> 最多返回500個點 // ④@param qualityLevel Parameter characterizing the minimal accepted quality of image corners. The // parameter value is multiplied by the best corner quality measure, which is the minimal eigenvalue // (see #cornerMinEigenVal) or the Harris function response(see #cornerHarris).The corners with the // quality measure less than the product are rejected.For example, if the best corner has the // quality measure = 1500, and the qualityLevel = 0.01, then all the corners with the quality measure // less than 15 are rejected. <0.01> 質量水平低於0.01*BestLevel 的點將被去除 // ⑤@param minDistance Minimum possible Euclidean distance between the returned corners. <10> 角點之間最小歐氏距離 // ⑥@param mask Optional region of interest.If the image is not empty(it needs to have the type // CV_8UC1 and the same size as image), it specifies the region in which the corners are detected.<default::noArray> 指定ROI // ⑦@param blockSize Size of an average block for computing a derivative covariation matrix over each // pixel neighborhood.See cornerEigenValsAndVecs or #cornerMinEigenVal.. <3> 演算法選取的塊大小 // ⑧@param gradientSize <3> 梯度塊 // ⑨@param useHarrisDetector Parameter indicating whether to use a Harris detector (see #cornerHarris) <0> 不使用哈爾檢測 // ⑩param k Free parameter of the Harris detector. <0.04>哈爾探測器Free引數 cornerSubPix(gray, points[1], subPixWinSize, Size(-1,-1), termcrit); // 前面已經提及,cv::goodFeaturesToTrack()提取到的角點只能達到畫素級別, //在很多情況下並不能滿足實際的需求,這時,我們則需要使用cv::cornerSubPix()對檢測到的角點作進一步的優化計算, //可使角點的精度達到亞畫素級別。 //第一個引數是輸入影象,和cv::goodFeaturesToTrack()中的輸入影象是同一個影象。<gray> //第二個引數是檢測到的角點,即是輸入也是輸出。 <point[1]> //第三個引數是計算亞畫素角點時考慮的區域的大小,大小為NXN; N = (winSize * 2 + 1)。 <subPixWinSize> //第四個引數作用類似於winSize,但是總是具有較小的範圍,通常忽略(即Size(-1, -1)) <default> //第五個引數用於表示計算亞畫素時停止迭代的標準,可選的值有cv::TermCriteria::MAX_ITER 、cv::TermCriteria::EPS(可以是兩者其一,或兩者均選),前者表示迭代次數達到了最大次數時停止,後者表示角點位置變化的最小值已經達到最小時停止迭代。二者均使用cv::TermCriteria()建構函式進行指定。 addRemovePt = false; } else if( !points[0].empty() )//有圖片進來 { vector<uchar> status; vector<float> err; if(prevGray.empty()) gray.copyTo(prevGray); calcOpticalFlowPyrLK(prevGray, gray, points[0], points[1], status, err, winSize, 3, termcrit, 0, 0.001); // brief Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids. //@param prevImg first 8-bit input image or pyramid constructed by buildOpticalFlowPyramid. 第一幅8位輸入影象 或 由buildOpticalFlowPyramid()構造的金字塔。 //@param nextImg second input image or pyramid of the same size and the same type as prevImg.第二幅與preImg大小和型別相同的輸入影象或金字塔 // @param prevPts vector of 2D points for which the flow needs to be found; point coordinates must be // single - precision floating - point numbers. 光流法需要找到的二維點的vector。點座標必須是單精度浮點數。 // @param nextPts output vector of 2D points(with single - precision floating - point coordinates) // containing the calculated new positions of input features in the second image; when // OPTFLOW_USE_INITIAL_FLOW flag is passed, the vector must have the same size as in the input.包含輸入特徵在第二幅影象中計算出的新位置的二維點(單精度浮點座標)的輸出vector。 // 當使用OPTFLOW_USE_INITIAL_FLOW 標誌時,nextPts的vector必須與input的大小相同。 // @param status output status vector(of unsigned chars); each element of the vector is set to 1 if // the flow for the corresponding features has been found, otherwise, it is set to 0. 輸出狀態vector(型別:unsigned chars)。如果找到了對應特徵的流,則將向量的每個元素設定為1;否則,置0。 // @param err output vector of errors; each element of the vector is set to an error for the // corresponding feature, type of the error measure can be set in flags parameter; if the flow wasn't // found then the error is not defined(use the status parameter to find such cases).誤差輸出vector。vector的每個元素被設定為對應特徵的誤差,可以在flags引數中設定誤差度量的型別; // 如果沒有找到流,則未定義誤差(使用status引數來查詢此類情況)。 // @param winSize size of the search window at each pyramid level. 每級金字塔的搜尋視窗大小。 // @param maxLevel 0 - based maximal pyramid level number; if set to 0, pyramids are not used(single // level), if set to 1, two levels are used, and so on; if pyramids are passed to input then // algorithm will use as many levels as pyramids have but no more than maxLevel.基於最大金字塔層次數。如果設定為0,則不使用金字塔(單級);如果設定為1,則使用兩個級別,等等。 // 如果金字塔被傳遞到input,那麼演算法使用的級別與金字塔同級別但不大於MaxLevel。 // @param criteria parameter, specifying the termination criteria of the iterative search algorithm // (after the specified maximum number of iterations criteria.maxCount or when the search window // moves by less than criteria.epsilon. 指定迭代搜尋演算法的終止準則(在指定的最大迭代次數標準值(criteria.maxCount)之後,或者當搜尋視窗移動小於criteria.epsilon。) // @param flags operation flags : //-**OPTFLOW_USE_INITIAL_FLOW** uses initial estimations, stored in nextPts; if the flag is // not set, then prevPts is copied to nextPts and is considered the initial estimate. // - **OPTFLOW_LK_GET_MIN_EIGENVALS** use minimum eigen values as an error measure(see // minEigThreshold description); if the flag is not set, then L1 distance between patches // around the original and a moved point, divided by number of pixels in a window, is used as a // error measure.操作標誌,可選引數: // OPTFLOW_USE_INITIAL_FLOW:使用初始估計,儲存在nextPts中;如果未設定標誌,則將prevPts複製到nextPts並被視為初始估計。 // OPTFLOW_LK_GET_MIN_EIGENVALS:使用最小本徵值作為誤差度量(見minEigThreshold描述);如果未設定標誌,則將原始周圍的一小部分和移動的點之間的 L1 距離除以視窗中的畫素數,作為誤差度量。 // @param minEigThreshold the algorithm calculates the minimum eigen value of a 2x2 normal matrix of // optical flow equations(this matrix is called a spatial gradient matrix in @cite Bouguet00), divided // by number of pixels in a window; if this value is less than minEigThreshold, then a corresponding // feature is filtered out and its flow is not processed, so it allows to remove bad points and get a // performance boost.演算法所計算的光流方程的2x2標準矩陣的最小本徵值(該矩陣稱為[Bouguet00]中的空間梯度矩陣)÷ 視窗中的畫素數。如果該值小於MinEigThreshold,則過濾掉相應的特徵, // 相應的流也不進行處理。因此可以移除不好的點並提升效能。 size_t i, k; for( i = k = 0; i < points[1].size(); i++ ) { if( addRemovePt ) { if( norm(point - points[1][i]) <= 5 )//如果設定新點太小就算了 { addRemovePt = false; continue; } } if( !status[i] )//如果沒找到對應點也算了 continue; points[1][k++] = points[1][i];//提取存在聯絡的點 circle( image, points[1][i], 3, Scalar(0,255,0), -1, 8); /* @brief Draws a circle. The function cv::circle draws a simple or filled circle with a given center and radius. @param img Image where the circle is drawn.//輸入影象 @param center Center of the circle. //圓心 @param radius Radius of the circle.//半徑 @param color Circle color. //顏色 @param thickness Thickness of the circle outline, if positive. Negative values, like #FILLED, mean that a filled circle is to be drawn.//正數:輪廓線條,負數:填充 @param lineType Type of the circle boundary. See #LineTypes//線條型別 @param shift Number of fractional bits in the coordinates of the center and in the radius value.//小數位數 */ } points[1].resize(k);//去除多餘空間 } if( addRemovePt && points[1].size() < (size_t)MAX_COUNT )//如果要新增點即addRemovePt==true { vector<Point2f> tmp; tmp.push_back(point);//存入手動點 cornerSubPix( gray, tmp, winSize, Size(-1,-1), termcrit);//合格檢測一下 points[1].push_back(tmp[0]); //不錯的話收納 addRemovePt = false; } needToInit = false; imshow("LK Demo", image); char c = (char)waitKey(10); //獲取鍵盤上字元 if( c == 27 ) break; switch( c ) { case 'r': needToInit = true; break; case 'c': //清理掉 points[0].clear(); points[1].clear(); break; case 'n': //自動生成點 nightMode = !nightMode; break; } std::swap(points[1], points[0]);//輪替向量 cv::swap(prevGray, gray); //輪替圖片 } return 0; }