Moravec角点检测原理及C++实现

(1)Moravec角点检测算法原理
Moravec角点检测算法是最早的角点检测算法之一。该算法将角点定义为具有低“自相关性”的点。算法会检测图像的每一个像素,将像素周边的一个邻域作为一个patch,并检测这个patch和周围其他patch的相关性。这种相关性通过两个patch间的平方差之和(SSD)来衡量,SSD值越小则相似性越高。
如果像素位于平滑图像区域内,周围的patch都会非常相似。如果像素在边缘上,则周围的patch在与边缘正交的方向上会有很大差异,在与边缘平行的方向上则较为相似。而如果像素是各个方向上都有变化的特征点,则周围所有的patch都不会很相似。
Moravec会计算每个像素patch和周围patch的SSD最小值作为强度值,取局部强度最大的点作为特征点。

(2)Moravec角点检测示意图:


(3)将moravec角点检测方法公式化:
Moravec 在1981年提出Moravec角点检测算子,并将它应用于立体匹配。
首先, 计算每个像素点的兴趣值, 即以该像素点为中心, 取一个w*w(如:5x5)的方形窗口, 计算0度、45度、90度、135度四个方向灰度差的平方和, 取其中的最小值作为该像素点的兴趣值。


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

(4)moravec角点检测步骤:
(1)对于每一个像素点,计算在E(u,v),在我们的算法中,(u,v)的取值是((1,0), (1,1), (0,1), (-1, 1).当然,你自己可以改成(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1) 8种情况
(2)计算最小值对每个位置minValue = min{E(u,v)},其中(u,v) = (1,0), (1,1), (0,1), (-1, 1).

(3)对每个位置minValue 进行判断,是不是大于设定阈值,如果是大于设定阈值,接着判断是不是局部极大值


下面放上源代码:

 
 
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
//***********moravec角点检测函数*******************
//第一个参数srcImage表示输入的图
//第二个参数表示核的边长
//第三个参数表示设定的阈值的大小
//*************************************************
Mat MoravecCorners(Mat srcImage, int kSize, int threshold);

int main()
{
	Mat srcImage = imread("1.jpg");
	if (!srcImage.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	Mat srcGray;
	cvtColor(srcImage, srcGray, CV_BGR2GRAY);
	Mat resMorMat = MoravecCorners(srcGray, 5, 10000);
	imshow("srcGray", srcGray);
	imshow("resMorMat", resMorMat);
	waitKey(0);
	return 0;
}
Mat MoravecCorners(Mat srcImage, int kSize, int threshold)
{
	//角点检测的结果图
	Mat resMorMat = srcImage.clone();
	int r = kSize / 2;
	//获取图像的高和宽
	const int nRows = srcImage.rows;
	const int nCols = srcImage.cols;
	int nCount = 0;
	//保存角点的坐标
	CvPoint *pPoint = new CvPoint[nRows*nCols];
	//遍历图像
	for (int i = r; i < srcImage.rows - r; i++)
	{
		for (int j = r; j < srcImage.cols - r; j++)
		{
			int wV1, wV2, wV3, wV4;
			wV1 = wV2 = wV3 = wV4 = 0;
			//计算水平方向窗内的兴趣值
			for (int k = -r; k <= r; k++)
			{
				for (int m = -r; m <= r; m++)
				{
					//判断移动的过程中是否越界,越界的话就跳过当前的循环,以免出错
					int a = i + k ;
					int b = j + m + 1;
					if (b>=srcImage.cols)
					{
						continue;
					}
					wV1 += (srcImage.at<uchar>(i + k, j + m + 1) - srcImage.at<uchar>(i + k, j + m))
						*(srcImage.at<uchar>(i + k, j + m + 1) - srcImage.at<uchar>(i + k, j + m));
				}
			}

			//计算垂直方向窗内的兴趣值
			for (int k = -r; k <= r; k++)
			{
				for (int m = -r; m <= r; m++)
				{
					int a = i + k + 1;
					int b = j + m ;
					if (a >=srcImage.rows)
					{
						continue;
					}
					wV2 += (srcImage.at<uchar>(i + k + 1, j + m) - srcImage.at<uchar>(i + k, j + m))
						*(srcImage.at<uchar>(i + k + 1, j + m) - srcImage.at<uchar>(i + k, j + m));
				}
			}

			//计算45°方向窗内的兴趣值
			for (int k = -r; k <= r; k++)
			{
				for (int m = -r; m <= r; m++)
				{
					int a = i + k + 1;
					int b = j + m + 1;
					if (a >=srcImage.rows || b >=srcImage.cols)
					{
						continue;
					}
					wV3 += (srcImage.at<uchar>(i + k + 1, j + m + 1) - srcImage.at<uchar>(i + k, j + m))
						*(srcImage.at<uchar>(i + k + 1, j + m + 1) - srcImage.at<uchar>(i + k, j + m));
				}
			}

			//计算135°方向窗内的兴趣值
			for (int k = -r; k <= r; k++)
			{
				for (int m = -r; m <= r; m++)
				{
					int a = i + k + 1;
					int b = j + m - 1;
					if (a >=srcImage.rows || b < 0)
					{
						continue;
					}
						
					wV4 += (srcImage.at<uchar>(a, b) - srcImage.at<uchar>(i + k, j + m))
						*(srcImage.at<uchar>(a, b) - srcImage.at<uchar>(i + k, j + m));
				}
			}
			int  value = min(min(wV1, wV2), min(wV3, wV4));
			//如果兴趣值大于阈值,那么将坐标存入数组中
			if (value > threshold)
			{
				pPoint[nCount] = cvPoint(j, i);
				nCount++;
			}
		}
	}
	//绘制兴趣点
	for (int i = 0; i < nCount; i++)
	{
		circle(resMorMat, pPoint[i], 5, Scalar(255, 0, 0));
	}
	return resMorMat;
}


 
 

原图的灰度图:



角点检测效果图:



这张图片因为没有经过极大值抑制,好多角点检测到的重复了。你们可以加上极大值抑制

猜你喜欢

转载自blog.csdn.net/linqianbi/article/details/78924923