1、Moravec角点检测算法

一、相关原理

Moravec是最早的角点检测算法之一,它将角点定义为具有低“自相关性”的点。针对图像的每个像素,算法将像素周边的一个邻域作为一个patch,并检测这个patch和周围其他patch的相关性。这种相关性通过两个patch间的SSD(平方差之和)来衡量,SSD值越小则相似性越高。

一幅图像可分为区域、边缘和角点三个部分。如图1所示,如果像素位于平滑图像区域内,周围的patch都会非常相似。如果像素在边缘上,则周围的patch在与边缘正交的方向上会有很大差异,在与边缘平行的方向上则较为相似。而如果像素是各个方向上都有变化的特征点,则周围所有的patch都不会很相似。Moravec计算每个像素patch和周围patch的SSD最小值作为强度值,取局部强度最大的点作为角点。

                                                                                             图1

首先,计算每个像素点的兴趣值,即以该像素点为中心,取一个w*w(如:5x5)的方形窗口,计算0度、45度、90度、135度四个方向灰度差的平方和,取其中的最小值作为该像素点的兴趣值。

E(u,v)=\sum_{x,y} \omega (x,y)\left [ I(x+u,y+v)-I(x,y) \right ]^2      (1.1)

如图2所示,以3x3窗口为例,黑色窗口为I(x,y),红色窗口为I(x+u,y+v),其中四种移位(u,v)= (1,0), (1,1), (0,1), (-1, 1)。w(x,y)为方形二值窗口,若像素点在窗口内,则取值为1,否则为0。

                                                                                               图2

然后,使用非极大值抑制找到局部最大值(此时局部窗口大小可另行设定),将局部最大值与设定的阈值比较,最终确定角点。

此外,度量图像块A与图像块B之间的差异性(或相似性)有以下几种方法:

1)SAD(Sum of Absolute Difference),即两个小块的差的绝对值之和:

S(A,B)_{SAD}=\sum_{i,j}\left | A(i,j) -B(i,j)\right |        (1.2)

2)SSD(Sum of Squared Distance),即两个小块之间差的平方和:

S(A,B)_{SSD}=\sum_{i,j}(A(i,j)-B(i,j))^2       (1.3)

3)NCC(Normalized Cross Correlation,归一化互相关),这种方式比前两种方法要复杂,它计算两个小块的相关性,所以相关性接近0表示不相似,而接近1表示相似。前两种距离形式恰好相反,距离越小越相似。

S(A,B)_{NCC}=\frac{\sum_{i,j}A(i,j)B(i,j)}{\sqrt{\sum_{i,j}A(i,j)^2\sum_{i,j}B(i,j)^2}}        (1.4)

二、基本步骤

输入:源单通道图像、窗口大小、阈值

输出:角点

算法具体步骤:

1)对于每个像素点,计算E(u,v)的值。如果是3*3,(u,v)的值为4种情况,(1,0),(1,1),(0,1),(-1,1);如果是5*5的窗口,(u,v)的取值为8种情况,(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1)。

2)计算E(u,v)的最小值minValue;

3)对于每个位置minValue进行判断,是不是大于设定的阈值,如果大于设定的阈值,就进行非局部极大值抑制,判断是不是局部极大值。如果是局部极大值则为角点,否者不是角点。

算法结束。

                                                                                        图3 算法流程图

注:为什么要进行非局部极大值抑制?因为在一个窗口内,中心点附近的值也可能大于阈值,导致角点聚集现象。所以要在这个窗口内排除这些值,确定局部的最大值,而进行非局部极大值抑制。

三、缺点分析

1、对边缘点的反应比较强烈

强度值的计算并不是各向同性的(各向异性),只有离散的8个角度方向被考虑。moravec算法对角点的定义是:窗口在各个方向的移动,窗口内的灰度值都会产生较大的变化。而其实这里的“各个方向”,最多也就只有8个方向。所以,如果边缘的方向是这8个方向以外的方向,那么,就会被认为是角点。

                                                                                                 图4

2、不具备旋转不变性

因为moravec算法只计算最多八个方向的灰度值差的平方和,所以可能会出现原来的一条边缘在这8个方向之上而旋转之后却不在了,也有可能是相反的情况。这种旋转不变性进一步抑制了moravec算法的实用性。

3、窗口是方形且是二元的

在moravec中,使用的滑动窗口是正方形的、以及窗内的各个像素权重同质性(应该是中心像素权重大,距离中心越远,权重越小),并且窗口值是二元的,在窗口内,窗口值是1,在窗口外,窗口值是0。为了达到精确估计局部灰度值的变化程度,圆形的窗口才是理想的。圆形的窗口,使得中心点到窗口的每一个边缘点的欧式距离基本是相等的,以及赋予离中心更近的点更大的权重。

4、离散点(噪声点)与角点有相同的角点性(cornerness),因此Moravec算子对噪声敏感,但是通过增大滑动窗口的大小可以对噪声起到一定的抑制作用,同时增加了计算量。另一方面,通过设定一个阈值T来对cornerness map进行二值化,小于阈值T的cornerness map设置为0,从而对离散点的局部极大值进行抑制。

5、对于图像的一些边界区域像素,没有计算E(u,v)值。

四、代码分析

注意在实际使用时还要进行非局部极大值抑制,本代码未实现。

/**********************************************************************************
*函数 Mat MoravecCorners(cv::Mat SrcImage, int kSize, int threshold)
*输入:
     *SrcImage : 单通道图像
     *kSize : 窗口尺寸
     *threshold : 阈值 
*输出
     *MorImage : 提取到角点的图像
***************************************************************************************/
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

Mat MoravecCorners(cv::Mat SrcImage, int kSize, int threshold)
{
	Mat MorImage = SrcImage.clone();
	// 获取初始化参数信息
	int r = kSize / 2;
	const int nRows = SrcImage.rows;
	const int nCols = SrcImage.cols;
	int nConut = 0;
	CvPoint *pPoint = new CvPoint[nRows*nCols];
	
	// 图像遍历
	for (int y = r; y < SrcImage.rows - r; y++)//行(y轴--v轴)
	{
		for (int x = r; x<SrcImage.cols - r; x++)//列(x轴--u轴)
		{
			int wV1, wV2, wV3, wV4;
			wV1 = wV2 = wV3 = wV4 = 0;
			// 计算水平(0度)方向窗内兴趣值
			for (int k = -r; k < r; k++)//k=-2,-1,0,1
				wV1 += (SrcImage.at<uchar>(y, x + k) - SrcImage.at<uchar>(y, x + k + 1))*
				(SrcImage.at<uchar>(y, x + k) - SrcImage.at<uchar>(y, x + k + 1));
			// 计算垂直(90度)方向窗内兴趣值
			for (int k = -r; k < r; k++)
				wV2 += (SrcImage.at<uchar>(y + k, x) - SrcImage.at<uchar>(y + k + 1, x))*
				(SrcImage.at<uchar>(y + k, x) - SrcImage.at<uchar>(y + k + 1, x));
			// 计算45度方向窗内兴趣值
			for (int k = -r; k < r; k++)
				wV3 += (SrcImage.at<uchar>(y + k, x + k) - SrcImage.at<uchar>(y + k + 1, x + k + 1))*
				(SrcImage.at<uchar>(y + k, x + k) - SrcImage.at<uchar>(y + k + 1, x + k + 1));
			// 计算135度方向窗内兴趣值
			for (int k = -r; k < r; k++)
				wV4 += (SrcImage.at<uchar>(y + k, x - k) - SrcImage.at<uchar>(y + k + 1, x - k - 1))*
				(SrcImage.at<uchar>(y + k, x - k) - SrcImage.at<uchar>(y + k + 1, x - k - 1));
			// 取其中的最小值作为该像素点的最终兴趣值
			int value = min(min(wV1, wV2), min(wV3, wV4));

			//若兴趣值大于阈值,则将点的坐标存入数组中
			if (value > threshold)
			{
				pPoint[nConut] = cvPoint(x, y);
				nConut++;
			}
		}
	}
	//绘制兴趣点
	cout << "kSize :5*5" << endl;
	cout << "threshold : 5000" << endl;
	cout << "corners :" << nConut << endl;
	for (int i = 0; i < nConut; i++)
		cv::circle(MorImage, pPoint[i], 3, cv::Scalar(255, 0, 0));
	return MorImage;
}

int main()
{
	cv::Mat SrcImage = imread("3.jpg");
	if (!SrcImage.data)
		return -1;
	cv::Mat srcImage;
	cvtColor(SrcImage, srcImage,CV_RGB2GRAY);
	//窗口设置为5*5,阈值设置为5000
	double time0 = static_cast<double>(getTickCount());
	cv::Mat MorImage = MoravecCorners(srcImage, 5, 5000);
	time0 = ((double)getTickCount() - time0) / getTickFrequency();
	cout <<"runtime :"<< time0 << "s" << endl;
	cv::imshow("MorMat", MorImage);
	cv::waitKey(0);
	return 0;
}

输入:lena.jpg,窗口大小5*5,阈值5000

输出:角点检测图(由于没有实现非局部极大值抑制,所以出现角点粘连现象)

                                                                                      图5 实验结果图

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值