OpenCV快速入门—C++实现Moravec算子(内附OpenCV配置方法)

简介

  • OpenCV:全称是Open Source Computer Vision Library(即开源计算机视觉库),由英特尔公司开发。OpenCV可用于开发实时的数字图像处理计算机视觉以及模式识别程序。[参考文档:https://wikipedia.org/wiki/OpenCV]

OpenCV提供了多种接口,可跨平台开发,本文主要介绍OpenCV的C++使用。

  • Moravec算子:Moravec算子作为最早也是最经典的点特征提取算子,在计算机视觉摄影测量领域多有应用。它的改进版Harris算子可以提取出大量稳定的点特征(或称角点)

tips:因为Moravec算子较为简单,所以希望读者能够通过学习Moravec算子的实现打开使用OpenCV的大门。


实现步骤

(已经能使用OpenCV的读者可以跳过一、二,直接阅读三)

一、安装与配置OpenCV
  • 1. 进入OpenCV官网,点击“Release”下载对应版本的opencv,本文使用的OpenCV版本为OpenCV 4.1.1,系统为Windows,这里赋上官网链接:https://opencv.org
  • 2. 下载完成后,将opencv解压到对应位置(我安装在D:\OpenCV4.1.1)中。安装具体步骤不再赘述
  • 3. 配置OpenCV。(以Windows10为例)
    (1) 右键“此电脑”打开属性,在“高级系统设置”中配置点击“环境变量”进行配置
    (2) 在“系统变量”中新建一个名为“Opencv_Loc”(名字可自由选择)的变量,值设置为“你安装的OpenCV路径\opencv\build\x64\vc15
    (3) 在Path变量中添加“%Opencv_Loc%\bin
    (4) 在Visual Studio 2017中配置OpenCV。具体步骤:

设置项目属性,在“C/C++”-常规-附加包含目录中添加

$(Opencv_Loc)\..\..\include

在“链接器”-常规-附加库目录中添加

$(Opencv_Loc)\lib

在“链接器”-输入-附加依赖项中添加

opencv_world411.lib //Release模式下添加这段
opencv_world411d.lib //Debug模式下添加这段

[参考文档:https://www.cnblogs.com/shlll/archive/2018/02/06/8424692.html]

  • 4. 在要用到OpenCV的地方添加上
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2\opencv.hpp"
using namespace cv;//或 使用cv::对应函数
二、使用OpenCV读取显示图像

tips:OpenCV中的Mat类型数据可以看成一个矩阵,OpenCV将图像读入存到Mat类型数据里

//读取图像使用imread函数
Mat m_srcimg = imread('图像路径');
//显示图像使用imshow函数
imshow("src image", m_srcimg); 
waitKey(); //等待操作,将窗口关闭\回车\ESC等操作会运行以下的程序
三、编写Moravec算子
  • Step1: 计算兴趣值

Moravec算子对图像的每个像素点都会计算一个兴趣值,这里我们用OpenCV的Mat类型数据来存放。

Mat Interest=Mat::zeros(srcImg.rows, srcImg.cols, CV_64F);//用于存放兴趣值 double

这段代码表示创建一个名为“Interest”的Mat变量,初始化为0,矩阵大小与原始图像矩阵大小保存一致(即srcImg.rows, srcImg.cols)rows表示图像高,cols表示图像宽。而CV_64F表示存放的数据为double类型。

某一像素点的兴趣值定义为:在以该像素为中心的窗口中,四个方向相邻像素灰度差的平方和的最小值。
(四个方向分别为0°、45°、90°和135°,窗口大小需为大于等于3的奇数)
example:

若像素点(x,y)的灰度值为g(x,y),窗口大小为3
则四个方向的兴趣值分别为:
V1=[g(x-1, y )-g(x,y)]²+[g(x,y)-g(x+1, y )]²  
V2=[g(x-1,y-1)-g(x,y)]²+[g(x,y)-g(x+1,y+1)]²  
V3=[g( x ,y-1)-g(x,y)]²+[g(x,y)-g( x ,y+1)]²  
V4=[g(x-1,y+1)-g(x,y)]²+[g(x,y)-g(x+1,y-1)]²  
 
若窗口大小为5,则从g(x-2,y),g(x-2,y-2),g(x,y-2),g(x-2,y+2)开始算起。

该像素兴趣值取最小值为min{V1,V2,V3,V4}
  • Step2: 选取候选点

将兴趣值大于某个经验阈值的像素点作为候选角点。这里同样用Mat类型数据来存放。(函数中的const double T为阈值)

Mat Candidate(srcImg.rows, srcImg.cols, srcImg.type());//候选点

srcImg.type()表示与原始灰度图一样的数据类型

  • Step3: 选候选点中的极值点作为特征点(抑制局部非最大)

将给定窗口大小内兴趣值最大的点作为特征点。这是Moravec算子的最后一步,也是其他点特征提取算子的最后一步。

注意:
1.这里的窗口大小可不同于计算兴趣值的窗口大小。
2.只对窗口的中心点进行判断。即若窗口中心点的兴趣值为窗口内最大,则作为特征点,而窗口其他点兴趣值为最大,则暂时不做判断。

下面给出代码全文:
【函数变量说明:srcImg为原始灰度图,iwindowsize为计算兴趣值窗口大小,ichecksize为抑制局部非最大窗口大小,T为经验阈值,IntrPtVec为输出的特征点】

//Morevec算子
void MorevecOperator(const Mat srcImg, const int iwindowsize,const int ichecksize, const double T, vector<Point2f> &IntrPtVec)
{
	Mat Interest=Mat::zeros(srcImg.rows, srcImg.cols, CV_64F);//用于存放兴趣值 double
	Mat Candidate(srcImg.rows, srcImg.cols, srcImg.type());//候选点
	//计算各像素点兴趣值
	for (int i = iwindowsize / 2; i < srcImg.rows /*- 1*/ - iwindowsize / 2; i++)//因rows cols从1起算
	{
		for (int j = iwindowsize / 2; j < srcImg.cols /*- 1*/ - iwindowsize / 2; j++)
		{
	        double V1, V2, V3, V4;
			V1 = V2 = V3 = V4 = 0;
			for (int iw = -1 * iwindowsize / 2; iw <= iwindowsize / 2 - 1; iw++)
			{
				V1 += pow((double(srcImg.at<uchar>(i + iw, j))- double(srcImg.at<uchar>(i + iw + 1, j))),2);
				V2 += pow((double(srcImg.at<uchar>(i + iw, j + iw)) - double(srcImg.at<uchar>(i + iw + 1, j + iw + 1))), 2);
				V3 += pow(double(srcImg.at<uchar>(i, j + iw))-double(srcImg.at<uchar>(i, j + iw + 1)), 2);
				V4 += pow((double(srcImg.at<uchar>(i + iw, j - iw)) - double(srcImg.at<uchar>(i + iw + 1, j - iw - 1))), 2);
			}
			double nMinV = V1 >= V2 ? V2 : V1;
			nMinV = nMinV >= V3 ? V3 : nMinV;
			nMinV = nMinV >= V4 ? V4 : nMinV;
			Interest.at<double>(i, j) = nMinV;
			//获取候选点
			if (Interest.at<double>(i, j) >=T)
			{
				Candidate.at<uchar>(i, j) = 0;
			}
			else
			{
				Candidate.at<uchar>(i, j) = 255;
			}
		}
	}
	
	//抑制局部非最大
	Point2f pt;
	Mat Img = srcImg;
	for (int i = ichecksize / 2; i < srcImg.rows - ichecksize / 2; i++)
	{
		for (int j = ichecksize / 2; j < srcImg.cols - ichecksize / 2; j++)
		{
			//只搜索候选点
			if (Candidate.at<uchar>(i, j) == 0)
			{
				//抑制局部非最大窗口内
			   //ix iy为相对中心点的偏移量
				double nMax = 0;//窗口内最大兴趣值
				//查找窗口内兴趣值最大的点
				for (int iy = -ichecksize / 2; iy < ichecksize / 2; iy++)
				{
					for (int ix = -ichecksize / 2; ix < ichecksize / 2; ix++)
					{
						if (Interest.at<double>(i + iy, j + ix) > nMax)
						{
							nMax = Interest.at<double>(i + iy, j + ix);
						}
					}
				}
				//若中心点为最大
				if (Interest.at<double>(i, j) == nMax)
				{
					circle(Img, Point(j, i), 3, RGB(0, 0, 0));
					pt.x = j;
					pt.y = i;
					IntrPtVec.push_back(pt);
				}
			}
		}
	}
}

四、结果展示

<1>原始灰度图:

<2>候选点图(黑色为候选点):

<3>提取结果

  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值