Harris角点检测

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给出了灰度变化度量的解析表达式:

Harris角点检测_第1张图片

其中,为窗口内的灰度变化度量;为窗口函数,一般定义为

I为图像灰度函数,略去无穷小项有:


    表示限定某一个窗口内的像素点为1其余为0,其本质就是各个方向滑动窗口来求取其对应位置灰度值的差异。这里(x,y)用来表示方向向量,例如(1.0),(1,1),(0,1)等表示水平方向,45°方向和竖直方向,用来表示窗口的移动(shift).这样做的缺点较多,例如方向(x,y)的选择,很容易造成各个方向不同的能量惩罚,这里称为'anisotropic'; 另外更重要的是其对边界太敏感,容易误检测。      

将其化为二次型有:



Harris角点检测_第2张图片

其中,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即意味着对边界的惩罚力度较大:

Harris角点检测_第3张图片

而所有需要计算的特征值用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


Harris角点检测_第4张图片

Harris角点检测_第5张图片

2 编程实现

基本步骤:




Harris角点检测_第6张图片

Harris角点检测_第7张图片

参考文献
[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

效果图:

Harris角点检测_第8张图片

2opencv代码:

#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;

}

Harris角点检测_第9张图片

不适用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;

}

 


你可能感兴趣的:(Harris角点检测)