OpenCV学习笔记(十一)——自定义角点检测函数

前言:

        我们知道,在OpenCV中已经为我们提供了相关函数——cornerHarris() 函数和 goodFeaturesToTrack()函数,来实现Harris角点检测和Shi-Tomasi角点检测,除此之外,其实我们也可以根据算法的原理和需求来制作角点检测的函数。例如:使用cornerEigenValsAndVecs()函数和minMaxLoc()函数结合来模拟Harris角点检测,或者使用cornerMinEigenVal()函数和minMaxLoc()函数结合来模拟Shi-Tomasi角点检测,最后特征点选取的判断条件要根据实际情况进行选择。

一、相关函数介绍

1.1  cornerEigenValsAndVecs()函数

         cornerEigenValsAndVecs()函数用来求解输入图像矩阵的特征向量与特征值,其函数原型如下:


其函数参数解释如下:

       src:输入图像矩阵,即单通道8位或者浮点类型的图像。

        dst:输出矩阵,即用来存储结果的图像,大小与输入图像一致并且为CV_32FC(6)类型。计算自相关矩阵M的特征值和特征向量,并将它们以(λ1, λ2, x1, y1, x2,y2)的形式存储在目标图像dst中。其中λ1, λ2是M未经过排序的特征值;x1, y1是对应于λ1的特征向量;x2, y2是对应于λ2的特征向量。因此是6通道的矩阵。

        blockSize:邻域大小。

        ksize:Sobel算子当中的核大小,只能取1、3、5、7。

        borderType:像素扩展的方法。


1.2 cornerMinEigenVal()函数

       功能与cornerEigenValsAndVecs()函数相似,但是它只计算导数协方差矩阵(即自相关矩阵)的最小特征值,其函数原型如下:


其函数参数解释如下:

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

        src:输入图像矩阵,即单通道8位或者浮点类型的图像。

        dst:输出矩阵,即用来存储结果的图像,大小与输入图像一致并且为CV_32FC(1)类型。

        blockSize:邻域大小。

        ksize:Sobel算子当中的核大小,只能取1、3、5、7。

        borderType:像素扩展的方法。


1.3 minMaxLoc()函数
       minMaxLoc()函数寻找输入矩阵(在c++中就是Mat对象)中的最小值和最大值,同时可以其相应值的位置(Point对象)。其函数声明如下:

其参数解释如下:

        src:输入矩阵。

        minVal:找到到最小值,double *类型。

        maxVal:找到到最大值,double *类型。

        minLoc、maxLoc:都是Point *类型的变量,用来存所找到的最大值和最小值的坐标,即Point坐标。

        mask:如果是对整个图像区域搜索,指定为Mat()即可。

如果参数maxVal、minLoc、maxLoc不需要,只需指定为NULL或者0即可。


二、自定义Harris角点检测函数

         下面我们采用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("test11.png");
	if (src_img.empty())
	{
		printf("could not load the image...\n");
		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);
}




运行程序,如下所示:


拖动TrackBar,得到不同阈值下的检测结果:




三、自定义Shi-Tomasi角点检测函数

      接下里我们采用cornerMinEigenVal()函数和minMaxLoc()函数,结合Shi-Tomasi算法的原理来编写实现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("test11.png");
	if (src_img.empty())
	{
		printf("could not load the image...\n");
		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);
}

运行程序,如下所示:


拖动TrackBar,查看不同阈值条件下的shi-Tomasi角点检测结果:



阅读更多

没有更多推荐了,返回首页