一、引言:关于兴趣点(interest points)
在图像处理和与计算机视觉领域,兴趣点(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)。
另外,关于角点的具体描述可以有几种:
1 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
2 两条及两条以上边缘的交点;
3 图像中梯度值和梯度方向的变化速率都很高的点;
4 角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
四、cornerHarris函数详解
cornerHarris 函数用于在OpenCV中运行Harris角点检测算子处理图像。函数原型如下:
C++: void cornerHarris(InputArray src,OutputArray dst, int blockSize, int ksize, double k, intborderType=BORDER_DEFAULT )
1 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
2 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。
3 第三个参数,int类型的blockSize,表示邻域的大小,更多的详细信息在cornerEigenValsAndVecs()中有讲到。
4 第四个参数,int类型的ksize,表示Sobel()算子的孔径大小。
5 第五个参数,double类型的k,Harris参数。
6 第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。
五、Threshold函数详解
函数Threshold( ) 对单通道数组应用固定阈值操作。该函数的典型应用是对灰度图像进行阈值操作得到二值图像。(另外,compare( )函数也可以达到此目的) 或者是去掉噪声,例如过滤很小或很大象素值的图像点。
1 C++: double threshold(InputArray src,OutputArray dst, double thresh, double maxval, int type)
1 第一个参数,InputArray类型的src,输入数组,填单通道 , 8或32位浮点类型的Mat即可。
2 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放输出结果,且和第一个参数中的Mat变量有一样的尺寸和类型。
3 第三个参数,double类型的thresh,阈值的具体值。
4 第四个参数,double类型的maxval,当第五个参数阈值类型type取 CV_THRESH_BINARY 或CV_THRESH_BINARY_INV 阈值类型时的最大值.
5 第五个参数,int类型的type,阈值类型。
【示例】
1 //角点检测
2 #include <opencv2/opencv.hpp>
3 #include <opencv2/imgproc/imgproc.hpp>
4
5 using namespace cv;
6
7 int main()
8 {
9 //以灰度模式载入图像并显示
10 Mat srcImage = imread("1.jpg", 0);
11 imshow("原始图", srcImage);
12
13 //进行Harris角点检测找出角点
14 Mat cornerStrength;
15 cornerHarris(srcImage, cornerStrength, 2, 3, 0.01);
16
17 //对灰度图进行阈值操作,得到二值图并显示
18 Mat harrisCorner;
19 threshold(cornerStrength, harrisCorner, 0.00001, 255, THRESH_BINARY);
20 imshow("角点检测后的二值效果图", harrisCorner);
21 imwrite("barrisCorner.jpg", harrisCorner);
22
23 waitKey(0);
24 return 0;
25 }
六、综合示例
1 //检点检测完整示例
2 #include <opencv2/opencv.hpp>
3 #include "opencv2/highgui/highgui.hpp"
4 #include "opencv2/imgproc/imgproc.hpp"
5 #include <iostream>
6
7 using namespace cv;
8 using namespace std;
9
10 #define WINDOW_NAME1 "【程序窗口1】" //为窗口标题定义的宏
11 #define WINDOW_NAME2 "【程序窗口2】" //为窗口标题定义的宏
12
13 Mat g_srcImage, g_srcImage1,g_grayImage;
14 int thresh = 30; //当前阈值
15 int max_thresh = 175; //最大阈值
16
17 void on_CornerHarris( int, void* );//回调函数
18 static void ShowHelpText();
19
20
21 int main( int argc, char** argv )
22 {
23 //【0】改变console字体颜色
24 system("color 3F");
25
26 //【0】显示帮助文字
27 ShowHelpText();
28
29 //【1】载入原始图并进行克隆保存
30 g_srcImage = imread( "1.jpg", 1 );
31 //if(!g_srcImage.data ) { cout << "读取图片错误,请确定目录下是否有imread函数指定的图片存在~!
"); return false; }
32 imshow("原始图",g_srcImage);
33 g_srcImage1=g_srcImage.clone( );
34
35 //【2】存留一张灰度图
36 cvtColor( g_srcImage1, g_grayImage, CV_BGR2GRAY );
37
38 //【3】创建窗口和滚动条
39 namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE );
40 createTrackbar( "阈值: ", WINDOW_NAME1, &thresh, max_thresh, on_CornerHarris );
41
42 //【4】调用一次回调函数,进行初始化
43 on_CornerHarris( 0, 0 );
44
45 waitKey(0);
46 return(0);
47 }
48
49
50 void on_CornerHarris( int, void* )
51 {
52 Mat dstImage;//目标图
53 Mat normImage;//归一化后的图
54 Mat scaledImage;//线性变换后的八位无符号整型的图
55
56 //置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值
57 dstImage = Mat::zeros( g_srcImage.size(), CV_32FC1 );
58 g_srcImage1=g_srcImage.clone( );
59
60 //进行角点检测
61 cornerHarris( g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT );
62
63 // 归一化与转换
64 normalize( dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
65 convertScaleAbs( normImage, scaledImage );//将归一化后的图线性变换成8位无符号整型
66
67 // 将检测到的,且符合阈值条件的角点绘制出来
68 for( int j = 0; j < normImage.rows ; j++ )
69 { for( int i = 0; i < normImage.cols; i++ )
70 {
71 if( (int) normImage.at<float>(j,i) > thresh+80 )
72 {
73 circle( g_srcImage1, Point( i, j ), 5, Scalar(10,10,255), 2, 8, 0 );
74 circle( scaledImage, Point( i, j ), 5, Scalar(0,10,255), 2, 8, 0 );
75 }
76 }
77 }
78 imshow( WINDOW_NAME1, g_srcImage1 );
79 imshow( WINDOW_NAME2, scaledImage );
80
81 }
82
83 static void ShowHelpText()
84 {
85 //输出一些帮助信息
86 cout << "
【欢迎来到Harris角点检测示例程序~】
";
87 cout << "
请调整滚动条观察图像效果~
";
88 cout << "
";
89 }