角点检测
- 1 Harris角点检测
- 1.1 兴趣点与角点
- 1.2 角点检测
- 1.3 harris角点检测
- 1.4 实现harris角点检测:cornerHarris()函数
- 1.5 综合案例:harris角点检测与测绘
- 2. Shi—Tomasi角点检测
- 2.1Shi—Tomasi角点检测概述
- 2.2 确定图像强角点:goodFeaturesToTrack()函数
- 2.3 综合示例:Shi—Tomasi角点检测
- 3. 亚像素级角点检测
- 3.1 背景概述
- 3.2 寻找亚像素角点:cornerSubPix()函数
1 Harris角点检测
1.1 兴趣点与角点
在图像处理和计算机视觉领域,兴趣点,也被成作关键点、特征点。它被大量用于解决物体识别、图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢地分析。如果能检测到足够多的这种点,同时它们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就具有实用价值。
图像特征类型可以被分为如下三种:
- 边缘
- 角点(感兴趣关键点)
- 斑点(感兴趣区域)
其中,角点是个很特殊的存在。如果某一点在任意方向的一个微小变化都会引起灰度很大的变化,那么我们就把它称之为角点。角点作为图像上的特征点,包含有重要的信息,在图像融合和目标跟踪以及三维重建中有重要的应用价值。他们在图像中可以轻易地定位,同时,在人造物体场景,比如门、窗、桌等处也随处可见。因为角点位于两条边缘交点处,代表了两个边缘变化的方向上的点,所以他们是可以精确定位的而为特征,甚至可以达到亚像素的精度。又由于其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其物体上精确定位。
另外,关于角点的具体描述可以有如下几种:
- 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
- 两条及两条以上边缘的交点;
- 图像中梯度值和梯度方向的变化速率都很高的
- 角点处的一阶导数最大,二阶导数为零,它指示了物体边缘变化不连续的方向。
1.2 角点检测
现有的角点检测算法并不是都十分的健壮。很多方法都要求有大量的训练集和冗余数据来防止或减少错误特征的出现。另外,角点检测方法的一个很重要的评价标准是其对多幅图像中相同或相似特征的检测能力,并且能够应对光照变化、图像旋转等图像变化。
在当前的图像处理领域,角点检测算法可归纳为一下三类。
- 基于灰度图像的角点检测
- 基于二值图像的角点检测
- 基于轮廓曲线的角点检测
1.3 harris角点检测
harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象,而且角点提取有聚簇的现象。
1.4 实现harris角点检测:cornerHarris()函数
- 第一个参数:输入单通道8位或浮点图像。
- 第二个参数:用于存储Harris检测器响应的图像。它的类型为CV_32FC1,大小与src相同。
- 第三个参数:邻域大小
- 第四个参数:表示Sobel()算子的孔径大小
- 第五个参数:Harris检测器的自由参数,见上述公式
1.5 综合案例:harris角点检测与测绘
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
#define WINDOW_NAME1 "程序窗口1"
#define WINDOW_NAME2 "程序窗口2"
Mat g_srcImage, g_srcImage1, g_grayImage;
int thresh = 30; //当前阈值
int max_thresh = 175; //最大阈值
//回调函数
void on_ConerHarris(int, void*) {
Mat dstImage;
Mat normImage; //归一化之后
Mat scaledImage; //线性变换后的八位无符号整型的图
dstImage = Mat::zeros(g_srcImage.size(), CV_32FC1);
g_srcImage1 = g_srcImage.clone();
//进行角点检测
cornerHarris(g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT);
//归一化
normalize(dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
convertScaleAbs(normImage, scaledImage);//将归一化后的图线性变化为无符号整型
//进行绘制
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, 0);
circle(scaledImage, Point(i, j), 5, Scalar(0, 10, 255), 2, 0);
}
}
}
imshow(WINDOW_NAME1, g_srcImage1);
imshow(WINDOW_NAME2, scaledImage);
}
int main() {
g_srcImage = imread("1.jpg", 1);
imshow("原始图", g_srcImage);
g_srcImage1 = g_srcImage.clone();
//存留一张灰度图
cvtColor(g_srcImage1, g_grayImage, COLOR_BGR2GRAY);
//创建窗口和滚动条
namedWindow(WINDOW_NAME1, 0);
createTrackbar("阈值", WINDOW_NAME1, &thresh, max_thresh, on_ConerHarris);
//对回调函数进行初始化
on_ConerHarris(0, 0);
waitKey();
return 0;
}
2. Shi—Tomasi角点检测
2.1Shi—Tomasi角点检测概述
除了利用Harris进行角点检测之外,我们通常还可以利用Shi-Tomasi方法进行角点检测。Shi-Tomasi算法是 Harris算法的改进,此算法最原始的定义是将矩阵M的行列式值与M的迹相减,再将差值同预先给定的阈值进行比较。后来Shi和 Tomasi提出改进了方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。
2.2 确定图像强角点:goodFeaturesToTrack()函数
goodFeaturesToTrack()函数结合了Shi-Tomasi算子,用于确定图像的墙角点。
- 第一个参数:输入8位或浮点32位的单通道图像。
- 第二个参数:检测到的角的输出向量。
- 第三个参数:要返回的角的最大数量。maxCorners <= 0意味着没有设置最大数量的限制,所有检测到的角都被返回。
- 第四个参数:表征图像角落的最小接受质量的参数。参数值乘以最佳角部质量度量,即最小特征值(见cornerMinEigenVal)或Harris函数响应(见cornerHarris)。质量度量小于乘积的角被拒绝。例如,如果最佳角的质量度量=1500,并且qualityLevel=0.01,那么所有质量度量小于15的角都被拒绝。
- 第五个参数:角点之间的最小距离,此参数用于保证返回的角点之间的距离不小于minDistance个像素。
- 第六个参数:可选的兴趣区域。如果图像不是空的(它需要有CV_8UC1的类型和与图像相同的大小),它指定了检测角的区域。
- 第七个参数:计算导数自相关矩阵时指定的邻域范围。
- 第八个参数:表示是否使用Harris检测器(见cornerHarris)或cornerMinEigenVal的参数。
- 第九个参数:设置Hessian自相关矩阵行列式的权重系数。
2.3 综合示例:Shi—Tomasi角点检测
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
#define WINDOW_NAME "Shi-Tomasi角点检测"
Mat g_srcImage, g_grayImage;
int g_maxCornerNumber = 33;
int g_maxTarckbar = 500;
RNG g_rng(12345);
void on_GoodFeaturesToTrack(int, void*) {
if (g_maxCornerNumber <= 1) g_maxCornerNumber = 1;
//Shi-Tomasi算法参数准备
vector<Point2f> corners;
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
double k = 0.04;
Mat copy = g_srcImage.clone();
goodFeaturesToTrack(g_grayImage,
corners,
g_maxCornerNumber,
qualityLevel,
minDistance,
Mat(),
blockSize,
false,
k
);
cout << "此次检测到的角点数量为:" << corners.size() << endl;
//绘制检测到的角点
for (int i = 0; i < corners.size(); i++) {
circle(copy, corners[i], 4, Scalar(0, 0, 255), 1, 8, 0);
}
imshow(WINDOW_NAME, copy);
}
int main() {
g_srcImage = imread("1.jpg", 1);
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
namedWindow(WINDOW_NAME);
createTrackbar("最大角点数", WINDOW_NAME, &g_maxCornerNumber,g_maxTarckbar, on_GoodFeaturesToTrack);
imshow("源图像", g_srcImage);
on_GoodFeaturesToTrack(0, 0);
waitKey();
}
3. 亚像素级角点检测
3.1 背景概述
若我们进行图像处理的目的不是提取用于识别的特征点而是进行几何测量,这通常需要更高的精度,而函数 goodFeaturesToTrack()只能提供简单的像素的坐标值,也就是说,有时候会需要实数坐标值而不是整数坐标值。
亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重建被跟踪目标的三维结构时,是一个基本的测量值。
亚像素精确的角定位器是基于这样的观察:从中心q到位于q的邻域内的点p的每个矢量都是与p处的图像梯度正交的,并受到图像和测量噪声的影响。考虑一下这个表达式:
其中DIpi是q附近的一个点pi的图像梯度。要找到q的值,使ϵi达到最小。可以建立一个方程组,将ϵi设为零:
其中梯度是在q的邻域(“搜索窗口”)内求和。将第一个梯度项称为G,第二个梯度项称为b,就可以得到:
该算法将邻域窗口的中心设置在这个新的中心q,然后进行迭代,直到该中心保持在一个设定的阈值之内。
3.2 寻找亚像素角点:cornerSubPix()函数
- 第一个参数:输入的图像
- 第二个参数:InputOutputArray类型的corners,提供输入角点的初始坐标和精确地输出坐标
- 第三个参数:搜索窗口边长的一半。例如,如果winSize=Size(5,5) ,则使用(5∗2+1)×(5∗2+1)=11×11的搜索窗口。
- 第四个参数:Size类型的zeroZone,表示死区的一半尺寸。而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。
- 第五个参数:角部细化迭代过程的终止标准。也就是说,角位置的细化过程要么在 criteria.maxCount 迭代之后停止,要么在某个迭代中角位置的移动小于 criteria.epsilon时停止。