Moravec算子实现(C++)


一、Moravec算子原理

考虑某一点与周围像素间的灰度差,以四个方向上具有最小最大灰度方差的点作为特征点。
在这里插入图片描述

① 计算各像元的兴趣值IV(Interest Value)
② k值计算(w是窗口大小)及各个方向兴趣值计算

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

取四个方向中的最小者作为该像元的兴趣值:

在这里插入图片描述

③ 给定一经验阈值,将兴趣值大于阈值的点作为候选点

在这里插入图片描述

④ 选取候选点中的极值点作为特征点

在一定大小的窗口内,将候选点中兴趣值不是最大者均去掉,仅留下一个兴趣值最大者,该像素即为一个特征点
“抑制局部非最大”

二、实现代码

这是我根据Moravec算子原理写的代码,没有用到opencv里面的库函数,因此在各个方面(无论是运行效率还是效果)都感觉有些欠缺,欢迎批评指正。

#include "gdal_priv.h"
#include "cpl_conv.h"
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include<cmath>
#include <string>
#include <vector>
using namespace std;
using namespace cv;

//main(int argc, char *argv[ ], char **env)才是UNIX和Linux中的标准写法。
int main(int argc, char** argv)
{
	void Moravec(Mat image);
	//将所有需要的库函数等,导入进来并改变位数为X64
	//包括VC++目录,C/C++常规、连接器常规等,不同的库路径不同,引入的位置也不同
	string filename = "G:/myself/Major/MathPhotograph/Experiment/Picture02.jpg";//斜杠的方向不能反	//argc: 整数, 用来统计你运行程序时送给main函数的命令行参数的个数
	//* argv[ ]: 指针数组,用来存放指向字符串参数的指针,每一个元素指向一个参数
	//cout << filename << std::endl;	//测试语句
	if (argc > 1)	//如果参数比1大的话,就把第一个参数赋值给filename
	{
		filename = argv[1];	//将参数赋值给filename
		//cout <<"Hey! I'm here."<< filename << std::endl;  //测试语句
	}
	Mat image = imread(filename, IMREAD_GRAYSCALE);	//灰色样式读取图像到Mat中IMREAD_COLOR//IMREAD_GRAYSCALE//IMREAD_UNCHANGED
	if (image.empty())	//如果为空
	{
		std::cout << "Could not open or find the image." << std::endl;
		return -1;
	}
	//namedWindow("Display window", WINDOW_AUTOSIZE);	//建立大小固定的窗体显示图像,可删除
	//imshow("Display window", image);
	std::cout << "图像的类型编号为:" << image.type() << endl;	//网上有类型对应的编号
	std::cout << "图像的通道数量为:" << image.channels() << endl;

	//计算各个像元的兴趣值
	Moravec(image);
	cv::waitKey(0);//等待用户需要多长时间毫秒,零意味着永远等待
	return 0;
}

//共有两层窗口移动:候选窗口移动(整个)、待求窗口在候选窗口中移动(一行一行)
void Moravec(Mat image)
{
	int rows = image.rows - 1, cols = image.cols - 1;
	int CandidateWin = 25, window = 7;	//候选区窗口大小,移动窗口大小
	int candiPositionR = 0, candiPositionC = 0, i, j, g;	//row行,col列,i候选区行,j候选区列
	int k = int(window / 2);	//窗口中心像元

	while (candiPositionR + CandidateWin < rows - window || candiPositionC < cols - window)	//当候选区窗口的坐标小于图像坐标时执行
	{
		//---------------------------临界条件,比for循环多考虑了边缘-------------------------------
		//临界条件,神志不清地写了个换行操作
		if (candiPositionC == cols - window)	//列到达边缘时,换行操作,这个要放在上面以免换行溢出
		{
			candiPositionR = candiPositionR + CandidateWin - window;
			candiPositionC = 0;
		}
		if (candiPositionC + CandidateWin  > cols && candiPositionC + CandidateWin < cols + CandidateWin)		//列溢出时
			candiPositionC = cols - CandidateWin;	//退回相应的列数
		if (candiPositionR + CandidateWin  > rows && candiPositionR + CandidateWin < rows + CandidateWin)		//当行溢出时
			candiPositionR = rows - CandidateWin;	//退回相应的行数
		//--------------------------------------------------分界线---------------------------------------------------------------------	

		long winMax = 0;	//候选窗口最大值以及最大值坐标
		int maxPositionR = 0, maxPositionC = 0;

		for (i = candiPositionR; i <= candiPositionR + CandidateWin - window; i++)	//i表示待求窗口在候选窗口中的移动位置
		{
			//加上窗口大小减去待选窗口大小,只能在这里面蹦跶
			for (j = candiPositionC; j <= candiPositionC + CandidateWin - window; j++)
			{
				//计算V1
				int V[4] = { 0, 0, 0, 0 };	//四个方向
				for (g = -k; g <= k - 1; g++)
				{
					V[0] = V[0] + pow((image.at<uchar>(i + k + g, j + k) - image.at<uchar>(i + k + g + 1, j + k)), 2);
					//cout << int(image.at<uchar>(i + k + g, j + k)) << int(image.at<uchar>(i + k, j + k + g + 1)) << endl;
					V[1] = V[1] + pow((image.at<uchar>(i + k + g, j + k + g) - image.at<uchar>(i + k + g + 1, j + k + g + 1)), 2);
					V[2] = V[2] + pow((image.at<uchar>(i + k, j + k + g) - image.at<uchar>(i + k, j + k + g + 1)), 2);
					V[3] = V[3] + pow((image.at<uchar>(i + k + g, j + k - g) - image.at<uchar>(i + k + g + 1, j + k - g - 1)), 2);
					//cout << V1 <<V2<<V3<<V4<< endl;	//测试语句
				}

				//取四个方向中的最小者作为该像元的兴趣值
				int Vmin = V[0];
				if (V[1] < Vmin) Vmin = V[1];
				if (V[2] < Vmin) Vmin = V[2];
				if (V[3] < Vmin) Vmin = V[3];
				//找到整个候选窗口中最大的值
				if (Vmin > winMax)
				{
					winMax = Vmin;
					maxPositionR = i + k;
					maxPositionC = j + k;
				}
			}
		}
		if (winMax > 3000)
		{
			std::cout << maxPositionR << "  " << maxPositionC << endl;
			std::cout << "winMax = " << winMax << endl;
			Point pt = Point(maxPositionC, maxPositionR);

			circle(image, pt, 3, Scalar(255, 0, 0));
		}
		//候选窗口所在的位置加上窗口大小,减去代求窗口边长,原因:候选窗口最边上得几行几列覆盖不到
		candiPositionC = candiPositionC + CandidateWin - window;
	}
	imshow("Moravec算子", image);
	cv::waitKey(0);
	std::cout << "执行完毕!" << endl;
}

三、结果及分析

代码结果如下(小窗口:7个像元; 大窗口:25个像元):

小窗口:7;  大窗口:25

Moravec的代码的关键点是两个窗口的移动,大窗口作为候选窗口,小窗口作为匹配窗口。难点是窗口的边界区域的处理,我把边缘都包括进去了,并且变化设为零。也可以不管边缘,这样在窗口大的时候可能会忽略一些特征点。

| 大窗口:13,小窗口:5 | 大窗口:13,小窗口:7 |大窗口:25,小窗口:7 |

另外,从结果上可以看出,Morevec得算子实现好坏不仅仅与窗口大小有关系,还与两个窗口的大小比例有关系。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值