概述
亚像素角点,又叫次级像素角点,其实通俗来说,就是输入整数坐标,使用最小二乘法迭代计算所需精度的实数坐标。
进行图像处理提取用于识别的特征点进行几何测量,这通常需要更高的精度,而函数goodFeaturesToTrack()只能提供简单的像素的坐标值,也就是说,有时候会需要要实数坐标值而不是整数坐标值。
亚像素级角点检测的位置在摄像机标定、跟踪并重建摄像机的轨迹,或者重被功跟踪目标的三维结构时候,是一个基本的测量值 。
亚像素检测方法即使一些有关峰值点位置的计算技巧。
区别:
Shi-Tomas角点检测输出为简单的整数类型坐标值;
亚像素级角点检测则是更精确的浮点类型。
逻辑步骤
Shi-Tomasi角点检测算法本身输出的是像素级别的角点坐标,但可以通过进一步的亚像素级角点检测来获得更精确的亚像素坐标。以下是实现这一过程的步骤:
1. 使用Shi-Tomasi角点检测算法获得初步角点:
• 利用OpenCV中的goodFeaturesToTrack函数,该函数可以实现Shi-Tomasi角点检测。
• 输入图像、角点的最大数量、角点检测可接受的最小特征值、角点之间的最小距离等参数,函数将输出检测到的角点坐标,这些坐标是整数类型的像素坐标。
2. 进行亚像素级角点检测:
• 在获得初步角点坐标后,可以使用OpenCV中的cornerSubPix函数进行亚像素级角点检测。
• cornerSubPix函数需要输入图像、初步角点坐标、搜索窗口的大小、死区的大小以及迭代过程的终止条件等参数。
• 通过最小二乘法迭代计算,cornerSubPix函数将输出更精确的亚像素级角点坐标。
3. 输出亚像素坐标:
• 在完成亚像素级角点检测后,可以将得到的亚像素坐标进行输出或进一步处理。
• 亚像素坐标是浮点类型的,具有比像素坐标更高的精度。
需要注意的是,在进行亚像素级角点检测时,搜索窗口的大小、死区的大小以及迭代过程的终止条件等参数的选择会影响最终的检测精度和计算效率。因此,在实际应用中需要根据具体需求和场景进行参数调整和优化。
另外,虽然Shi-Tomasi角点检测算法和Harris角点检测算法在理论上有所差异,但在实际应用中,两者经常可以互换使用,并且都可以通过上述步骤实现亚像素级角点检测。
原理
- (a) 点p附近的图像是均匀的,其梯度为0,
- (b) 边缘的梯度与沿地缘方向的q-p向量正交。
- 在图中的两种情况下,p点梯度与q-p向量的点积均为0。
假设起始角点q在实际亚像素级角点的附近。检测所有的q-p向量。若点p位于一个均匀的区域,则点p处的梯度为0。若q-p向量的方向与边缘的方向一致,则此边缘上p点处的梯度与q-p向量正交,在这两种情况下,p点处的梯度与q-p向量的点积为0。可以在p点周围找到很多组梯度以及相关的向量q节,令其点集为0,然后可以通过求解方程组,方程组的解即为角点q的亚像素级精度的位置,也就是精确的角点位置。
函数及代码
1、Shi-Tomas角点检测
//Shi-Tomas角点检测
int test1()
{
Mat img = imread("F:/testMap/lena.png");
if (!img.data)
{
cout << "读取图像错误, 请确认图像文件是否正确" << endl;
return -1;
}
Mat gray;
cvtColor(img, gray,COLOR_BGR2GRAY);
//Detector parametersl
//提取角点
int maxCorners = 100;//检测角点数目
double quality_level = 0.01;//质量等级,或者说阈值与最佳角点的比例关系
double minDistance = 0.04;//两个角点之间的最小欧式距离
vector<Point2f> corners;
goodFeaturesToTrack(gray,corners,maxCorners,quality_level, minDistance,Mat(),3,false);
//绘制角点
vector<KeyPoint> keyPoints;//存放角点的KeyPoint类,用于后期绘制角点时用
for (int i = 0; i < corners.size(); i++)
{
//将角点存放在KeyPoint类中
KeyPoint keyPoint;
keyPoint.pt = corners[i];
keyPoints.push_back(keyPoint);
}
//用drawKeypoints()函数绘制角点坐标
drawKeypoints(img, keyPoints, img);
imshow("角点结果", img);
waitKey(0);
return 0;
}
2、亚像素级别角点
cornerSubPix函数:
void cornerSubPix( InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria );
- image:InputArray类型的image,输入图像,即源图像,填Mat类的对象,必须是CV_8U(8位)或者CV_32F的单通道灰度图像;
- corners:整数值的角点坐标,即是输入角点的初始坐标和精确后的输出坐标;
- winSize:搜索窗口尺寸的一半,必须是整数。实际的搜索窗口尺寸比该参数的2倍大1。若winSize=Size(5,5),那么就表示使用(5X2+1)x(5X2+1)=11x11大小的搜索窗口。
- zeroZone:搜索区域中间死区大小的一半,即不提取像素点的区域,(-1,-1)表示没有死区。死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。
- criteria:角点优化迭代的终止条件。迭代过程的中止条件可以是:1,最大迭代次数;2,设定的精度;3,也可以是它们的组合。中止条件的设置在很大程度上影响最终得到的亚像素值得精度。
//亚像素级别角点位置优化
int test2()
{
system("color 02");//改变DOS界面颜色
Mat img = imread("F:/testMap/lena.png", IMREAD_COLOR);
if (!img.data)
{
cout << "读取图像错误,请确认图像文件是否正确" << endl;
return -1;
}
//彩色图像转成灰度图像
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
//提取角点
int maxCorners = 100;//检测角点数目
double quality_level = 0.01;//质量等级,或者说阈值与最佳角点的比例关系
double minDistance = 0.04;//两个角点之间的最小欧式距离
vector<Point2f> corners;
goodFeaturesToTrack(gray, corners, maxCorners, quality_level, minDistance, Mat(), 3, false);
//计算亚像素级别角点坐标
vector<Point2f> cornersSub = corners;//角点备份,防止被函数修改
Size winSize = Size(5, 5);
Size zeroZone = Size(-1, -1);
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 40, 0.001);
cornerSubPix(gray, cornersSub, winSize, zeroZone, criteria);
//输出初始坐标和精细坐标
for (size_t i = 0; i < corners.size(); i++)
{
string str = to_string(i);
str = "第" + str + "个角点点初始坐标:";
cout << str << corners[i] << "精细后坐标:" << cornersSub[i] << endl;
}
waitKey(0);
return 0;
}
函数及代码原文:
https://blog.youkuaiyun.com/qq_57594025/article/details/131731154