OpenCV学习笔记之十三——角点检测之Harris角点检测

首先感谢@浅墨_毛星云本篇博文是小武通过学习@浅墨_毛星云的博客以及书籍《opencv3.0编程入门》整理的笔记及疑问心得,小武水平有限,欢迎交流。

@浅墨_毛星云博文:https://blog.csdn.net/poem_qianmo/article/category/1923021


一、引言

在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints)、特征点(feature points) 被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析。如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。


图像特征类型可以被分为如下三种:

  • <1>边缘
  • <2>角点 (感兴趣关键点)
  • <3>斑点(Blobs)(感兴趣区域)

其中,角点是个很特殊的存在。他们在图像中可以轻易地定位,同时,他们在人造物体场景,比如门、窗、桌等出随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,,所以他们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。


二、角点算法的分类

在当前的图像处理领域,角点检测算法可归纳为三类:

  • <1>基于灰度图像的角点检测
  • <2>基于二值图像的角点检测
  • <3>基于轮廓曲线的角点检测

而基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合三类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。

三、角点的定义
“如果某一点在任意方向的一个微小变动都会引起灰度很大的变化,那么我们就把它称之为角点”
   角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模和目标   识别等领域中。也称为特征点检测。
  角点通常被定义为两条边的交点,更严格的说,角点的局部邻域应该具有两个不同区域的不同方向的边界。而实际应用中,大多数所谓的角点检 测方法检测的是拥有特定特征的图像点,而不仅仅是“角点”。这些特征点在图像中有具体的坐标,并具有某些数学特征,如局部最大或最小灰 度、某些梯度特征等。

现有的角点检测算法并不是都十分的健壮。很多方法都要求有大量的训练集和冗余数据来防止或减少错误特征的出现。另外,角点检测方法的一个很重要的评价标准是其对多幅图像中相同或相似特征的检测能力,并且能够应对光照变化、图像旋转等图像变化。

在我们解决问题时,往往希望找到特征点,“特征”顾名思义,指能描述物体本质的东西,还有一种解释就是这个特征微小的变化都会对物体的某一属性产生重大的影响。而角点就是这样的特征。

观察日常生活中的“角落”就会发现,“角落”可以视为所有平面的交汇处,或者说是所有表面的发起处。假设我们要改变一个墙角的位置,那么由它而出发的平面势必都要有很大的变化。所以,这就引出了图像角点的定义。

我们知道,特征检测与匹配是计算机视觉应用中非常重要的一部分,这需要寻找图像之间的特征建立对应关系。图像中的点作为图像的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(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 )

参数详解:

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
  • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。
  • 第三个参数,int类型的blockSize,表示邻域的大小,更多的详细信息在cornerEigenValsAndVecs()中有讲到。
  • 第四个参数,int类型的ksize,表示Sobel()算子的孔径大小。
  • 第五个参数,double类型的k,Harris参数。
  • 第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。


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<opencv/cxcore.hpp>
#include<opencv2/core/core.hpp>  
#include<opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  
#include<iostream>  
  
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<float>(i,j) > threshod_val+100 )  
        {  //画圆
            circle( Img_scr1, 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("work",Img_scr1);
 imshow("work_1",scaledImage);
}

效果图:




猜你喜欢

转载自blog.csdn.net/weixin_40647819/article/details/80084759