一、Moravec角点检测算法原理
Moravec角点检测算法的思想是: 在图像中设计一个局部检测窗口,当该窗口沿各个方向作微小移动时 ,
考查窗口的平均能量变化,当该能量变化值超过设定的阈值时,就将窗口的中心像素点提取为角点。
此检测窗口可以是3*3,也可以是5*5。现在就以3*3为例,至于平均能量用兴趣值来表示。计算每个像素
点的兴趣值,就要在3*3的窗口中计算0度,45度,90度,135度四个方向灰度差的平方和,取其中最小值作为
该像素点的兴趣值,具体公式为:
其中(u,v)是窗口移动的位移,(x,y)表示该窗口内的像素点的坐标,w(x,y)表示方形的二值窗口,若像
素点在窗口内,则取值为1,否则为0。四种移位(u,v)=(1,0),(1,1),(0,1),(-1,1),具体移位如图:
二、角点检测的步骤
1.对于每个像素点,计算在E(u,v)的值,如果是3*3,(u,v)的值是(1,0),(1,1),(0,1),(-1,1)
如果是5*5的窗口,(u,v)的取值是(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),
(0,-1),(1,-1)8种情况。
2.计算E(u,v)的最小值minValue
3,.对于每个位置minValue进行判断,是不是大于设定的阈值,如果大于设定的阈值,就进行非局部
极大值抑制,判断是不是局部极大值。
为什么要进行非局部极大值抑制?
因为在一个窗口内,中心点附近的值也可能大于阈值,但是要在这个窗口内排除这些值,确定局部的最大
值,所以要进行非局部极大值抑制。
三、具体代码如下
/**********************************************************************************
*函数 int getMoravec(IplImage* src,CvSeq* corners)
*输入:
*src : 单通道图像
*corners : 用来保存提取到的角点
*threshold : 角点量的阈值 (具体函数 请看视频)
*输出
*corners : 用来保存提取到的角点
*返回值
*角点的个数
***************************************************************************************/
int getMoravec(IplImage* src,CvSeq* corners , float threshold)
{
//窗口大小
const int winSize=5;
int x,y,halfWinSize=winSize/2;
//保存最小的变化量
IplImage* diffDst = cvCreateImage(cvGetSize(src),32,1);
cvZero(diffDst);
//保存角点个数
int cornersCount=0;
//计算图像上每一个点上的 在4个方向上的变化量 并计算出最小值 保存在矩阵diffDst中
for(y=halfWinSize;y
{
for(x=halfWinSize;x
{
//compute the reaction in the four directions(0,45,90,135)
int winx;
//数组reaction[4] 用于保持 在四个方向上的灰度值变化量
//minValue用于保存4个变化量中的最小值
float reaction[4],minValue;
reaction[0]=0;
reaction[1]=0;
reaction[2]=0;
reaction[3]=0;
//提示 下面的4个循环 可以综合成一个循环
//0 度方向的变化量保存在reaction[0]中
for( winx=-halfWinSize;winx
reaction[0] = reaction[0]+pow(cvGetReal2D(src,y,x+winx)-cvGetReal2D(src,y,x+winx+1),2);
}
//45 度方向的变化量保存在reaction[1]中
for( winx=-halfWinSize;winx
reaction[1] = reaction[1]+pow(cvGetReal2D(src,y+winx,x+winx)-cvGetReal2D(src,y+winx+1,x+winx+1),2);
}
//0 度方向的变化量保存在reaction[2]中
for( winx=-halfWinSize;winx
reaction[2] = reaction[2]+pow(cvGetReal2D(src,y+winx,x)-cvGetReal2D(src,y+winx+1,x),2);
}
//135 度方向的变化量保存在reaction[3]中
for( winx=-halfWinSize;winx
reaction[3] = reaction[3]+pow(cvGetReal2D(src,y+winx,x-winx)-cvGetReal2D(src,y+winx+1,x-winx-1),2);
}
//计算4个量中最小值 保存到minValue
minValue = reaction[0];
minValue = minValue > reaction[1] ? reaction[1] : minValue;
minValue = minValue > reaction[2] ? reaction[2] : minValue;
minValue = minValue > reaction[3] ? reaction[3] : minValue;
//将最小的变化量保存到矩阵
cvSetReal2D(diffDst,y,x,minValue);
}
}
//获取角点坐标 判断当前点的局部极大值
for(y=halfWinSize;y
{
for(x=halfWinSize;x
{
float max=0;
int flag = 0 ;
CvPoint maxLoc;
maxLoc.x = -1;
maxLoc.y = -1;
//首先计算以点(x,y)位中心的winSize*winSize的窗口内部的局部极大值
for(int winy=-halfWinSize;winy<=halfWinSize;winy++)
{
for(int winx=-halfWinSize;winx<=halfWinSize;winx++)
{
float value ;
value = cvGetReal2D(diffDst,y+winy,x+winx);
//计算该窗口内 最大值 保存到max 并保存其坐标到maxLoc
if(value>max)
{
max = value;
maxLoc.x = x+winx;
maxLoc.y = y+winy;
flag = 1;
}
}
}
//如果找到局部极大值 并且该值大于预先设定的阈值 则认为是角点
if(flag==1 && max>threshold)
{
cvSeqPush(corners,&maxLoc);
cornersCount++;
}
//下一个窗口
x=x+halfWinSize;
}
//下一行的第一个窗口
y=y+halfWinSize;
}
cvReleaseImage(&diffDst);
return cornersCount;
}
四、moravec角点检测缺点分析及检测
moravec有两个重要的缺点:
1.它不具备旋转不变性
2.对边缘点的反应比较强烈
moravec只计算了一些离散的偏移产生的灰度值变化(最多是8个方向),如图下所示,旋转之后,检测到角点不一样了。moravec统计的8个方向(最多是8个方向)是0°,45°,90°,135°,180°,225°,270°,315°。如果图像上的边缘上的某个点,在旋转前刚好是处于45°方向,此时检测到不是角点(因为窗口在45°方向平移的时候,窗口内部像素值变化肯定很小),当图像旋转10°,此时该点处在55°,那么现在就很有可能变成了一个角点(因为窗口只有在55°方向平移的时候,窗口内部的灰度值变化才会取到最小值,而55°不在moravec算法的统计之内)。具体如图:
下图中,是用3*3的窗口,对图像进行morave角点检测。虽然moravec算法能把所有角点都检测出来,但是把边缘上面的很多点也作为角点了,对于这种情况,我们应该是可以理解的。moravec算法对角点定义是:窗口在各个方向的移动,窗口内的灰度值都会产生较大的变化。而其实这里的“各个方向“,最多也就只有8个方向。所以,如果边缘的方向,是这8个方向以外的方向,那么,就会被认为是角点。
为了达到精确估计局部灰度值的变化程度,圆形的窗口才是理想的。圆形的窗口,使得中心点到窗口的每一个边缘点的欧式距离基本是相等的。如图,左图是7*7窗口,右图是半径为7的圆形窗口,可以看出,对于圆形窗口,从中心点到各个边缘点之间的欧式距离更接近。这样更有利于评估局部灰度值的变化程度。为什么呢?“评估局部灰度值的变化程度“,注意这句话里的一个关键词”局部“,那么什么是局部?如果我们使用7*7的正方形窗口,那么这个正方形窗口就是局部,我们的评估局部灰度值的变化程度的时候,这49个元素都会参与运算;当我们使用半径为7的圆形窗口的时候,那么这个半径为7的圆形窗口就是局部,在这个圆形窗口内的灰度值都会参与运算。
通常,我们认为,里中心点更近的点,对该点影响也会大一些。既然离得比较近的点对中心点的影响比较大,那么我们在评估局部灰度值的变化程度的时候,应该赋予离中心更近的点更大的权重才是合理的,而不是moravec使用的窗口值,只要在窗口内部,不管远近,权重都是1.
通过上面的分析,既要使用圆形的窗口,又要赋予离中心更近的点更大的权重,那么高斯窗口就是比较好的一个选择了。5*5的高斯窗口如图所示:虽然看起来的也是方形的,但是它在四个角落上的权重是非常小的,可以近似的认为是0,那么就可以认为是一个圆形的窗口,并且,权重是离中心点越近变得越大。