Moravec算子实现(C++)
程序员文章站
2022-03-27 14:13:32
...
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个像元):
Moravec的代码的关键点是两个窗口的移动,大窗口作为候选窗口,小窗口作为匹配窗口。难点是窗口的边界区域的处理,我把边缘都包括进去了,并且变化设为零。也可以不管边缘,这样在窗口大的时候可能会忽略一些特征点。
另外,从结果上可以看出,Morevec得算子实现好坏不仅仅与窗口大小有关系,还与两个窗口的大小比例有关系。