1. 程式人生 > >OpenCV學習筆記(1)——LKdemos(KLT)解析

OpenCV學習筆記(1)——LKdemos(KLT)解析

開啟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;
}