OpenCV学习21_ 角点检测

在这里插入图片描述

角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模和目标识别等领域中,也称为特征点检测。

角点通常被定义为两条边的交点,更严格地说法是,角点的局部邻域应该具有两个不同区域的不同方向的边界。而实际应用中,大多数所谓的角点检测方法检测的是拥有特定特征的图像点,而不仅仅是“角点”。这些特征点在图像中有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、某些梯度特征等。

一、harris角点检测

harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高。但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象。

1.1 实现Harris角点检测: cornerHarris()函数

cornerHarris函数用于在OpenCV中运行Harris角点检测算子来进行角点检测。和cornerMinEigenVal()以及cornerEigenValsAndVecs()函数类似, cornerHarris函数对于每一个像素(xy)在blockSize × blockSize邻域内,计算2x2梯度的协方差矩阵M(x,p),就可以找出输出图中的局部最大值,即找出了角点。
其函数原型和参数解析如下。

C++: void cornerHarris(InputArray src,outputArray dst, int blocksize, int ksize, double k, intborderType=BORDER_DEFAULT )
  • 第一个参数, InputArray类型的sre,输入图像,即源图像,填Mat类的对象即可,且须为单通道8位或者浮点型图像。
  • 第二个参数, OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型
  • 第三个参数, int类型的blockSize,表示邻域的大小,更多详细信息在cornerEigen ValsAndVecs)中有讲到。
  • 第四个参数, int类型的ksize,表示Sobel)算子的孔径大小。
  • 第五个参数, double类型的k, Harris参数。第六个参数, int类型的borderType,图像像素的边界模式。注意它有默认值BORDER DEFAULT,更详细的解释,参考borderInterpolate()函数。

1.2 Harris程序示例

#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;

#define WINDOW_NAME1 "【程序窗口1】"        //为窗口标题定义的宏  
#define WINDOW_NAME2 "【程序窗口2】"        //为窗口标题定义的宏  

Mat g_srcImage, g_srcImage1, g_grayImage;
int thresh = 30; //当前阈值
int max_thresh = 175; //最大阈值

void on_CornerHarris(int, void*)
{
	//---------------------------【1】定义一些局部变量-----------------------------
	Mat dstImage;//目标图
	Mat normImage;//归一化后的图
	Mat scaledImage;//线性变换后的八位无符号整型的图

	//---------------------------【2】初始化---------------------------------------
	//置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值
	dstImage = Mat::zeros(g_srcImage.size(), CV_32FC1);
	g_srcImage1 = g_srcImage.clone();

	//---------------------------【3】正式检测-------------------------------------
	//进行角点检测
	cornerHarris(g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT);

	// 归一化与转换
	normalize(dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
	convertScaleAbs(normImage, scaledImage);//将归一化后的图线性变换成8位无符号整型 

	//---------------------------【4】进行绘制-------------------------------------
	// 将检测到的,且符合阈值条件的角点绘制出来
	for (int j = 0; j < normImage.rows; j++)
	{
		for (int i = 0; i < normImage.cols; i++)
		{
			if ((int)normImage.at<float>(j, i) > thresh + 80)
			{
				circle(g_srcImage1, Point(i, j), 5, Scalar(10, 10, 255), 2, 8, 0);
				circle(scaledImage, Point(i, j), 5, Scalar(0, 10, 255), 2, 8, 0);
			}
		}
	}
	//---------------------------【4】显示最终效果---------------------------------
	imshow(WINDOW_NAME1, g_srcImage1);
	imshow(WINDOW_NAME2, scaledImage);

}

int main(int argc, char** argv)
{

	//【1】载入原始图并进行克隆保存
	g_srcImage = imread("F:\\CV\\LearnCV\\files\\ZeldaH.jpg", 1);
	if (!g_srcImage.data) { return false; }
	g_srcImage1 = g_srcImage.clone();

	//【2】存留一张灰度图
	cvtColor(g_srcImage1, g_grayImage, COLOR_BGR2GRAY);

	//【3】创建窗口和滚动条
	namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
	createTrackbar("阈值: ", WINDOW_NAME1, &thresh, max_thresh, on_CornerHarris);

	//【4】调用一次回调函数,进行初始化
	on_CornerHarris(0, 0);

	waitKey(0);
	return(0);
}

在这里插入图片描述

二、Shi-Tomasi角点检测

2.1 Shi-Tomasi角点检测概述

除了利用Harris进行角点检测之外,我们通常还可以利用Shi-Tomasi方法进行角点检测。Shi-Tomasi算法是Harris算法的改进,此算法最原始的定义是将矩阵M的行列式值与M的迹相减,再将差值同预先给定的阈值进行比较。后来Shi和Tomasi提出改进了方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。由于Shi-Tomasi算子是1994年在文章《Good Features to Track》中被提出的,OpenCV实现此算法的函数名便定义为goodFeaturesToTrack。下面我们来看看此函数的用法。

2.2 确定图像强角点: goodFeaturesToTrack()函数

goodFeaturesToTrack()函数结合了Shi-Tomasi算子,用于确定图像的强角点。

goodFeaturesToTrack(InputArray image, OutputArray corners,
                   int maxCorners, double qualityLevel, double minDistance,
                   InputArray mask = noArray(), int blockSize = 3,
                   bool useHarrisDetector = false, double k = 0.04 );

goodFeaturesToTrack(InputArray image, OutputArray corners,
                   int maxCorners, double qualityLevel, double minDistance,
                   InputArray mask, int blockSize,
                   int gradientSize, bool useHarrisDetector = false,
                   double k = 0.04 );
  • 第一个参数, InputArray类型的image,输入图像,须为8位或浮点型32位单通道图像。
  • 第二个参数, OutputArray类型的corners,检测到的角点的输出向量。
  • 第三个参数, int类型的maxCorners,角点的最大数量。
  • 第四个参数, double类型的qualityLevel,角点检测可接受的最小特征值。其实实际用于过滤角点的最小特征值是qualityLevel与图像中最大特征值的乘积。所以qualityLevel通常不会超过1 (常用的值为0.10或者0.01)而检测完所有的角点后,还要进一步剔除掉一些距离较近的角点。
  • 第五个参数, double类型的minDistance,角点之间的最小距离,此参数用于保证返回的角点之间的距离不小于minDistance个像素。
  • 第六个参数, InputArray类型的mask,可选参数,表示感兴趣区域,有默认值noArray(),若此参数非空(需为cv_8UC1类型,且和第一个参数image有相同的尺寸),便用于指定角点检测区域。
  • 第七个参数, int类型的blockSize,有默认值3,是计算导数自相关矩阵时指定的邻域范围。
  • 第八个参数, bool类型的useHarrisDetector,默认值false,指示是否使用Harris角点检测。
  • 第九个参数, double类型的k,有默认值0.04,为用于设置Hessian自相关矩阵行列式的相对权重的权重系数。
    另外值得一提的是, goodFeaturesToTrack函数可用来初始化一个基于点的对象跟踪操作。

2.3 程序示例

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.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);//初始化随机数生成器

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 << "\t>此次检测到的角点数量为:" << 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);
}
int main()
{
	//【1】载入源图像并将其转换为灰度图
	g_srcImage = imread("F:\\CV\\LearnCV\\files\\ZeldaH.jpg", 1);
	cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);

	//【2】创建窗口和滑动条,并进行显示和回调函数初始化
	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);
}

在这里插入图片描述

三、亚像素级角点检测

3.1 概述

若我们进行图像处理的目的不是提取用于识别的特征点而是进行几何测量,这通常需要更高的精度,而函数goodFeatures ToTrack()只能提供简单的像素的坐标值,也就是说,有时候会需要实数坐标值而不是整数坐标值。

亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重建被跟踪目标的三维结构时,是一个基本的测量值。

下面我们将讨论如何将所求得的角点位置精确到亚像素级精度。一个向量和与其正交的向量的点积为0,角点则满足如图所示情况。
在这里插入图片描述
其中, (a)点p附近的图像是均匀的,其梯度为0; (b)边缘的梯度与沿边缘方向的q-p向量正交。在图中的两种情况下, p点梯度与g-p向量的点积均为0。

图中,我们假设起始角点q在实际亚像素级角点的附近。检测所有的q-p向量。若点p位于一个均匀的区域,则点p处的梯度为0,若g-p向量的方向·与边缘的方向一致,则此边缘上p点处的梯度与qp向量正交,在这两种情况下,p点处的梯度与q-p向量的点积为0,我们可以在p点周围找到很多组梯度以及相关的向量qp,令其点集为0,然后可以通过求解方程组,方程组的解即为角点q的亚像素级精度的位置,也就是精确的角点位置。

OpenCV为我们提供了cornerSubPix()函数,用于发现亚像素精度的角点位置。

3.2 寻找亚像素角点: cornerSubPix()函数

cornerSubPix函数用于寻找亚像素角点位置(不是整数类型的位置,而是更精确的浮点类型位置)。

C++: void cornerSubPix(InputArray image,InputOutputArray corners,Size winsize,size zerozone, TermCriteria criteria)
  • 第一个参数, InputArray类型的image,输入图像,即源图像。
  • 第二个参数, InputOutputArray类型的corners,提供输入角点的初始坐标和精确的输出坐标。
  • 第三个参数, Size类型的winSize,搜索窗口的一半尺寸。若winSize = Size(5,5),那么就表示使用(52+1) x (52+1) =11x11大小的搜索窗口。
  • 第四个参数, Size类型的zeroZone,表示死区的一半尺寸。而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。
  • 第五个参数, TermCriteria类型的criteria,求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者是精确懂达到某个设定值。criteria可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。

3.3 程序示例

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME "【亚像素级角点检测】"        //为窗口标题定义的宏 

Mat g_srcImage, g_grayImage;
int g_maxCornerNumber = 33;
int g_maxTrackbarNumber = 500;
RNG g_rng(12345);//初始化随机数生成器

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 << "\n\t>-------------此次检测到的角点数量为:" << corners.size() << endl;

	//【5】绘制检测到的角点
	int r = 4;
	for (unsigned 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);

	//【7】亚像素角点检测的参数设置
	Size winSize = Size(5, 5);
	Size zeroZone = Size(-1, -1);
	//此句代码的OpenCV2版为:
	//TermCriteria criteria = TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40, 0.001 );
	//此句代码的OpenCV3版为:
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);

	//【8】计算出亚像素角点位置
	cornerSubPix(g_grayImage, corners, winSize, zeroZone, criteria);

	//【9】输出角点信息
	for (int i = 0; i < corners.size(); i++)
	{
		cout << " \t>>精确角点坐标[" << i << "]  (" << corners[i].x << "," << corners[i].y << ")" << endl;
	}
}

int main()
{
	//【1】载入源图像并将其转换为灰度图
	g_srcImage = imread("F:\\CV\\LearnCV\\files\\ZeldaH.jpg", 1);
	cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);

	//【2】创建窗口和滑动条,并进行显示和回调函数初始化
	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);
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值