首先感谢@浅墨_毛星云,本篇博文是小武通过学习@浅墨_毛星云的博客以及书籍《opencv3.0编程入门》整理的笔记及疑问心得,小武水平有限,欢迎交流。
@浅墨_毛星云博文:https://blog.csdn.net/poem_qianmo/article/category/1923021
一、引言
在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints)、特征点(feature points) 被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析。如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。
图像特征类型可以被分为如下三种:
其中,角点是个很特殊的存在。他们在图像中可以轻易地定位,同时,他们在人造物体场景,比如门、窗、桌等出随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,,所以他们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。
二、角点算法的分类
在当前的图像处理领域,角点检测算法可归纳为三类:
而基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合三类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
现有的角点检测算法并不是都十分的健壮。很多方法都要求有大量的训练集和冗余数据来防止或减少错误特征的出现。另外,角点检测方法的一个很重要的评价标准是其对多幅图像中相同或相似特征的检测能力,并且能够应对光照变化、图像旋转等图像变化。
在我们解决问题时,往往希望找到特征点,“特征”顾名思义,指能描述物体本质的东西,还有一种解释就是这个特征微小的变化都会对物体的某一属性产生重大的影响。而角点就是这样的特征。
观察日常生活中的“角落”就会发现,“角落”可以视为所有平面的交汇处,或者说是所有表面的发起处。假设我们要改变一个墙角的位置,那么由它而出发的平面势必都要有很大的变化。所以,这就引出了图像角点的定义。
我们知道,特征检测与匹配是计算机视觉应用中非常重要的一部分,这需要寻找图像之间的特征建立对应关系。图像中的点作为图像的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。
另外,关于角点的具体描述可以有几种:
- 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
- 两条及两条以上边缘的交点;
- 图像中梯度值和梯度方向的变化速率都很高的点;
- 角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
四、函数详解
cornerHarris 函数用于在OpenCV中运行Harris角点检测算子处理图像。和cornerMinEigenVal( )以及cornerEigenValsAndVecs( )函数类似,cornerHarris 函数对于每一个像素(x,y)在
邻域内,计算2x2梯度的协方差矩阵
,接着它计算如下式子:
即可以找出输出图中的局部最大值,即找出了角点。
函数原型:
C++: void cornerHarris(InputArray src,
OutputArray dst,
int blockSize,
int ksize,
double k,
int borderType=BORDER_DEFAULT )
参数详解:
Threshold函数详解:
原型:
C++: double threshold(InputArray src,
OutputArray dst,
double thresh,
double maxval,
int type)
参数:
- 第一个参数,InputArray类型的src,输入数组,填单通道 , 8或32位浮点类型的Mat即可。
- 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放输出结果,且和第一个参数中的Mat变量有一样的尺寸和类型。
- 第三个参数,double类型的thresh,阈值的具体值。
- 第四个参数,double类型的maxval,当第五个参数阈值类型type取 CV_THRESH_BINARY 或CV_THRESH_BINARY_INV 阈值类型时的最大值.
- 第五个参数,int类型的type,阈值类型,。threshold( )函数支持的对图像取阈值的方法由其确定,具体用法如下图:
而图形化的阈值描述如下图:
代码实现:
//添加头文件
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
void call_back(int,void*) ;
Mat Img_scr,Img_dst,Img_gray;
int threshod_val=30; //当前阈值
int max_threshod_val=155;//最大阈值
int main()
{
Img_scr=imread("harris.jpg");
imshow("original",Img_scr);
cvtColor(Img_scr,Img_gray,CV_BGR2GRAY);
namedWindow("work");
createTrackbar("thresh","work",&threshod_val,max_threshod_val,call_back);
call_back(threshod_val,0);
waitKey(0);
return 0;
}
void call_back(int,void*)
{
Mat normImage,scaledImage;
//置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值
Mat Img_scr1=Img_scr.clone();
Img_dst=Mat::zeros(Img_scr.size(), CV_32FC1 );
//进行角点检测
cornerHarris(Img_gray ,Img_dst, 2 , 3 , 0.04 , BORDER_DEFAULT );
// 归一化与转换
normalize(Img_dst , normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
convertScaleAbs( normImage, scaledImage );//将归一化后的图线性变换成8位无符号整型
// 将检测到的,且符合阈值条件的角点绘制出来
for(int i=0;i< normImage.rows;i++)
{
for(int j=0;j< normImage.cols;j++)
{
if( (int) normImage.at(i,j) > threshod_val+100 )
{ //画圆
circle( Img_scr1, Point( j,i ), 5, Scalar(10,10,255), 2, 8, 0 );
circle( scaledImage, Point( j,i ), 5, Scalar(0,10,255), 2, 8, 0 );
}
}
}
imshow("work",Img_scr1);
imshow("work_1",scaledImage);
}
效果图: