欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Moravec算子实现(C++)

程序员文章站 2022-03-27 14:13:32
...


一、Moravec算子原理

考虑某一点与周围像素间的灰度差,以四个方向上具有最小最大灰度方差的点作为特征点。
Moravec算子实现(C++)

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

Moravec算子实现(C++)
Moravec算子实现(C++)
Moravec算子实现(C++)
Moravec算子实现(C++)
Moravec算子实现(C++)

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

Moravec算子实现(C++)

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

Moravec算子实现(C++)

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

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

二、实现代码

这是我根据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个像元):

Moravec算子实现(C++)

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

Moravec算子实现(C++)

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