图像的特征
在计算机视觉中,我们通常需要找出两幅图像的匹配点(matching points),因为如果能找到两幅图像如何相关,就能利用两幅图像一起来提取出它们所包含的信息。当我们说匹配点时实际上是指在一般情形下能够很容易被识别的一些特征,这些特征被叫做feature,他们所具有的最鲜明的特点就是具有唯一可识别性(uniquely recognizable)。图像的feature类型通常包括边界(edges)、角点(corners,也叫兴趣点)、区块(blobs,也叫兴趣区域)。
图像的角点
这里要重点介绍的角点就是图像的一个局部特征,它是两条边界(edge)的交叉处,代表了两条边界的方向发生改变的一个点。因此,该处图像在各个方向的梯度都有很大的变化,这个原理可以用于检测角点:
(1)对于一幅灰度图像,假设计算梯度时的扫描窗口为w(x,y),在x方向和y方向上的位移分别为u和v,那么强度的变化是:
其中w(x,y)是点(x,y)处的窗口,I(x,y)是点(x,y)处的强度,I(x+u,y+v)是移动后的点(x+u,y+v)处的强度。
(2)要寻找角点就是寻找强度变化最大的点,就是使上面的式子值最大,即使得下式值最大:
使用泰勒展开:
展开并消去:
表示成矩阵形式:
将中间的项表示为M:
所以最初的式子变成了:
(3)对每个扫描窗口都作上述计算,来判断是否包含角点:
其中det(M)=λ1λ2,trace(M)=λ1+λ2(λ1、λ2为M的特征值)。如果某个窗口的R值大于某个设定的阈值,就被认为是包含角点。
Harris角点检测
Harris角点检测是一种直接基于灰度图像的角点提取算法,稳定性高,尤其对L型角点检测精度高,但由于采用了高斯滤波,运算速度相对较慢,角点信息有丢失和位置偏移的现象,而且角点提取有聚簇现象。在opencv中的具体实现就是使用函数cornerHarris()实现,函数原型如下:
C++: void cornerHarris(InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType=BORDER_DEFAULT ) C: void cvCornerHarris(const CvArr* image, CvArr* harris_response, int block_size, int aperture_size=3, double k=0.04 )
参数定义如下:
src:输入单通道8-bit或者浮点型图像;
dst:存储Harris检测器响应结果的图像,类型为CV_32FC1并且与src大小一致;
blockSize:邻域大小,相当于上文所说的扫描窗口大小(具体可参考这里:cornerEigenValsAndVecs());
ksize:Sobel算子当中的核大小,只能取1、3、5、7(具体可参考这里:Sobel());
k:Harri角点检测自由参数,相当于上文第(3)步中所说的k。
borderType:像素扩展的方法,因为在滤波处理的过程中会扩展图像边缘,每扩张一个边界像素,都需要计算出该像素点在原图中的位置,这个功能被提炼出来就变成了borderInterpolate()函数。该函数输入一个点坐标,返回他在原图中的坐标,关于这个函数的详细解释,参考:
1、在OpenCV中圖像邊界擴展 copyMakeBorder 的實現_人人IT網
2、borderInterpolate解释_Halley_新浪博客
示例代码如下:
/** * @使用Harris-Stephens方法进行角点检测 * @author holybin */ #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> #include <stdlib.h> using namespace cv; using namespace std; /// 全局变量 Mat src, srcGray; int thresh = 150; //阈值 int maxThresh = 255; //最大阈值 char* sourceWindow = "src"; char* cornersWindow = "harris corner"; /// 角点检测函数声明 void doCornerHarris( int, void* ); int main( int argc, char** argv ) { /// 载入图像并灰度化 src = imread( "D:\\opencv_pic\\house_small.jpg", 1 ); cvtColor( src, srcGray, CV_BGR2GRAY ); /// 创建显示窗口以及滑动条 namedWindow( sourceWindow, CV_WINDOW_AUTOSIZE ); createTrackbar( "Threshold: ", sourceWindow, &thresh, maxThresh, doCornerHarris ); imshow( sourceWindow, src ); doCornerHarris( 0, 0 ); waitKey(0); return 0; } /// 角点检测函数实现:标示出每个角点位置 void doCornerHarris( int, void* ) { Mat dst, dstNorm, dstNormScaled; dst = Mat::zeros( src.size(), CV_32FC1 ); /// 检测参数 int blockSize = 2; int apertureSize = 3; double k = 0.04; /// Harris角点检测 cornerHarris( srcGray, dst, blockSize, apertureSize, k, BORDER_DEFAULT ); /// 归一化 normalize( dst, dstNorm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); convertScaleAbs( dstNorm, dstNormScaled ); /// 标记出角点 for( int j = 0; j < dstNorm.rows ; j++ ) { for( int i = 0; i < dstNorm.cols; i++ ) { //如果大于阈值thresh,表明有角点存在 if( (int) dstNorm.at<float>(j,i) > thresh ) circle( dstNormScaled, Point( i, j ), 3, Scalar(0), 2, 8, 0 ); } } /// 显示结果 namedWindow( cornersWindow, CV_WINDOW_AUTOSIZE ); imshow( cornersWindow, dstNormScaled ); cout<<"Threshold: "<<thresh<<endl; }
结果如图: