角点检测

本文参考于:https://blog.csdn.net/linqianbi/article/details/78930239

                      https://blog.csdn.net/weixin_41695564/article/details/79979784

Harris角点检测算法步骤

1.利用sobel计算出xy方向上的梯度值(sobel对图像求取梯度已在上篇博文提到)

2.计算出Ix*Ix,Iy*Iy,Ix*Iy

3.利用高斯函数对Ix*Ix,Iy*Iy,Ix*Iy进行滤波

4.计算局部特征结果矩阵M的特征值和相应函数

5.将计算出响应函数的值进行非极大值抑制,滤除一些不是角点的点,同时要满足大于设定的阈值

对上述公式进行泰勒展开: 

令M代表蓝色圈的表达式,求取M的特征值

Harris角点计算角点响应的公式:R=det(M)-k(traceM)^2=λ1*λ2-k*(λ1+λ2)^2 

cornerEigenValsAndVecs()函数

#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("D:/2015project/picture/building.jpg");
	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;
}

Shi-Tomasi角点计算角点响应的公式:R=min(λ1,λ2)

cornerMinEigenVal()函数

#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("D:/2015project/picture/building.jpg");
	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);
}

 

这里解释为什么当λ1、λ2都很大的时候是角点,λ1远远大于λ2或相反的时候是边缘,当λ1、λ2都很小的时候是平坦区域。

 从上图看来:

对于Linear Edge,在x方向求导,x方向是有变化的,y方向基本是没有变化的,求导后检测出的是一条竖直的线。

对于Corner来说,x方向有变化,对应竖直的那条线,y方向上有变化,对应水平的那条线。

 从上图看来:

对于Corner,是Ix,Iy都是有变化的,所以两个轴上都有点

对于Linear Edge,Ix有变化,所以点都集中在Ix

对于平坦区域,Ix,Iy没什么变化,所以点都集中在圆心

所以,以此来画椭圆,可以清楚看出圆的轴长度λ1,λ2的大小,以此来反推到底是角点还边缘、平坦区域。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值