OpenCV-特征提取与检测(03、自定义角点检测器)

版权声明:本文由 Micheal 超 博客 创作,未经博主允许不得转载。 https://blog.csdn.net/qq_42887760/article/details/86632150

前言:

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

自定义角点检测器

  • 基于 Harris 与 Shi-Tomasi 角点检测
  • 首先通过计算矩阵 M 得到 λ λ 1 λ λ 2 两个特征值根据他们得到角点响应值
  • 然后自己设置阈值实现计算出阈值得到有效响应值的角点位置

相关API说明

1. cornerEigenValsAndVecs()函数
cornerEigenValsAndVecs()函数用来求解输入图像矩阵的特征向量与特征值,其函数原型如下:
在这里插入图片描述

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

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

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

在这里插入图片描述

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

3.minMaxLoc()函数介绍

1. cornerEigenValsAndVecs()函数、自定义角点检测器_harris

程序代码:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;

const char* harris_win = "Custom Harris Corners Detector";
Mat src, gray_src;
Mat harris_dst, harrisRspImg;
double harris_min_rsp;// harris 角度响应最小值
double harris_max_rsp;// harris 角度响应最大值
// quality level
int qualityLevel = 30;
int max_count = 100;

void CustomHarris_Demo(int, void*);

int main(int argc, char** argv) {
	src = imread("E:/Experiment/OpenCV/Pictures/cornerHarrisTest.jpg");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	namedWindow(harris_win, CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	// 计算特征值 eigon values λ1 λ2
	int blockSize = 3;// blockSize取2或取3,会导致计算出来的角度响应的最大最小值不一样,下面的阈值需要适当调整
	int ksize = 3;
	double k = 0.04;
	harris_dst = Mat::zeros(src.size(), CV_32FC(6));// 深度为 CV_32F 通道数为6 ,通道数不一定要6 大于等于2就行
	harrisRspImg = Mat::zeros(src.size(), CV_32FC1);
	cornerEigenValsAndVecs(gray_src, harris_dst, blockSize, ksize, 4);// 计算 λ1 λ2
	// 计算角度响应
	for (int row = 0; row < harris_dst.rows; row++) {
		for (int col = 0; col < harris_dst.cols; col++) {
			double lambda1 =harris_dst.at<Vec6f>(row, col)[0]; // λ1
			double lambda2 = harris_dst.at<Vec6f>(row, col)[1]; // λ2
			// harris角度响应计算,公式 R = det(M) - k*(trace(M))^2,  det(M)=λ1*λ2, trace(M)=λ1+λ2
			harrisRspImg.at<float>(row, col) = lambda1*lambda2 - k*pow((lambda1 + lambda2), 2);
		}
	}
	//寻找harrisRspImg中角度响应最大值,最小值,及它们所在的位置,位置参数传0表示不管
	minMaxLoc(harrisRspImg, &harris_min_rsp, &harris_max_rsp, 0, 0, Mat());
	printf("harris_min_rsp=%f, harris_max_rsp=%f\n", harris_min_rsp, harris_max_rsp); //harris_min_rsp=-0.016178, harris_max_rsp=0.047225
	
	createTrackbar("harris thresh:", harris_win, &qualityLevel, max_count, CustomHarris_Demo);
	CustomHarris_Demo(0, 0);

	waitKey(0);
	return 0;
}

void CustomHarris_Demo(int, void*) {// 自定义harris角点检测,与harris函数计算的结果差不多
	if (qualityLevel < 10) {
		qualityLevel = 10;
	}
	Mat resultImg = src.clone(); // 完全拷贝
	
	//定义阈值, 以 thresh_v_harris / thresh_max_harris 的比例显示角点
	float t = harris_min_rsp + (((double)qualityLevel) / max_count)*(harris_max_rsp - harris_min_rsp);
	
	for (int row = 0; row < src.rows; row++) {
		for (int col = 0; col < src.cols; col++) {
			float v = harrisRspImg.at<float>(row, col);
			if (v > t) {
				// 角度响应大于阈值,在当前位置显示出来,当角度响应阈值设的很低时,因为绘制的多,所以绘制速度会很慢
				circle(resultImg, Point(col, row), 2, Scalar(0, 0, 255), 2, 8, 0);
			}
		}
	}

	imshow(harris_win, resultImg);
}

运行截图:

在这里插入图片描述

2. cornerMinEigenVal()函数、自定义角点检测器_ShiTomasi

程序代码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;

const char* shitomasi_win = "Custom Shi-Tomasi Corners Detector";
Mat src, gray_src, shiTomasiRsp;
double shitomasi_min_rsp;// tomasi 角度响应最小值
double shitomasi_max_rsp;// tomasi 角度响应最大值
int sm_qualitylevel = 30;
int max_count = 100;

void CustomShiTomasi_Demo(int, void*);

int main(int argc, char** argv) {
	src = imread("E:/Experiment/OpenCV/Pictures/ShiTomasiTest.jpg");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	namedWindow(shitomasi_win, CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	cvtColor(src, gray_src, COLOR_BGR2GRAY);

	// 计算特征值
	int blockSize = 3;
	int ksize = 3;
	double k = 0.04;
	shiTomasiRsp = Mat::zeros(src.size(), CV_32FC1);
	
	//计算最小特征值,即ShiTomasi角度响应值 R = min(λ1, λ2)
	cornerMinEigenVal(gray_src, shiTomasiRsp, blockSize, ksize, BORDER_DEFAULT);
	minMaxLoc(shiTomasiRsp, &shitomasi_min_rsp, &shitomasi_max_rsp, 0, 0, Mat());//寻找tomasiRspImg中角度响应最大值,最小值,及它们所在的位置,位置参数传0表示不管
	printf("tomasi_min_rsp=%f, tomasi_max_rsp=%f\n", shitomasi_min_rsp, shitomasi_max_rsp);//tomasi_min_rsp=-0.000000, tomasi_max_rsp=0.192643
	
	createTrackbar("Quality:", shitomasi_win, &sm_qualitylevel, max_count, CustomShiTomasi_Demo);
	CustomShiTomasi_Demo(0, 0);

	waitKey(0);
	return 0;
}

void CustomShiTomasi_Demo(int, void*) {
	if (sm_qualitylevel < 20) {
		sm_qualitylevel = 20;
	}

	Mat resultImg = src.clone();
	//定义阈值, 以 thresh_v_tomasi / thresh_max_tomasi 的比例显示角点
	float t = shitomasi_min_rsp + (((double)sm_qualitylevel) / max_count)*(shitomasi_max_rsp - shitomasi_min_rsp);
	for (int row = 0; row < src.rows; row++) {
		for (int col = 0; col < src.cols; col++) {
			float v = shiTomasiRsp.at<float>(row, col);// shiTomasi角点检测出的角度响应值有很多为0,所以减少了circle绘制,所以程序速度比harris要快
			if (v > t) {
				circle(resultImg, Point(col, row), 2, Scalar(0, 0, 255), 2, 8, 0);
			}
		}
	}
	imshow(shitomasi_win, resultImg);
}

运行截图

在这里插入图片描述

参考博客

  1. https://blog.csdn.net/weixin_41695564/article/details/79979784
  2. https://www.cnblogs.com/long5683/p/9692969.html
  3. https://blog.csdn.net/huanghuangjin/article/details/81263320
  4. https://blog.csdn.net/huanghuangjin/article/details/81263778

猜你喜欢

转载自blog.csdn.net/qq_42887760/article/details/86632150
今日推荐