作者:K.Fire | 来源:计算机视觉工坊
前言
角点时图像中存在物体边缘角落位置的点或者一些特殊位置的点,角点检测(Corner Detection)是计算机视觉系统中获取图像特征的一种方法,是运动检测、图像匹配、视频跟踪、三维重建和目标识别的基础。
本篇文章将介绍opencv中常用的几种角点检测方法的原理和基于C++的实现。
一、Harris角点检测
Harris角点原理:设置一个矩形框,将这个矩形框放置在图像中,将矩形框内像素进行求和,然后移动矩形框,当相邻两次求和得到的值差别较大时,判定矩形框内存在Harris角点。
通常出现的位置:直线的端点处、直线的交点处、直线的拐点处。
但是如果按照上述检测原理检测,会很麻烦,很难确定矩形框移动的方向和当框内出现角点时,角点的具体位置。
因此,在程序中一般是用以下方式进行检测:1.定义Harris角点检测函数为:
其中I(x,y)为(x,y)位置的像素值,w(x,y)为矩形框的权重矩阵,定义这个矩阵的原因是:当矩形框内检测到可能存在角点时,权重矩阵中权重更大的位置更有可能是角点。2.将上述公式表示为矩阵形式:
其中M为梯度协方差矩阵,表示为以下公式:
3.然后使用Harris角点评价函数判断矩形框内是否存在角点:
其中det是矩阵的行列式,k是一个自定义的常数,tr是矩阵的迹。使用上述方式计算起来计算消耗比较大,因此换用以下方式进行评价:
其中λ是梯度协方差矩阵M的特征向量,根据这个评价系数,当评价系数值较大时矩形框内有可能存在角点,但当评价系数较小时举行框内一定不存在角点。具体原因如下图所示,当λ1和λ2都是一个较大的值时才能确定一定存在角点,而当其中一个值远远大于另一个时,也会使得R值很大,但这时很有可能位于“边缘”区域,而且评价精度也跟自定义的常数k有很大关系。
Harris角点检测的代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
/*Harris角点检测*/
string path = "Lena.png";
Mat img = imread(path, 1);
Mat img_gray, harris;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
cornerHarris(img_gray, harris, 2, 3, 0.04);
//归一化便于进行数值比较和结果显示
Mat harrisn;
normalize(harris, harrisn, 0, 255, NORM_MINMAX);
//将图像数据类型转换为CV_8U
harrisn.convertTo(harrisn, CV_8U);
//寻找Harris角点
vector<KeyPoint> keyPoints;
for (int row = 0; row < harrisn.rows; row++)
{
for (int col = 0; col < harrisn.cols; col++)
{
int R = harrisn.at<uchar>(row, col);
if (R > 125)
{
//将角点存入KeyPoints中
KeyPoint keyPoint;
keyPoint.pt.x = row;
keyPoint.pt.y = col;
keyPoints.push_back(keyPoint);
}
}
}
drawKeypoints(img, keyPoints, img);
imshow("系数矩阵", harrisn);
imshow("img", img);
waitKey(0);
exit(0);
return 0;
}
运行结果:
二、Shi-Tomas角点检测
在上一节中介绍的Harris角点计算方法中,最后我们发现存在一个问题:角点检测的精度跟自定义的常数k有很大关系,Shi-Tomas角点检测就是对于Harris角点中的这一问题作出了改善,它重新定义了评价系数计算方式:
根据这个评价函数,评价系数取值为λ1和λ2中的较小值,然后用R与自定义的阈值进行比较,当其中当较小的特征向量都满足条件时,则该点一定为角点。
如下图所示,当较小的特征向量都满足条件时,角点区域为图中绿色区域,一定为角点,而图中橙色区域可能位于边缘,灰色区域一定不是角点。
Shi-Tomas角点检测代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
/*Shi-Tomas角点检测*/
string path = "Lena.png";
Mat img = imread(path, 1);
Mat img_gray;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
vector<Point2f> corners;
//Shi-Tomas角点检测
goodFeaturesToTrack(img_gray, corners, 100, 0.01, 0.1);
//绘制角点
vector<KeyPoint> keyPoints;
for (int i = 0; i < corners.size(); i++)
{
KeyPoint keyPoint;
keyPoint.pt.x = corners[i].x;
keyPoint.pt.y = corners[i].y;
keyPoints.push_back(keyPoint);
}
drawKeypoints(img, keyPoints, img);
imshow("img", img);
waitKey(0);
exit(0);
return 0;
}
运行结果:
三、亚像素级别角点位置优化
通过上面的角点检测算法,虽然能在原图像中检测出角点的位置,但仔细观察函数可以发现检测出的角点坐标都为整数值,这是因为图像是由像素构成的,是一个离散的数据类型,如果我们想要的到更精细的角点坐标,就需要对角点位置进行优化。
优化的原理如下图所示,当一个像素点q是角点时,它在自己的领域附近是一个局部最大值,以q为起点,周围一点p1或p0为终点组成一个向量,并求梯度,此时向量与梯度的积应该是0。
当在opencv中检测出角点后(此时的角点位置时不精确的),我们就根据这个原理不断调整角点q的位置,直到达到一定的精度或迭代次数。
而在实际的计算过程中,通常是通过以下公式进行计算:
就是对q与周围点组成向量与梯度的积求和,当这个和小于一定值时,就判定为达到一定的精度。
亚像素级别角点位置优化代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
/*Shi-Tomas角点检测*/
string path = "Lena.png";
Mat img = imread(path, 1);
Mat img_gray;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
vector<Point2f> corners;
//Shi-Tomas角点检测
goodFeaturesToTrack(img_gray, corners, 100, 0.01, 0.1);
cout << "检测到的角点数量:" << corners.size() << endl;
//绘制角点
vector<KeyPoint> keyPoints;
for (int i = 0; i < corners.size(); i++)
{
KeyPoint keyPoint;
keyPoint.pt.x = corners[i].x;
keyPoint.pt.y = corners[i].y;
keyPoints.push_back(keyPoint);
}
drawKeypoints(img, keyPoints, img);
//亚像素角点位置优化
vector<Point2f> cornersSub = corners;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 40, 0.01);
cornerSubPix(img_gray, cornersSub, Size(3, 3), Size(-1, -1), criteria);
for (int i = 0; i < corners.size(); i++)
{
cout << "第" << to_string(i) << "个检测出的角点坐标: " << corners[i] << " 角点位置优化后: " << cornersSub[i] << endl;
}
imshow("img", img);
waitKey(0);
exit(0);
return 0;
}
运行结果:
可以看到优化后的角点位置出现了小数,说明对其位置进行了优化。
四、FAST角点检测
相比于其他的角点检测方法,FAST角点检测的方法精度比较低,但FAST的优点也非常明显,就像它的名字一样--FAST,就是快,它计算速度快,资源消耗少,在具有有限计算资源的情况下(如基于嵌入式设备的SLAM机器人)应用非常广泛。
FAST角点检测的原理如下:
-
在图像中任选一点p, 假定其像素值为Ip
-
以3为半径画圆,覆盖p点周围的16个像素,如下图所示
-
设定阈值t,如果这周围的16个像素中有连续的n个像素的像素值都小于(Ip−t)或大于(Ip+t),那么就判断点p为角点。在OpenCV的实现中n取值为12(16个像素周长的 3/4).
-
一种更加快的改进是:首先检测p点周围的四个点,即1,5,9,12四个点中是否有三个点满足超过Ip+t,如果不满足,则直接跳过,如果满足,则继续使用前面的算法,全部判断16个点中是否有12个满足条件
FAST角点检测代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
/*FAST角点检测*/
string path = "Lena.png";
Mat img = imread(path, 1);
Mat img_gray;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
vector<KeyPoint> keyPoints;
Ptr<FastFeatureDetector> fast = FastFeatureDetector::create(32); //FAST角点检测器,32为阈值,阈值越大,特征点越少
fast->detect(img_gray, keyPoints, Mat()); //FAST角点检测
drawKeypoints(img, keyPoints, img); //FAST角点绘制
imshow("img", img);
waitKey(0);
exit(0);
return 0;
}
运行结果如下:
FAST的缺点也很明显:
-
不具备尺度不变性
-
不具备旋转不变性
-
容易受到噪声影响
ORB特征点检测中专门针对上述缺点做了改进,使得FAST在保持运算速度快的优势下,更加鲁棒。
五、ORB特征点检测
ORB的全称是ORiented Brief,它是将FAST角点检测与BRIEF特征描述结合并进行了改进:
(1)由于要解决BRIEF算法的旋转不变性,则需要计算特征点的主方向。ORB中利用重心来计算,如下(其中(x,y)是特征邻域内的点):
-
计算图像的矩
-
计算矩形区域的质心
-
连接FAST角点与质心得到方向向量
得到的θ值就是FAST特征点的主方向.
(2) BRIEF描述子,它是一种二进制编码的描述子,摈弃了利用区域灰度直方图描述特征点的传统方法,大大的加快了特征描述符建立的速度。
以特征点为中心,取SxS的邻域窗口。在窗口内随机选取一对(两个)点,比较二者像素的大小,进行如下二进制赋值。
在窗口中随机选取n对随机点,将其进行旋转,后进行判别,然后重复上述步骤的二进制赋值,形成一个二进制编码,这个编码就是对特征点的描述,即特征描述子。(一般N=256)
ORB特征点检测代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
/*ORB特征点检测*/
string path = "Lena.png";
Mat img = imread(path, 1);
Mat img_gray;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
vector<KeyPoint> keyPoints;
Ptr<ORB> orb = ORB::create(500, //特征点数目
1.2f, //金字塔层级间的缩放比例
8, //金字塔图像层数系数
31, //边缘阈值
0, //原图像在金字塔中的层级
2, //生成描述子时需要用的像素点数目
ORB::HARRIS_SCORE, //使用Harris方法评价特征点
31, //生成描述子时关键点周围邻域的尺寸
20 //计算FAST角点时像素值差值的阈值
); //ORB特征点检测器
orb->detect(img_gray, keyPoints, Mat()); //ORB特征点检测
Mat descriptors;
orb->compute(img_gray, keyPoints, descriptors); //ORB描述子计算
drawKeypoints(img, keyPoints, img); //ORB特征点绘制:不含有角度和大小
//drawKeypoints(img, keyPoints, img, DrawMatchesFlags::DRAW_RICH_KEYPOINTS); //ORB特征点绘制:含有角度和大小
imshow("img", img);
waitKey(0);
exit(0);
return 0;
}
运行结果:
总结
非常常用且经典的特征检测算法还有SIFT和SUFT算法,这两种算法都是申请专利的,不能商用,在opencv中实现时需要先配置opencv_contrib拓展包,然后包含<xfeatures2d.hpp>头文件,然后初始化方式与ORB检测类似Ptrsift = SIFT::create。
关于SIFT特征点检测可以看一下这篇文章(https://blog.csdn.net/wuzhongqiang/article/details/123969628?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168786332316800186532255%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168786332316800186532255&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-123969628-null-null.142%5Ev88%5Econtrol_2,239%5Ev2%5Einsert_chatgpt&utm_term=opencv%20sift%E7%89%B9%E5%BE%81%E7%82%B9%E5%8C%B9%E9%85%8Dc%2B%2B&spm=1018.2226.3001.4187)
其他的检测方法就不过多展开,大家可以在网上自己找一下资料进行学习。
—END—
高效学习3D视觉三部曲
第一步 加入行业交流群,保持技术的先进性
目前工坊已经建立了3D视觉方向多个社群,包括SLAM、工业3D视觉、自动驾驶方向,细分群包括:[工业方向]三维点云、结构光、机械臂、缺陷检测、三维测量、TOF、相机标定、综合群;[SLAM方向]多传感器融合、ORB-SLAM、激光SLAM、机器人导航、RTK|GPS|UWB等传感器交流群、SLAM综合讨论群;[自动驾驶方向]深度估计、Transformer、毫米波|激光雷达|视觉摄像头传感器讨论群、多传感器标定、自动驾驶综合群等。[三维重建方向]NeRF、colmap、OpenMVS等。除了这些,还有求职、硬件选型、视觉产品落地等交流群。大家可以添加小助理微信: dddvisiona,备注:加群+方向+学校|公司, 小助理会拉你入群。
添加小助理微信: dddvisiona,拉你入群
第二步 加入知识星球,问题及时得到解答
针对3D视觉领域的视频课程(三维重建、三维点云、结构光、手眼标定、相机标定、激光/视觉SLAM、自动驾驶等)、源码分享、知识点汇总、入门进阶学习路线、最新paper分享、疑问解答等进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业、项目对接为一体的铁杆粉丝聚集区,6000+星球成员为创造更好的AI世界共同进步,知识星球入口:「3D视觉从入门到精通」
学习3D视觉核心技术,扫描查看,3天内无条件退款
高质量教程资料、答疑解惑、助你高效解决问题
第三步 系统学习3D视觉,对模块知识体系,深刻理解并运行
如果大家对3D视觉某一个细分方向想系统学习[从理论、代码到实战],推荐3D视觉精品课程学习网址:www.3dcver.com
基础课程:
[1]面向三维视觉算法的C++重要模块精讲:从零基础入门到进阶
工业3D视觉方向课程:
[1](第二期)从零搭建一套结构光3D重建系统[理论+源码+实践]
SLAM方向课程:
[1]如何高效学习基于LeGo-LOAM框架的激光SLAM?
[1]彻底剖析激光-视觉-IMU-GPS融合SLAM算法:理论推导、代码讲解和实战
[2](第二期)彻底搞懂基于LOAM框架的3D激光SLAM:源码剖析到算法优化
[3]彻底搞懂视觉-惯性SLAM:VINS-Fusion原理精讲与源码剖析
[4]彻底剖析室内、室外激光SLAM关键算法和实战(cartographer+LOAM+LIO-SAM)
视觉三维重建
[1]彻底搞透视觉三维重建:原理剖析、代码讲解、及优化改进)
自动驾驶方向课程:
[1] 深度剖析面向自动驾驶领域的车载传感器空间同步(标定)
[2]面向自动驾驶领域目标检测中的视觉Transformer
[4]面向自动驾驶领域的3D点云目标检测全栈学习路线!(单模态+多模态/数据+代码)
[5]如何将深度学习模型部署到实际工程中?(分类+检测+分割)
标签:gray,搞定,img,检测,角点,FAST,opencv,ORB,关键点 From: https://blog.csdn.net/weixin_46788581/article/details/137233048