opencv角点检测(一)
Harris角点检测算法原理简介
harris角点检测算法首先对图像中的每个像素计算2*2的协方差矩阵M,然后求出如下表达式的值:
R=det(M) -k*(trace(M)^2) (一般k的取值在0.04~0.06之间,opencv中取值范围更大)
det(M)=λ1*λ2 trace(M)=λ1+λ2, λ1、λ2为协方差矩阵M的特征值;
R值为正且比较大时为角点位置,R值为负时为边缘,R值很小时是平坦区域。
opencv中cornerHarris函数实现harris角点的提取,具体代码如下:
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include<opencv2/imgproc/imgproc.hpp> //添加函数中用到的模块 #include <iostream> using namespace std; using namespace cv; //定义全局变量,与Trackbar相关联的变量声明为全局变量,调用比较方便; Mat src,src_gray; int thresh = 150; int max_thresh = 255; char* sourceimg = "Source image"; char* cornersimg = "Corners detected"; void Harris_demo(int,void*); int main(int argc,char* argv[]) { src=imread("road.jpg");//读取图像,在当前文件夹下 cvtColor(src,src_gray,CV_RGB2GRAY);//将图像转换成灰度图 namedWindow(sourceimg,CV_WINDOW_AUTOSIZE);//一般在创建控制条前,先创建一个窗口用来放置控制条; createTrackbar("thresh:",sourceimg,&thresh,max_thresh,Harris_demo);//创建一个控制条,用来改变检测角点的阈值; imshow(sourceimg,src); Harris_demo(0,0);//调用回调函数;将<span style="font-family: verdana, Arial, helvetica, sans-seriff;">thresh定义为全局变量,不用传递参数;</span> waitKey(0);//回车结束 return 0; } void Harris_demo(int,void*) { Mat dst,dst_norm,dst_norm_scaled; dst = Mat::zeros(src.size(),CV_32FC1); int blocksize = 2; int kernel =3; double k = 0.04; cornerHarris(src_gray,dst,blocksize,kernel,k,4);//进行harris角点检测 normalize(dst,dst_norm,0,255,NORM_MINMAX,-1,Mat());//对检测的结果进行归一化处理,0到255; convertScaleAbs(dst_norm,dst_norm_scaled);//求其绝对值;这个函数会将结果转换成8bit; for(int j = 0; j < dst_norm.rows ; j++) { for(int i = 0; i < dst_norm.cols ; i++) { if((int) dst_norm.at<float>(j,i) > thresh)//在满足阈值条件的角点处画圆; { circle(src,Point(i,j),5,Scalar(0,0,255),2,8); } } } namedWindow(cornersimg,CV_WINDOW_AUTOSIZE); imshow(cornersimg,src); }阈值thresh的值为80,运行结果:
可以看出,检测出的有些角点粘连在了一起,可以采用膨胀处理进行非极大值抑制。
2、其它Harris角点检测程序
在CSDN上查找资料时,在http://blog.csdn.net/crzy_sparrow/article/details/7391511博客上看到了一个不错的Harris角点检测程序;其先定义了一个harris类,并将具体的检测过程在类的方法中进行实现;有助于理解角点检测的具体过程,而且还能加深对c++类和对象的理解;故将其程序进行整理和重新编写如下:
harris.h
<span style="font-size:14px;">#ifndef HARRIS_H #define HARRIS_H #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace cv; class harris { private: Mat cornerStrength;//opencv harris函数检测的结果,即每个像素点的角点响应函数值; Mat cornerThresh;//cornerStrength阈值化的结果 Mat localMax;//局部最大值的结果 int neighbourhood;//邻域窗口的大小 int aperture;//sobel边缘检测窗口的大小 double k;//cornerHarris函数的系数0.04-0.05 double maxStrength;//角点响应函数的最大值 double thresh;//角点检测的阈值 int nonMaxSize;//最大值抑制的邻域窗口的大小 Mat kernel;//最大值抑制的核,即膨胀用到的核 public: harris():neighbourhood(3),aperture(3),k(0.04),maxStrength(0.0),thresh(0.01),nonMaxSize(3) {} void setLocalMaxWindowsize(int nonMaxSize) { this->nonMaxSize = nonMaxSize; } void detect(const Mat image); Mat getCornerMap(double qualityLevel); void getCorners(vector<Point> &points,double qualityLevel); //void getCorners(vector<Point> &points,const Mat &cornerMap); void drawOnImage(Mat &image,const vector<Point> &points,Scalar color = Scalar(255,255,255), int radius = 3,int thickness = 2); }; #endif</span>
<span style="font-size:14px;"> </span>
</pre><pre>harris.cpp
<span style="font-size:14px;">#include "harris.h" //计算角点的响应函数以及非极大值抑制 void harris::detect(const Mat image) { //用opencv自带的函数求解角点的响应函数 cornerHarris(image,cornerStrength,neighbourhood,aperture,k,4); double minStrength; //求解相应的最大最小值 minMaxLoc(cornerStrength,&minStrength,&maxStrength); Mat dilated; //采用默认的3*3的核膨胀,膨胀之后,除了局部最大点和原来相同,其他非局部最大值 //被3*3邻域内的最大值取代 dilate(cornerStrength,dilated,Mat()); compare(cornerStrength,dilated,localMax,CMP_EQ); } //获取焦点图像 Mat harris::getCornerMap(double qualityLevel) { Mat cornerMap; thresh = qualityLevel*maxStrength; threshold(cornerStrength,cornerThresh,thresh,255,THRESH_BINARY); cornerThresh.convertTo(cornerMap,CV_8U); //和局部最大值与,剩下局部最大值,即完成非极大值抑制,且满足阈值条件 bitwise_and(cornerMap,localMax,cornerMap); return cornerMap; } void harris::getCorners(vector<Point> &points,double qualityLevel) { Mat cornerMap = getCornerMap(qualityLevel); for(int y = 0; y < cornerMap.rows; y++) { //取图像每一行的指针 const uchar* rowPtr = cornerMap.ptr<uchar>(y); for(int x = 0; x < cornerMap.cols; x++) { if(rowPtr[x]) points.push_back(Point(x,y)); } } } void harris::drawOnImage(Mat &image,const vector<Point> &points,Scalar color,int radius,int thickness ) { vector<Point>::const_iterator it = points.begin(); while(it != points.end()) { circle(image,*it,radius,color,thickness); it++; } }</span>
</pre><pre name="code" class="cpp"><span style="font-size:14px;">#include <iostream> #include "harris.h" using namespace std; int main(int argc,char* argv[]) { Mat src,src_gray; src = imread("road.jpg"); cvtColor(src,src_gray,CV_BGR2GRAY); harris Harris1; Harris1.detect(src_gray); //获得角点 vector<Point> POINTS; Harris1.getCorners(POINTS,0.03); //标记角点 Harris1.drawOnImage(src,POINTS,Scalar(0,0,255)); namedWindow("road",CV_WINDOW_AUTOSIZE); imshow("road",src); waitKey(); return 0; }</span>
程序运行的结果如下:
其它角点检测算法和亚像素角点检测见《opencv图像角点提取(二)》。