OpenCV学习笔记(1)——LKdemos(KLT)解析

版权声明:转载请注明出处(作者:Ephemeroptera) https://blog.csdn.net/Ephemeroptera/article/details/83960073

打开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;
}

猜你喜欢

转载自blog.csdn.net/Ephemeroptera/article/details/83960073
今日推荐