作者: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?#include? 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 ?keyPoints;?for?(int?row?=?0;?row?(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?#include? 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 ?corners;?//Shi-Tomas角点检测?goodFeaturesToTrack(img_gray,?corners,?100,?0.01,?0.1);?//绘制角点?vector ?keyPoints;?for?(int?i?=?0;?i? 运行结果:
三、亚像素级别角点位置优化
通过上面的角点检测算法,虽然能在原图像中检测出角点的位置,但仔细观察函数可以发现检测出的角点坐标都为整数值,这是因为图像是由像素构成的,是一个离散的数据类型,如果我们想要的到更精细的角点坐标,就需要对角点位置进行优化。
优化的原理如下图所示,当一个像素点q是角点时,它在自己的领域附近是一个局部最大值,以q为起点,周围一点p1或p0为终点组成一个向量,并求梯度,此时向量与梯度的积应该是0。
当在opencv中检测出角点后(此时的角点位置时不精确的),我们就根据这个原理不断调整角点q的位置,直到达到一定的精度或迭代次数。
而在实际的计算过程中,通常是通过以下公式进行计算:
就是对q与周围点组成向量与梯度的积求和,当这个和小于一定值时,就判定为达到一定的精度。
亚像素级别角点位置优化代码如下:
#include?#include? 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 ?corners;?//Shi-Tomas角点检测?goodFeaturesToTrack(img_gray,?corners,?100,?0.01,?0.1);?cout?<"检测到的角点数量:"?<?keyPoints;?for?(int?i?=?0;?i??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? 运行结果:
可以看到优化后的角点位置出现了小数,说明对其位置进行了优化。
四、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?#include? 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 ?keyPoints;?Ptr ?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?#include? 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 ?keyPoints;?Ptr ?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拓展包,然后包含
头文件,然后初始化方式与ORB检测类似Ptr 关于SIFT特征点检测可以看一下这篇文章(https://blog.csdn.net/wuzhongqiang/article/details/123969628)
其他的检测方法就不过多展开,大家可以在网上自己找一下资料进行学习。
—END— 目前工坊已经建立了3D视觉方向多个社群,包括SLAM、工业3D视觉、自动驾驶方向,细分群包括:[工业方向]三维点云、结构光、机械臂、缺陷检测、三维测量、TOF、相机标定、综合群;[SLAM方向]多传感器融合、ORB-SLAM、激光SLAM、机器人导航、RTK|GPS|UWB等传感器交流群、SLAM综合讨论群;[自动驾驶方向]深度估计、Transformer、毫米波|激光雷达|视觉摄像头传感器讨论群、多传感器标定、自动驾驶综合群等。[三维重建方向]NeRF、colmap、OpenMVS等。除了这些,还有求职、硬件选型、视觉产品落地等交流群。大家可以添加小助理微信: dddvisiona,备注:加群+方向+学校|公司, 小助理会拉你入群。
本文暂时没有评论,来添加一个吧(●'◡'●)