版权声明:转载请注明出处(作者: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;
}