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角点检测程序
在优快云上查找资料时,在http://blog.youkuaiyun.com/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>
main.cpp
</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图像角点提取(二)》。