zoukankan      html  css  js  c++  java
  • OpenCV——Harris、Shi Tomas、自定义、亚像素角点检测

          在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints)、特征点(feature points) 被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析。如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。

    图像特征类型可以被分为如下三种:

    • <1>边缘
    • <2>角点 (感兴趣关键点)
    • <3>斑点(Blobs)(感兴趣区域)

    其中,角点是个很特殊的存在。他们在图像中可以轻易地定位,同时,他们在人造物体场景,比如门、窗、桌等出随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,,所以他们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。
    为什么角点是特殊的?

    • 因为角点是两个边缘的连接点,它代表了两个边缘变化的方向上的点。图像梯度有很高的变化。这种变化是可以用来帮助检测角点的。

    在当前的图像处理领域,角点检测算法可归纳为三类:

    <1>基于灰度图像的角点检测
    <2>基于二值图像的角点检测
    <3>基于轮廓曲线的角点检测
    而基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合三类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。

    关于角点的具体描述可以有几种:

         一阶导数(即灰度的梯度)的局部最大所对应的像素点;

         两条及两条以上边缘的交点;

         图像中梯度值和梯度方向的变化速率都很高的点;

         角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。

    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    
    using namespace cv;
    using namespace std;
    
    /// Global variables
    Mat src, src_gray,dst;
    int thresh = 200;
    int max_thresh = 255;
    
    char* source_window = "Source image";
    char* corners_window = "Corners detected";
    
    /// Function header
    void cornerHarris_demo(int, void*);
    void cvtColor_src(Mat &src, Mat &dst);
    
    /** @function main */
    int main(int argc, char** argv)
    {
    	/// Load source image and convert it to gray
    	src = imread("E:\VS2015Opencv\vs2015\project\picture\06.jpg");
    
    	cvtColor(src, src_gray, CV_BGR2GRAY);
    //  转换单通道
    	
    
    
    	/// Create a window and a trackbar
    	namedWindow(source_window, CV_WINDOW_AUTOSIZE);
    	createTrackbar("Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo);
    	imshow(source_window , src);
    
        cornerHarris_demo(0, 0);
    
    	waitKey(0);
    	return(0);
    }
    
    
    /** @function cornerHarris_demo */
    void cornerHarris_demo(int, void*)
    {
    
    	Mat dst, dst_norm, dst_norm_scaled;
    	dst = Mat::zeros(src.size(), CV_32FC1);
    
    	/// Detector parameters
    	int blockSize = 2;
    	int apertureSize = 3;
    	double k = 0.04;
    
    	/// Detecting corners
    	cornerHarris(src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT);
    
    	/// Normalizing
    	normalize(dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
    	convertScaleAbs(dst_norm, dst_norm_scaled);
    
    	/// Drawing a circle around corners
    	for (int j = 0; j < dst_norm.rows; j++)
    	{
    		for (int i = 0; i < dst_norm.cols; i++)
    		{
    			if ((int)dst_norm.at<float>(j, i) > thresh)
    			{
    				circle(dst_norm_scaled, Point(i, j), 5, Scalar(0), 2, 8, 0);
    			}
    		}
    	}
    	/// Showing the result
    	namedWindow(corners_window, CV_WINDOW_AUTOSIZE);
    	imshow(corners_window, dst_norm_scaled);
    }

    现在讲解一下其中原理:

    w(x,y)是窗口函数;w(x,y)也可以说是权值;在x方向和 v 方向也就是我们说的水平,竖直方向上移动;

    上图中红色的窗口可以往任意一个方向上移动;来检测是否是角;

    红色的窗口开始有图像的像素,移动后窗口内的图像像素变化;两者相减在平方;

    前面说过角在各个方向上梯度都很大,因此我们就用(红色的窗口开始有图像的像素,移动后窗口内的图像像素变化;两者相减在平方,无论窗口移动大哪个方向)原理来找最大变化;下面是数学推导;

    由于角点代表了图像像素梯度变化,我们将寻找这个”变化”。
    考虑到一个灰度图像 I. 划动窗口 w(x,y) (with displacements u 在x方向和 v 方向) I 计算像素灰度变化。     
    其中:
    w(x,y) is the window at position (x,y)
    I(x,y) is the intensity at (x,y)
    I(x+u,y+v) is the intensity at the moved window (x+u,y+v)


    为了寻找带角点的窗口,我们搜索像素灰度变化较大的窗口。于是, 我们期望最大化以下式子:

    使用 泰勒(Taylor)展开式:
    
    具体的可以看数学推导(如果还不懂,可以去学学泰勒展开,治理主要用的是一阶泰勒展开)
    式子可以展开为:
    
    将上式表达为矩阵的形式,表达式可以写为:
    
    表示为:
    
    因此我们有等式:
    
    每个窗口中计算得到一个值。这个值决定了这个窗口中是否包含了角点:
    
    其中:
    
    一个窗口,它的分数 R 大于一个特定值,这个窗口就可以被认为是”角点”
    上面E(u,v)可以看出,无论u,v取何值,我们需要E尽可能的大;

    线代理论中实对称矩阵可以正交相似对角化,如下面理论;可以看出E(u,v)是一个椭圆,若E尽可能的大,则λi要尽可能大;

    以下图中的λi是椭圆的半长轴(及开根号,椭圆公式);所以当λi两个都很大时,是角及corner,一个大一个小是边;具体看下图中英文标识。

    corner:在水平、竖直两个方向上变化均较大的点,即Ix、Iy都较大; 
     edge :仅在水平、或者仅在竖直方向有较大的点,即Ix和Iy只有其一较大 ;
      flat   : 在水平、竖直方向的变化量均较小的点,即Ix、Iy都较小;

    讲到这里,大家基本原理懂了,那如何判别两个特征根都很大呢?

    首先矩阵行列式的求法:,只有两个都大时,det(M)才会大;k一般去为0.05;

    也是两个都大时,其平方也会大;

    最重要的是求每个像素的M矩阵,如下面步骤:

    最后最重要的一点;M的求法是不会变得,不同的角点检测方法最后区别就在于R的求法,也就是如何判断特征根都很大的。

    可参考:思维之际Harris角点

    void cornerHarris_demo(int, void*)
    {
    
    	Mat dst, dst_norm, dst_norm_scaled;
    	dst = Mat::zeros(src.size(), CV_32FC1);
    
    	/// Detector parameters
    	int blockSize = 2;
    	int apertureSize = 3;
    	double k = 0.04;
    
    	/// Detecting corners
    	cornerHarris(src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT);
    
    	/// Normalizing
    	normalize(dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
    	convertScaleAbs(dst_norm, dst_norm_scaled);
    
    	/// Drawing a circle around corners
    	for (int j = 0; j < dst_norm.rows; j++)
    	{
    		for (int i = 0; i < dst_norm.cols; i++)
    		{
    			if ((int)dst_norm.at<float>(j, i) > thresh)
    			{
    				circle(dst_norm_scaled, Point(i, j), 5, Scalar(0), 2, 8, 0);
    			}
    		}
    	}
    

     

     Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将黑塞矩阵

     

     M的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。二者原理一模一样,只是计算角点量的方式不一样。

    #include "opencv2/opencv.hpp"
    #include <iostream>
    using namespace cv;
    using namespace std;
    
    #define WINDOW_NAME "【Shi-Tomasi角点检测】" 
    Mat g_srcImage, g_grayImage;
    int g_maxCornerNumber = 33;
    int g_maxTrackbarNumber = 500;
    RNG g_rng(12345);//初始化随机数生成器
    
    
    				 //-----------------------------【on_GoodFeaturesToTrack( )函数】----------------------------
    				 //          描述:响应滑动条移动消息的回调函数
    				 //----------------------------------------------------------------------------------------------
    void on_GoodFeaturesToTrack(int, void*)
    {
    	//【1】对变量小于等于1时的处理
    	if (g_maxCornerNumber <= 1) { g_maxCornerNumber = 1; }
    
    	//【2】Shi-Tomasi算法(goodFeaturesToTrack函数)的参数准备
    	vector<Point2f> corners;
    	double qualityLevel = 0.01;//角点检测可接受的最小特征值
    	double minDistance = 10;//角点之间的最小距离
    	int blockSize = 3;//计算导数自相关矩阵时指定的邻域范围
    	double k = 0.04;//权重系数
    	Mat copy = g_srcImage.clone();    //复制源图像到一个临时变量中,作为感兴趣区域
    
    									  //【3】进行Shi-Tomasi角点检测
    	goodFeaturesToTrack(g_grayImage,//输入图像
    		corners,//检测到的角点的输出向量
    		g_maxCornerNumber,//角点的最大数量
    		qualityLevel,//角点检测可接受的最小特征值
    		minDistance,//角点之间的最小距离
    		Mat(),//感兴趣区域
    		blockSize,//计算导数自相关矩阵时指定的邻域范围
    		false,//不使用Harris角点检测
    		k);//权重系数
    
    
    		   //【4】输出文字信息
    	cout << "	>此次检测到的角点数量为:" << corners.size() << endl;
    
    	//【5】绘制检测到的角点
    	int r = 4;
    	for (int i = 0; i < corners.size(); i++)
    	{
    		//以随机的颜色绘制出角点
    		circle(copy, corners[i], r, Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255),
    			g_rng.uniform(0, 255)), -1, 8, 0);
    	}
    
    	//【6】显示(更新)窗口
    	imshow(WINDOW_NAME, copy);
    }
    
    static void ShowHelpText()
    {
    	//输出欢迎信息和OpenCV版本
    	printf("
    
    			非常感谢购买《OpenCV3编程入门》一书!
    ");
    	printf("
    
    			此为本书OpenCV2版的第87个配套示例程序
    ");
    	printf("
    
    			   当前使用的OpenCV版本为:" CV_VERSION);
    	printf("
    
      ----------------------------------------------------------------------------
    ");
    	//输出一些帮助信息
    	printf("
    
    
    	欢迎来到【Shi-Tomasi角点检测】示例程序
    ");
    	printf("
    	请调整滑动条观察图像效果
    
    ");
    
    }
    
    void main()
    {
    	system("color 2F");
    	ShowHelpText();
    
    	//【1】载入源图像并将其转换为灰度图
    	g_srcImage = imread("E:\VS2015Opencv\vs2015\project\picture\06.jpg", 1);
    	cvtColor(g_srcImage, g_grayImage, CV_BGR2GRAY);
    
    	//【2】创建窗口和滑动条,并进行显示和回调函数初始化
    	namedWindow(WINDOW_NAME, CV_WINDOW_AUTOSIZE);
    	createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
    	imshow(WINDOW_NAME, g_srcImage);
    	on_GoodFeaturesToTrack(0, 0);
    
    	waitKey(0);
    }

    自定义角点检测器简介:

    • 基于Harris与Shi-Tomasi角点检测

    • 首先通过计算矩阵M得到lamda1和lamda2两个特征值根据他们得到角点响应值

    • 然后自己设置阈值实现计算出阈值得到有效响应值的角点设置

    cornerEigenValsAndVecs()函数在角点检测中计算扫描图像块的特征向量与特征值,其函数原型如下:

    C++: void cornerEigenValsAndVecs(
           InputArray src,       --单通道输入8位或浮点图像
           OutputArray dst,    --输出图像,同源图像或CV_32FC(6)
           int blockSize,         --邻域大小值
           int apertureSize,    --Sobel算子的参数
           int borderType=BORDER_DEFAULT --像素外插方法
    )//对应于Harris
    

    borderType:像素扩展的方法,因为在滤波处理的过程中会扩展图像边缘,每扩张一个边界像素,都需要计算出该像素点在原图中的位置,这个功能被提炼出来就变成了borderInterpolate()函数。该函数输入一个点坐标,返回他在原图中的坐标;可参考:borderInterpolate()函数。

     下面我们采用cornerEigenValsAndVecs()函数和minMaxLoc()函数,根据其原理来编写Harris角点检测的实现代码,示例如下:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <math.h>
    
    using namespace cv;
    using namespace std;
    
    // 定义全局变量
    const string harris_winName = "自定义角点检测";
    Mat src_img, gray_img;                       // src_img表示原图, gray_img表示灰度图
    Mat harris_dst_img, harris_response_img;     // harris_dst_img存储自相关矩阵M的特征值和特征向量,harris_response_img存储响应函数的结果
    
    double min_respense_value;			  // 响应函数的结果矩阵中的最小值
    double max_respense_value;			  // 响应函数的结果矩阵中的最大值
    
    int qualityValue = 30;
    int max_qualityValue = 100;              // 通过qualityValue/max_qualityValue的结果作为qualitylevel来计算阈值
    RNG  random_number_generator;             // 定义一个随机数发生器
    void self_defining_Harris_Demo(int, void*);      //TrackBar回调函数声明
    
    												 // 主函数
    int main()
    {
    	src_img = imread("E:\VS2015Opencv\vs2015\project\picture\06.jpg");
    	if (src_img.empty())
    	{
    		printf("could not load the image...
    ");
    		return -1;
    	}
    	namedWindow("原图", CV_WINDOW_AUTOSIZE);
    	imshow("原图", src_img);
    	cvtColor(src_img, gray_img, COLOR_BGR2GRAY);      //将彩色图转化为灰度图
    
    													  // 计算特征值
    	int blockSize = 3;
    	int ksize = 3;
    	double k = 0.04;
    	harris_dst_img = Mat::zeros(src_img.size(), CV_32FC(6));
    	// 目标图像harris_dst_img存储自相关矩阵M的特征值和特征向量,
    	// 并将它们以(λ1, λ2, x1, y1, x2, y2)的形式存储。其中λ1, λ2是M未经过排序的特征值;
    	// x1, y1是对应于λ1的特征向量;x2, y2是对应于λ2的特征向量。
    	// 因此目标矩阵为6通道,即 CV_32FC(6)的矩阵。
    
    	harris_response_img = Mat::zeros(src_img.size(), CV_32FC1);
    	// harris_response_img用来存储通过每个像素值所对应的自相关矩阵所计算得到的响应值
    
    	cornerEigenValsAndVecs(gray_img, harris_dst_img, blockSize, ksize, 4);
    	// 该函数用来计算每个像素值对应的自相关矩阵的特征值和特征向量
    
    	// 计算响应函数值
    	for (int row = 0; row < harris_dst_img.rows; ++row)
    	{
    		for (int col = 0; col < harris_dst_img.cols; ++col)
    		{
    			double eigenvalue1 = harris_dst_img.at<Vec6f>(row, col)[0];     // 获取特征值1
    			double eigenvalue2 = harris_dst_img.at<Vec6f>(row, col)[1];		// 获取特征值2
    			harris_response_img.at<float>(row, col) = eigenvalue1* eigenvalue2 - k*pow((eigenvalue1 + eigenvalue2), 2);
    			// 通过响应公式R=λ1*λ2 - k*(λ1+λ2)*(λ1+λ2)来计算每个像素对应的响应值
    		}
    	}
    	minMaxLoc(harris_response_img, &min_respense_value, &max_respense_value, 0, 0, Mat());   // 寻找响应矩阵中的最小值和最大值
    	namedWindow(harris_winName, CV_WINDOW_AUTOSIZE);
    	createTrackbar("Quality Value:", harris_winName, &qualityValue, max_qualityValue, self_defining_Harris_Demo);    //创建TrackBar
    	self_defining_Harris_Demo(0, 0);
    
    	waitKey(0);
    	return 0;
    }
    
    
    //  回调函数实现
    void self_defining_Harris_Demo(int, void*)
    {
    	if (qualityValue < 10)
    	{
    		qualityValue = 10;       // 控制qualitylevel的下限值
    	}
    	Mat result_img = src_img.clone();    // 输出图像
    	float threshold_value = min_respense_value + (((double)qualityValue) / max_qualityValue)*(max_respense_value - min_respense_value);
    	for (int row = 0; row <result_img.rows; row++)
    	{
    		for (int col = 0; col < result_img.cols; col++)
    		{
    			float resp_value = harris_response_img.at<float>(row, col);
    			if (resp_value > threshold_value)
    			{
    				circle(result_img, Point(col, row), 2, Scalar(random_number_generator.uniform(0, 255),
    					random_number_generator.uniform(0, 255), random_number_generator.uniform(0, 255)), 2, 8, 0);
    			}
    		}
    	}
    	imshow(harris_winName, result_img);
    }

    erMinEigenVal()函数在角点检测中计算梯度矩阵的最小特征值,其函数原型如下

    C++: void cornerMinEigenVal(
           InputArray src,     --单通道输入8位或浮点图像
           OutputArray dst,  --图像存储的最小特征值。类型为CV_32FC1
           int blockSize,       --邻域大小值
           int apertureSize=3,   --Sobel算子的参数
           int borderType=BORDER_DEFAULT  --像素外插方法
    }//对应Shi-Tomasi
    

    函数参数

    函数参数说明中除了dst必须为CV_32FC1类型以外,其它与cornerEigenValsAndVecs()函数的一致。

    函数功能

    功能与cornerEigenValsAndVecs()函数相似,但是它只计算导数协方差矩阵的最小特征值,

    按照cornerEigenValsAndVecs()函数给定的特征值λ1, λ2来说就是min(λ1, λ2)。


    采用cornerMinEigenVal()函数和minMaxLoc()函数结合来模拟Shi-Tomasi角点检测的代码示例如下;(参考:定制化创建角点检测子

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <math.h>
    
    using namespace cv;
    using namespace std;
    
    // 定义全局变量
    const string ShiTomasi_winName = "Custom Shi-Tomasi Corners Detector";
    Mat src_img, gray_img;                  // src_img表示原图, gray_img表示灰度图
    Mat ShiTomasi_dst_img;                  // ShiTomasi_dst_img用来存储每个像素对应的自相关矩阵的最小特征值
    double min_ShiTomasi_value;            // 最小特征矩阵中的最小值
    double max_ShiTomasi_value;		      // 最小特征矩阵中的最大值
    int ShiTomasi_qualityValue = 30;
    int max_qualityValue = 100;
    RNG  random_number_generator;                        // 定义一个随机数发生器
    void self_defining_ShiTomasi_Demo(int, void*);      //TrackBar回调函数声明
    
    													// 主函数
    int main()
    {
    	src_img = imread("E:\VS2015Opencv\vs2015\project\picture\06.jpg");
    	if (src_img.empty())
    	{
    		printf("could not load the image...
    ");
    		return -1;
    	}
    	namedWindow("原图", CV_WINDOW_AUTOSIZE);
    	imshow("原图", src_img);
    	cvtColor(src_img, gray_img, COLOR_BGR2GRAY);      //将彩色图转化为灰度图
    
    													  // 计算特征值
    	int blockSize = 3;
    	int ksize = 3;
    
    	// 计算最小特征值
    	ShiTomasi_dst_img = Mat::zeros(src_img.size(), CV_32FC1);
    	cornerMinEigenVal(gray_img, ShiTomasi_dst_img, blockSize, ksize, 4);            // 计算每个像素对应的自相关矩阵的最小特征值
    	minMaxLoc(ShiTomasi_dst_img, &min_ShiTomasi_value, &max_ShiTomasi_value, 0, 0, Mat());           //计算最小最大值
    	namedWindow(ShiTomasi_winName, CV_WINDOW_AUTOSIZE);
    	createTrackbar("Quality:", ShiTomasi_winName, &ShiTomasi_qualityValue, max_qualityValue, self_defining_ShiTomasi_Demo);
    	self_defining_ShiTomasi_Demo(0, 0);
    
    	waitKey(0);
    	return 0;
    }
    
    
    //  回调函数实现
    void self_defining_ShiTomasi_Demo(int, void*)
    {
    	if (ShiTomasi_qualityValue < 10)
    	{
    		ShiTomasi_qualityValue = 10;       // 控制qualitylevel的下限值
    	}
    	Mat result_img = src_img.clone();    // 输出图像
    	float threshold_value = min_ShiTomasi_value + (((double)ShiTomasi_qualityValue) / max_qualityValue)*(max_ShiTomasi_value - min_ShiTomasi_value);
    	for (int row = 0; row <result_img.rows; row++)
    	{
    		for (int col = 0; col < result_img.cols; col++)
    		{
    			float resp_value = ShiTomasi_dst_img.at<float>(row, col);
    			if (resp_value > threshold_value)
    			{
    				circle(result_img, Point(col, row), 2, Scalar(random_number_generator.uniform(0, 255),
    					random_number_generator.uniform(0, 255), random_number_generator.uniform(0, 255)), 2, 8, 0);
    			}
    		}
    	}
    	imshow(ShiTomasi_winName, result_img);
    }
    

     

    亚像素角点检测需要先运行常规的角点检测,得到整数表示的角点坐标。然后算法对每个角点做细化,得到实数表示的角点坐标
    
    第一步:goodFeaturesToTrack()    //检测角点
    
    第二步:TermCriteria()            //设置迭代算法的终止条件
    TermCriteria(int type,int max_iter, double epsilon);
    参数
    type:终止条件类型;
    CV_TERMCRIT_ITER--max_iter达到最大值后停止算法;
    CV_TERMCRIT_EPS--当算法依赖的精确度低于epsilon后,停止算法;
    CV_TERMCRIT_ITER+CV_TERMCRIT_EPS--当max_iter达到最大值或算法依赖的精确度低于epsilon任一个满足时,停止算法;
    max_iter:最大迭代次数;
    epsilon:要求精度;
    
    第三步:cornerSubPix()         //细化角点位置;
    void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria);
    参数:
    image:输入图像;
    corners:初始化输入角点的坐标,为输出提供细化的坐标;
    winSize:搜索窗口的边长的一半;
    zeroZone:搜索区域中间的死区的一半大小,对它在下边的求和公式不计算,有时候它用来避免可能的自相关矩阵的奇异性,(-1,-1)用来表明这里没有这样的规模;
    criteria:角点细化的迭代过程的终止条件;
    
     可参考:亚像素角点检测

    相关代码和结果

    #include <opencv2/opencv.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    //描述:定义一些辅助宏
    
    #define WINDOW_NAME "【亚像素角点检测】"
    
    //全局变量声明
    Mat g_srcImage;
    Mat g_grayImage;
    int g_maxCornerNumber = 33;
    int g_maxTrackbarNumber = 500;
    RNG g_rng(12345);
    
    //回调函数
    void on_GoodFeaturesToTrack(int, void *)
    {
    	if (g_maxCornerNumber <= 1)
    	{
    		g_maxCornerNumber = 1;
    	}
    	//shi-tomasi算法
    	vector<Point2f> corner;
    	double qualityLevel = 0.01;     //角点检测可接收的最小特征
    	double minDistance = 10;        //角点之间的最小的距离
    	int blockSize = 3;              //计算导数自相关矩阵时指定的邻域范围
    	double k = 0.04;                //权重系数
    	Mat copy = g_srcImage.clone();
    	//进行角点检测
    	goodFeaturesToTrack(g_grayImage, corner, g_maxCornerNumber, qualityLevel, minDistance, Mat(), blockSize, false, k);
    	//输出文字信息
    	cout << ">此次检测到的角点数量为:" << corner.size() << endl;
    	//绘制检测到的角点
    	Size winSize = Size(5, 5);
    	Size zeroZone = Size(-1, -1);
    	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
    	cornerSubPix(g_grayImage, corner, winSize, zeroZone, criteria);
    	for (int i = 0; i < corner.size(); i++)
    	{
    		cout << "	>>精度角点坐标[" << i << "](" << corner[i].x << "," << corner[i].y << ")" << endl;
    	}
    	int r = 4;
    	for (unsigned int i = 0; i < corner.size(); i++)
    	{
    		circle(copy, corner[i], r, Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)), -1, 8, 0);
    	}
    	//显示更新窗口
    	imshow(WINDOW_NAME, copy);
    }
    int main()
    {
    	//载入原始图像
    	g_srcImage = imread("E:\VS2015Opencv\vs2015\project\picture\06.jpg", 1);
    	cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
    
    	//创建窗口和滑动条
    	namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
    	createTrackbar("最大角点数:", WINDOW_NAME, &g_maxCornerNumber, g_maxTrackbarNumber, on_GoodFeaturesToTrack);
    	imshow(WINDOW_NAME, g_srcImage);
    	on_GoodFeaturesToTrack(0, 0);
    	waitKey(0);
    	return 0;
    }
    

      

    原文链接:https://blog.csdn.net/poem_qianmo/article/details/29356187

    代码参考:https://blog.csdn.net/poem_qianmo/article/category/1923021和http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.html#good-features-to-track

    参考:OpenCV学习笔记(十一)——自定

     harris corner detection(角点检测)原理视频

  • 相关阅读:
    C#如何用OpenFileDialog控件打开图片显示到PictureBox这个控件
    C# winform 禁止窗体移动
    linux 硬链接和软链接(转)
    linux 源码编译(转)
    linux 压缩与解压缩
    硬盘分区(来自百度百科)
    arp:地址解析协议(Address Resolution Protocol)(来自维基百科)
    c++学习笔记(1)
    ProbS CF matlab源代码(二分系统)(原创作品,转载注明出处,谢谢!)
    [eclipse]UML之AmaterasUML 插件
  • 原文地址:https://www.cnblogs.com/fcfc940503/p/11332104.html
Copyright © 2011-2022 走看看