角点检测(兴趣点、关键点、特征点)是计算机视觉系统中用来获得图像特征的一种方法,广泛用于运动检测、图像匹配、视频跟踪、三维重建和目标识别等领域中,也称为特征点检测。
角点并没有明确的定义,一般将图像中亮度变化剧烈的点或图像边缘上曲率取极大值的点认为是角点。角点作为图像的重要特征,保留了图像绝大部分的特征信息,又有效地减少了信息的数据量从而有效地提高了运算速度以及匹配的可靠性。
1. Moravec算子
Moravec在1981年提出Moravec角点检测算子,Moravec角点检测算法是最早的角点检测算法之一。
Moravec的原理如果有一句话来说就是:通过滑动二值矩形窗口寻找灰度变化的局部最大值。具体来说主要包括四个过程:
- 滑动窗口计算灰度变化(Calculate intensity variantion from shifting windows)
- 构造角点性映射图C(x,y)(Construct the cornerness map)
- 根据实际图像设置阈值T,性映射图C(x,y)中低于阈值T的值设置为零
- 局部非极大值抑制选取角点,在一定大小的窗口内,将第三步选取的候选角点中,去掉角点响应值不是极大值的。(本质是搜索局部极大值,抑制非极大值元素)
2.Harris算子
Harris算子也称为Plessey算子,是由Harris和Stephens为了改善Moravec算子性能提出的。Harris算子以二阶矩阵(又称为自相关矩阵)为基础,二阶矩阵描述了像素点局部邻域内的梯度分布信息。定义M为像素点(x,y)的自相关矩阵,则
X、Y是一阶方向微分,反映了图像中每个像素的灰度变化方向。为了避免矩阵M进行特征值分解,方便运算,则将角点响应函数表示为:
k为经验值,一般在0.04-0.06之间取值。只要在某一点(x,y)处,超过某一设定的阈值,即认为该点为角点。
Harris算子的计算步骤:
- 对每一个像素计算自相关矩阵M
- 构造角点性映射图(Construct cornerness map)
- 阈值化,对得到的C(x,y)进行阈值
- 非极大值抑制
Harris算子具有平移和旋转不变性,对光照条件的变化不敏感。在的角点特征的比较实验中,Harri s角点特征的重复性和区分性被证明是最好的。通过在局部极值点的邻域内对角点响应函数进行二次逼近,Harris算子可以达到亚像素的定位精度。
3.SUSAN算子
考虑到基于局部梯度的方法对噪声影响比较敏感而且计算量大,Smith和Brady提出了一种基于形态学的角点特征检测方法。如果多个像素属于同一目标,那么在相对较小的局部邻域内像素的亮度应该是一致的 。
基于这一假设,SUSAN算子通过在圆形模板区域内进行亮度比较检测角点特征。对于图像中的每一个像素,考虑一个固定半径的圆形邻域,以该像素作为中心参照,圆形邻域内的所有像素根据与参照像素的亮度关系 ,被分类成相似像素和不相似像素。通过这种方式为每个像素点生成一个关联的局部亮度相似性区域,区域的大小包含了该像素点处的图像结构信息。
图1
如图1所示,其中圆形邻域内的黑色区域代表相似区域,白色区域代表不相似区域。SUSAN算子定义一个像素点为角点的条件为像素点的关联相似性区域内的像素数达到局部极小值并且小于预先设定的固定门限。SUSAN算子具有平移和旋转不变性 。
4.FAST算子
Rosten等人在SUSAN角点特征检测方法基础上利用机器学习方法提出FAST角点算子。FAST算法包含3个主要
步骤:
- 对固定半径圆上的像素进行分割测试,通过逻辑测试可以去处大量的非特征候选点;
- 基于分类的角点特征检测,利用ID3tree分类器根据16个特征判决候选点是否为角点特征,每个特征的状态为 -1,0,1。
- 利用非极大值抑制进行角点特征的验证。
FAST角点算子具有平移和旋转不变性、可靠性高、对噪声鲁棒性好、计算量小。
#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
Mat g_srcImage, g_srcImage1,g_grayImage;
int thresh=30;
void on_CornerHarris(); //Harris角点检测
void on_GoodFeaturesToTrack(); //Shi-Tomasi角点检测
void on_cornerSubPix(); //亚像素级角点检测
void on_FastFeatureDetector(); //fast特征点检测
int main( int argc, char** argv ){
g_srcImage = imread( "lena.png", 1 );
if(!g_srcImage.data ) { printf("读取图片错误!\n"); return false; }
g_srcImage1=g_srcImage.clone( );
cvtColor( g_srcImage1, g_grayImage, CV_BGR2GRAY );
//on_CornerHarris(); //Harris角点检测
//on_GoodFeaturesToTrack();//Shi-Tomasi角点检测
//on_cornerSubPix();
on_FastFeatureDetector();
waitKey(0);
return(0);
}
//Harris角点检测--------------------------------------------------------------------------------------------
void on_CornerHarris()
{
Mat dstImage;//目标图
Mat normImage;//归一化后的图
Mat scaledImage;//线性变换后的八位无符号整型的图
cornerHarris(g_grayImage, //Input single-channel 8-bit or floating-point image.
dstImage, //Image to store the Harris detector responses. It has the type CV_32FC1 and the same size as src .
2, //Neighborhood size
3, //Aperture parameter for the Sobel() operator
0.04, // Harris detector free parameter
BORDER_DEFAULT );// Pixel extrapolation method
normalize( dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
convertScaleAbs( normImage, scaledImage );//将归一化后的图线性变换成8位无符号整型
for( int j = 0; j < normImage.rows ; j++ ){
for( int i = 0; i < normImage.cols; i++ ){
if( (int) normImage.at<float>(j,i) > thresh+100 ){
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 );
}
}
}
imshow("g_srcImage1",g_srcImage1);
}
//Shi-Tomasi角点检测--------------------------------------------------------------------------------------------
void on_GoodFeaturesToTrack()
{
vector<Point2f> corners;
int g_maxCornerNumber = 33;
double qualityLevel = 0.01; //角点检测可接受的最小特征值
double minDistance = 10; //角点之间的最小距离
int blockSize = 3; //计算导数自相关矩阵时指定的邻域范围
double k = 0.04; //权重系数
Mat copy = g_srcImage.clone();
//进行Shi-Tomasi角点检测
goodFeaturesToTrack( g_grayImage, //Input 8-bit or floating-point 32-bit, single-channel image.
corners, //Output vector of detected corners.
g_maxCornerNumber, //Maximum number of corners to return.
qualityLevel, //Parameter characterizing the minimal accepted quality of image corners.
minDistance, //Minimum possible Euclidean distance between the returned corners.
Mat(), //Optional region of interest.
blockSize, //Size of an average block for computing a derivative covariation matrix over each pixel neighborhood.
false, //Parameter indicating whether to use a Harris detector
k ); // Free parameter of the Harris detector.
//point2f 转KeyPoint
vector<KeyPoint> keypoints;
for( size_t i = 0; i < corners.size(); i++ ) {
keypoints.push_back(KeyPoint(corners[i], 1.f));
}
//绘制关键点
drawKeypoints(copy,keypoints,copy,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
imshow("Shi-Tomasi角点", copy );
}
//亚像素级角点检测角点检测--------------------------------------------------------------------------------------------
void on_cornerSubPix()
{
vector<Point2f> corners;
int g_maxCornerNumber = 33;
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
double k = 0.04;
Mat copy = g_srcImage.clone();
//进行Shi-Tomasi角点检测
goodFeaturesToTrack(g_grayImage,corners, g_maxCornerNumber,qualityLevel,minDistance,Mat(),blockSize,false,k);
//亚像素级角点检测角点检测
Size winSize = Size( 5, 5 );
Size zeroZone = Size( -1, -1 );
TermCriteria criteria = TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40, 0.001 );
cornerSubPix( g_grayImage, // Input image
corners, //Initial coordinates of the input corners and refined coordinates provided for output.
winSize, //Half of the side length of the search window.
zeroZone, // Half of the size of the dead region in the middle of the search zone over which the summation in the formula below is not done.
criteria ); //Criteria for termination of the iterative process of corner refinement.
cout<<corners<<endl;
//point2f 转KeyPoint
vector<KeyPoint> keypoints;
for( size_t i = 0; i < corners.size(); i++ ) {
keypoints.push_back(KeyPoint(corners[i], 1.f));
}
//绘制关键点
drawKeypoints(copy,keypoints,copy,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
imshow("亚像素级角点检测角点检测", copy );
}
//fast特征点检测角点检测------------------------------------------------------------------------
void on_FastFeatureDetector(){
std::vector<KeyPoint> keyPoints;
FastFeatureDetector fast(50); // 检测的阈值为50
fast.detect(g_srcImage1,keyPoints);
drawKeypoints(g_srcImage1, keyPoints, g_srcImage1, Scalar::all(255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
imshow("FAST feature", g_srcImage1);
}