Harris角点检测
1角点特点
角点:简单理解就是在两个明显不同方向上梯度有明显变化的点,就是在水平、竖直两个方向上变化均较大的点,即Ix、Iy都较大。
边缘:若仅仅在某一个方向上梯度明显变化,即仅在水平、或者仅在竖直方向有较大的变化量,即Ix和Iy只有其一较大 则该点为边界上的点,
平坦地区:若任何方向上梯度都没有明显变化,即在水平、竖直方向的变化量均较小,即Ix、Iy都较小,则该点在光滑区域的内部。
2 角点检测启发
现在我们从零来思考角点检测问题,我们可以想到用窗口对图像进行分割:
1)若该窗口在光滑连续区域中,则在较小的范围内,无论按照哪个方向移动窗口,窗口内对应位置的像素灰度值变化较小;
2)若窗口沿着场景边界方向,则窗口内的像素灰度值变化较小,若垂直于边界方向移动,则窗口内对应位置的像素灰度值变化较大;
3)若该点为角点或者孤立点,则无论窗口沿哪个方向,窗口内对应位置的像素灰度值变化均较大。
3 Harris角点检测算法原理和步骤
1)Harris角点检测算法基本步骤:
1. Harris角点检测算法,该算法的基本原理是取以目标像素点为中心的一个小窗口,计算窗口沿任何方向移动后的灰度变化,并用解析形式表达。设以像素点(x,y)为中心的小窗口在X方向上移动u,y方向上移动v,Harris给出了灰度变化度量的解析表达式:
I为图像灰度函数,略去无穷小项有:
表示限定某一个窗口内的像素点为1其余为0,其本质就是各个方向滑动窗口来求取其对应位置灰度值的差异。这里(x,y)用来表示方向向量,例如(1.0),(1,1),(0,1)等表示水平方向,45°方向和竖直方向,用来表示窗口的移动(shift).这样做的缺点较多,例如方向(x,y)的选择,很容易造成各个方向不同的能量惩罚,这里称为'anisotropic'; 另外更重要的是其对边界太敏感,容易误检测。
将其化为二次型有:
其中,R为旋转因子,对角化处理后并不改变以u,v为坐标参数的空间曲面的形状,其特征值反应了两个主轴方向的图像表面曲率。
(1)当两个特征值均较小时,表明目标点附近区域为“平坦区域”;
(2)特征值一大一小时,表明特征点位于“边缘”上;
(3)当两个特征值均比较大时,沿任何方向的移动均将导致灰度的剧烈变化,为角点。
Harris的角点响应函数(CRF)表达式由此而得到:
其中:det(M)表示矩阵M的行列式,det(M)=λ1*λ2 ;trace(M)表示矩阵迹,trace(M)=λ1+λ2; k=0.04~0.06;当目标像素点的CRF值大于给定的阈值时,该像素点即为角点。
响应函数公式推导如下:
总的来说,对于边界情况,理想的情况是,和中一个为0,另外一个很大,当然事实上,由于噪声,通常是一个很大,另外一个相比较而言会足够小。对于光滑连续区域,则和 都很小,仅当 和 都很大时候,命中角点区域。
此时我们希望有关于E的新能量方程,使在角点处的E较大,而在边界处和光滑处E较小,其中ηi为权值系数,且η2>η1即意味着对边界的惩罚力度较大:
而所有需要计算的特征值用A与B进行相应的计算即可,这个很省事。
这里有个地方需要说明,根据A,B与C的定义可知,A*B-C*C=0
这样一来似乎没有计算Det(M)的必要,然而我们在实际操作过程中,由于噪声的存在,常常用Gaussian对窗口 进行平滑,此时计算中Det(M)不为0.
即:2 strong eigenvalues——interest point,1 strong eigenvalues——contour/edge
0 eigenvalues ——uniform region
2 编程实现
基本步骤:
参考文献
[1].HARRIS, C. ; STEPHENS, M.: A Combined Corner and Edge Detector. In:Proceedings of the 4th Alvey Vision Conference, 1988, S. 147—151
[2].MORAVEC, H, OBSTACLE AVOIDANCE AND NAVIGATION IN THE REAL WORLD BY A SEEINGROBOT ROVER, TECH REPORT CMU-RI-TR-3, CARNEGIE-MELLON UNIVERSITY, ROBOTICSINSTITUTE, SEPTEMBER 1980.
3 代码附件
1)MATLAB代码部分:
%对图像的角点进行提取,方便找到最大值/最小值的位置,但是计算局部最大值的速度并不快
%输入: 源图像 Y
%输出:源图像和对应角点
clc
clear all
Y=zeros(100,100);
Y(26:75,26:75)=1;
X=Y;
if ISRGB(X)
X=rgb2gray(Y);
end;
X=double(X);
[m n]=size(X);
I_x=zeros(m,n);
I_y=I_x;
h=fspecial('sobel')
I_x=imfilter(X,h,'replicate');
I_y=imfilter(X,h','replicate');
I_x2=I_x.*I_x;
I_xy=I_x.*I_y;
I_y2=I_y.*I_y;
k=0.05;
h=fspecial('gaussian',3,0.5) ;
grad_x=imfilter(I_x2,h,'replicate');
grad_xy=imfilter(I_xy,h,'replicate');
grad_y=imfilter(I_y2,h,'replicate');
for i=1:m %求 M 矩阵
for j=1:n
M=[grad_x(i,j)grad_xy(i,j);grad_xy(i,j) grad_y(i,j)];
R(i,j)=det(M)-k*trace(M);
end
end
R2=R;
R3=R;
%开始寻找局部最大值的角点,本质是行与行比较,列于列比较
local=2;%局部区域大小,默认2*2,j即该区域内最大值
sstep=8;%移动步长,每次移动的长度
step=local;
tic;
Src=R;
[m,n]=size(Src);
temp=Src;
compareMat=[temp,temp(:,end-local+1:end-1)];%补全,易于操作
indexMat=Src;
for i=1:local-1;%默认情况在2×2区域寻找最大值
index=(compareMat(:,1:end-i)>compareMat(:,i+1:end));
indexMat=double(index(1:m,1:n)).*indexMat(1:m,1:n);
end
compareMat=[indexMat;indexMat(end-local+1:end-1,:)];%补全,易于操作
for i=1:local-1;
index=(compareMat(1:end-i,:)>compareMat(i+1:end,:));
indexMat=double(index(1:m,1:n)).*indexMat(1:m,1:n);
end
toc;
R=indexMat;
N=10;%角点个数
S_R=sort(R(find(R~=0)));%选取最大值
th=S_R(length(S_R)-N+1);%设定阈值
R(find(R<th))=0;
R(find(R~=0))=1;
[findy,findx]=find(R~=0);
figure;
imshow(Y);
hold on
plot(findx,findy,'o','MarkerEdgeColor','m','MarkerFaceColor','m','MarkerSize',3); %显示出角点
hold off
效果图:2)opencv代码:
#include"stdafx.h"
#include"cv.h"
#include"highgui.h"
voiddrawcross(CvArr* img,CvPoint2D32f pt)
{
const int radius=3;
int ptx=cvRound(pt.x);
int pty=cvRound(pt.y);
int ls=ptx-radius;
int re=ptx+radius;
int us=pty-radius;
int de=pty+radius;
cvLine(img,cvPoint(ls,pty),cvPoint(re,pty),CV_RGB(0,0,255),1,0);
cvLine(img,cvPoint(ptx,us),cvPoint(ptx,de),CV_RGB(0,0,255),1,0);
}
int main(intargc,char*argv[])
{
CvPoint2D32f pt[100];
int cornercount=30;
IplImage* srcimg=cvLoadImage("2.bmp");
IplImage*grayimg=cvCreateImage(cvGetSize(srcimg),IPL_DEPTH_8U,1);
IplImage*eigimg=cvCreateImage(cvGetSize(srcimg),IPL_DEPTH_32F,1);
IplImage* tempimg=cvCloneImage(eigimg);
//cvConvertImage(srcimg,grayimg,0);
cvCvtColor(srcimg,grayimg,CV_BGR2GRAY);
cvGoodFeaturesToTrack(grayimg,eigimg,tempimg,pt,&cornercount,0.1,10,NULL,3,0,0.04);
for(int i=0;i"cornerdetection",CV_WINDOW_AUTOSIZE);
cvShowImage("cornerdetection",srcimg);
cvWaitKey(0);
return 0;
}
不适用opencv的代码(转)
//////////////////////////////////////////////////////////////////////
//Construction/Destruction
//////////////////////////////////////////////////////////////////////
#defineB(image,x,y) ((uchar *)(image->imageData+image->widthStep*(y)))[(x)*3]
#defineG(image,x,y) ((uchar *)(image->imageData+image->widthStep*(y)))[(x)*3+1]
#defineR(image,x,y) ((uchar *)(image->imageData+image->widthStep*(y)))[(x)*3+2]
#defineS(image,x,y) ((uchar *)(image->imageData+image->widthStep*(y)))[(x)]
//卷积计算求Ix,Iy,以及滤波
//a指向的数组是size1*size2大小的...求导
CvMat*mbys(CvMat *mat,int xwidth,int ywidth,double*a,intsize1,intsize2)
{
int i,j;
int i1,j1;
int px,py;
int m;
CvMat *mat1;
mat1=cvCloneMat(mat);
for(i=size1/2;ifor(j=size2/2;jfor(i1=0;i1for(j1=0;j1//CV_MAT_ELEM访问矩阵元素
m+=CV_MAT_ELEM(*mat,double,px,py)*a[i1*size1+j1];
}
CV_MAT_ELEM(*mat1,double,i,j)=m;
}
returnmat1;
}
//计算Ix2,Iy2,Ixy
CvMat*mbxy(CvMat *mat1,CvMat *mat2,int xwidth,int ywidth)
{
int i,j;
CvMat *mat3;
mat3=cvCloneMat(mat1);
for(i=0;ifor(j=0;jdouble,i,j)=CV_MAT_ELEM(*mat1,double,i,j)*CV_MAT_ELEM(*mat2,double,i,j);
}
returnmat3;
}
//用来求得响应度
CvMat*mbcim(CvMat *mat1,CvMat *mat2,CvMat *mat3,int xwidth,intywidth)
{
int i,j;
CvMat *mat;
mat=cvCloneMat(mat1);
for(i = 0; i for(j= 0; j < xwidth; j++)
{
//注意:要在分母中加入一个极小量以防止除数为零溢出
CV_MAT_ELEM(*mat,double,i,j)=(CV_MAT_ELEM(*mat1,double,i,j)*CV_MAT_ELEM(*mat2,double,i,j)-
CV_MAT_ELEM(*mat3,double,i,j)*CV_MAT_ELEM(*mat3,double,i,j))/
(CV_MAT_ELEM(*mat1,double,i,j)+CV_MAT_ELEM(*mat2,double,i,j)+0.000001);
}
}
return mat;
}
//用来求得局部极大值
CvMat*mblocmax(CvMat *mat1,int xwidth,int ywidth,int size)
{
int i,j;
double max=-1000;
int i1,j1;
int px,py;
CvMat *mat;
mat=cvCloneMat(mat1);
for(i=size/2;ifor(j=size/2;jfor(i1=0;i1for(j1=0;j1if(CV_MAT_ELEM(*mat1,double,px,py)>max)
max=CV_MAT_ELEM(*mat1,double,px,py);
}
if(max>0)
CV_MAT_ELEM(*mat,double,i,j)=max;
else
CV_MAT_ELEM(*mat,double,i,j)=0;
}
returnmat;
}
//用来确认角点
CvMat*mbcorner(CvMat *mat1,CvMat *mat2,int xwidth,intywidth,intsize,doublethresh)
{
CvMat *mat;
int i,j;
mat=cvCreateMat(ywidth,xwidth,CV_32FC1);
for(i=size/2;ifor(j=size/2;jif(CV_MAT_ELEM(*mat1,double,i,j)==CV_MAT_ELEM(*mat2,double,i,j))//首先取得局部极大值
if(CV_MAT_ELEM(*mat1,double,i,j)>thresh)//然后大于这个阈值
CV_MAT_ELEM(*mat,int,i,j)=255;//满足上两个条件,才是角点!
else
CV_MAT_ELEM(*mat,int,i,j)=0;
}
returnmat;
}
CvPoint*CHarris::harris_features(IplImage *src,int gausswidth,doublesigma,intsize,intthreshold)
{
CvMat*mat_I,*mat_Ix,*mat_Iy,*mat_Ixy,*mat_Ix2,*mat_Iy2;//相应的矩阵
IplImage *pImgGray=NULL; //灰度图像
IplImage *dst=NULL; //目标图像
IplImage *pImgDx=NULL; //水平梯度卷积后的图像
IplImage *pImgDy=NULL; //竖起梯度卷积后的图像
IplImage *pImgDx2=NULL;//Ix2图像
IplImage *pImgDy2=NULL;//Iy2图像
IplImage *pImgDxy=NULL;//Ixy图像
pImgGray=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
dst=cvCreateImage(cvGetSize(src),src->depth,3);
pImgDx=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);//创建图像
pImgDy=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
pImgDx2=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
pImgDy2=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
pImgDxy=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
const int cxDIB=src->width; // 图像宽度
const intcyDIB=src->height; //图像高度
double *I=newdouble[cxDIB*cyDIB];
cvCvtColor(src,pImgGray,CV_RGB2GRAY);//灰度化
dst=cvCloneImage(src);
int i,j;
for(j=0;jfor(i=0;i//将灰度图像数值存入I中
}
mat_I=cvCreateMat(cyDIB,cxDIB,CV_64FC1);
cvInitMatHeader(mat_I,cyDIB,cxDIB,CV_64FC1,I);//用I来初始化相应的矩阵
// cout<<CV_MAT_ELEM(*mat_I,double,200,200)<<endl;
//--------------------------------------------------------------------------
// 第一步:利用差分算子对图像进行滤波
//--------------------------------------------------------------------------
//定义水平方向差分算子并求Ix
double dx[9]={-1,0,1,-1,0,1,-1,0,1};
mat_Ix=mbys(mat_I,cxDIB,cyDIB,dx,3,3); //求Ix矩阵
// cout<<CV_MAT_ELEM(*mat_Ix,double,200,200)<<endl;
//定义垂直方向差分算子并求Iy
doubledy[9]={-1,-1,-1,0,0,0,1,1,1};
mat_Iy=mbys(mat_I,cxDIB,cyDIB,dy,3,3);//求Iy矩阵
// cout<<CV_MAT_ELEM(*mat_Iy,double,200,200)<<endl;
for(j=0;jfor(i=0;idouble,j,i);//为相应图像赋值
S(pImgDy,i,j)=CV_MAT_ELEM(*mat_Iy,double,j,i);
}
mat_Ix2=mbxy(mat_Ix,mat_Ix,cxDIB,cyDIB);//计算Ix2,Iy2,Ixy矩阵
mat_Iy2=mbxy(mat_Iy,mat_Iy,cxDIB,cyDIB);
mat_Ixy=mbxy(mat_Ix,mat_Iy,cxDIB,cyDIB);
for(j=0;jfor(i=0;idouble,j,i);//为相应图像赋值
S(pImgDx2,i,j)=CV_MAT_ELEM(*mat_Ix2,double,j,i);
S(pImgDy2,i,j)=CV_MAT_ELEM(*mat_Iy2,double,j,i);
}
//--------------------------------------------------------------------------
// 第二步:对Ix2/Iy2/Ixy进行高斯平滑,以去除噪声
//--------------------------------------------------------------------------
//本例中使用5×5的高斯模板
//计算模板参数
//int gausswidth=5;
//double sigma=0.8;
double *g=newdouble[gausswidth*gausswidth];
for(i=0;i//定义模板
for(j=0;jint(gausswidth/2))*(i-int(gausswidth/2))+(j-int(gausswidth/2))*(j-int(gausswidth/2)))/(2*sigma));
//归一化:使模板参数之和为1(其实此步可以省略)
double total=0;
for(i=0;ifor(i=0;ifor(j=0;j//进行高斯平滑
mat_Ix2=mbys(mat_Ix2,cxDIB,cyDIB,g,gausswidth,gausswidth);
mat_Iy2=mbys(mat_Iy2,cxDIB,cyDIB,g,gausswidth,gausswidth);
mat_Ixy=mbys(mat_Ixy,cxDIB,cyDIB,g,gausswidth,gausswidth);
//--------------------------------------------------------------------------
// 第三步:计算角点量
//--------------------------------------------------------------------------
//计算cim:即cornerness of image,我们把它称做‘角点量’
CvMat *mat_cim;
mat_cim=mbcim(mat_Ix2,mat_Iy2,mat_Ixy,cxDIB,cyDIB);
// cout<<CV_MAT_ELEM(*mat_cim,double,cyDIB-1,cxDIB-1)<<endl;
//--------------------------------------------------------------------------
// 第四步:进行局部非极大值抑制
//--------------------------------------------------------------------------
CvMat *mat_locmax;
//const int size=7;
mat_locmax=mblocmax(mat_cim,cxDIB,cyDIB,size);
// cout<<CV_MAT_ELEM(*mat_locmax,double,cyDIB-1,cxDIB-1)<<endl;
//--------------------------------------------------------------------------
// 第五步:获得最终角点
//--------------------------------------------------------------------------
CvMat *mat_corner;
//const doublethreshold=4500;
//int cornernum=0;
mat_corner=mbcorner(mat_cim,mat_locmax,cxDIB,cyDIB,gausswidth,threshold);
//CCommon CommonClass;
CvPoint pt[5000];
for(j=size/2;jfor(i=size/2;iif(CV_MAT_ELEM(*mat_corner,int,j,i)==255)
{
pt[cornerno].x=i;
pt[cornerno].y=j;
cornerno++;
// CommonClass.DrawCross(showImg2,pt,CV_RGB(0,0,255),1,4);
// cvCircle(dst,pt,2,CV_RGB(255,0,0),1,8,0);
// cout<<i<<""<<j<<endl;
}
}
return pt;
}