一、程序内容
1.以灰度的形式读入图像;
2.开辟和图像大小的矩阵,用于保存兴趣值;
3.计算每个像元的兴趣值;
4.用阈值对分割兴趣值,获取候选点,
5.对候选点抑制局部极值。
6.将特征提取结果输出到文本,并将特征点画在原始图像上。
二、设计思路
1.CMoravec类的设计
私有数据成员:兴趣值矩阵,兴趣值计算窗口大小(默认5X5),抑制局部非最大窗口大小(默认5X5),兴趣经验阈值(默认200)。
私有成员函数:将算法的每一步设计为一个函数:计算兴趣值矩阵,选择候选点,抑制局部极值,标记特征点。
共有成员函数:Moravec算法主函数(在主函数中进行影像的高斯滤波,并调用私有成员函数)。
三、主要代码
//Moravec.h:头文件
#pragma once
#include "opencv2\highgui\highgui.hpp"
using namespace cv;
class CMoravec
{
public:
CMoravec(void);
~CMoravec(void);
private:
Mat Interest; //兴趣值矩阵
int IVwindowsize; //兴趣值计算窗口大小
int NMSwindowsize; //抑制局部非最大窗口大小
int intvalueT; //兴趣阈值
private:
void InterestValue(const Mat srcimg); //计算兴趣值
void CandidatePoint(); //选择候选点
void FeaturePoint (); //抑制局部非最大
void Mark (Mat &dstimg); //标记特征点
public:
void Moravec(const Mat srcimg,Mat &dstimg);// Moravec算法主函数
};
//Moravec.cpp: 实现文件
#include "StdAfx.h"
#include "Moravec.h"
#include "math.h"
#include "ZQLImgPro.h"
CMoravec::CMoravec(void)
{
IVwindowsize=5;
NMSwindowsize=5;
intvalueT=200;
}
CMoravec::~CMoravec(void)
{
}
//计算兴趣值
void CMoravec::InterestValue(const Mat srcimg)
{
Interest.create(srcimg.rows,srcimg.cols,CV_32S);
int rows=srcimg.rows;
int cols=srcimg.cols;
for (int i=0;i<rows;i++)
{
for (int j=0;j<cols;j++)
{
Interest.at<int>(i,j)=0;
//将兴趣值矩阵初始化为零矩阵
}
}
int k=int(IVwindowsize/2);
for (int r=k;r<rows-k;r++)
{
for (int c=k;c<cols-k;c++)
{
int v1=0;
int v2=0;
int v3=0;
int v4=0;
for (int i=-k;i<k;i++)
{
v1+=(srcimg.at<uchar>(r,c+i)-srcimg.at<uchar>(r,c+i+1))*
(srcimg.at<uchar>(r,c+i)-srcimg.at<uchar>(r,c+i+1));
v2+=(srcimg.at<uchar>(r+i,c+i)-srcimg.at<uchar>(r+i+1,c+i+1))*
(srcimg.at<uchar>(r+i,c+i)-srcimg.at<uchar>(r+i+1,c+i+1));
v3+=(srcimg.at<uchar>(r+i,c)-srcimg.at<uchar>(r+i+1,c))*
(srcimg.at<uchar>(r+i,c)-srcimg.at<uchar>(r+i+1,c));
v4+=(srcimg.at<uchar>(r-i,c+i)-srcimg.at<uchar>(r-i-1,c+i+1))*
(srcimg.at<uchar>(r-i,c+i)-srcimg.at<uchar>(r-i-1,c+i+1));
}
Interest.at<int>(r,c)=min(min(min(v1,v2),v3),v4);
//取v1,v2,v3,v4中的最小值作为兴趣值
}
}
}
//选择候选点
void CMoravec::CandidatePoint()
{
for (int i=0;i<Interest.rows;i++)
{
for (int j=0;j<Interest.cols;j++)
{
//选择特征值大于经验阈值的点作为候选点
if (Interest.at<int>(i,j)<=intvalueT)
Interest.at<int>(i,j)=0;
}
}
}
//抑制局部非最大
void CMoravec::FeaturePoint()
{
int halfw=NMSwindowsize/2;
for (int i=halfw;i<Interest.rows-halfw;i++)
{
for (int j=halfw;j<Interest.cols-halfw;j++)
{
for (int m=-halfw;m<halfw+1;m++)
{
for (int n=-halfw;n<halfw+1;n++)
{
//抑制局部极值
if (Interest.at<int>(i,j)<Interest.at<int>(i+m,j+n))
Interest.at<int>(i,j)=0;
}
}
}
}
}
//标记特征点
void CMoravec::Mark(Mat &dstimg)
{
FILE *fp=fopen("result.txt","w");
if (fp == NULL)
{
return;
}
fprintf(fp,"%s\t%s\n","像素位置","兴趣值");
Point2f pt;
for (int i=3;i<Interest.rows-3;i++)
{
for (int j=3;j<Interest.cols-3;j++)
{
if (Interest.at<int>(i,j)!=0)
{
circle(dstimg,Point(j,i),1,cvScalar(0,0,255),-1);
//将特征点标记为实心点
fprintf(fp,"(%1i,\t%1i)\t%1i\n",i,j,Interest.at<int>(i,j));
//特征提取结果输出到文件
}
}
}
fclose(fp);
//显示影像
imshow(_T("结果影像"),dstimg);
cvWaitKey(0);
}
//Moravec算法主函数
void CMoravec::Moravec(const Mat srcimg,Mat &dstimg)
{
CZQLImgPro CIP(3,1.5);
Mat Gauss;
CIP.Gaussianfilter(srcimg,Gauss);// 高斯滤波
InterestValue(Gauss);
CandidatePoint();
FeaturePoint();
Mark(dstimg);
}
// ZQL_0107150120_2Dlg.h : 头文件
//
#pragma once
#include "opencv2\core\core.hpp"
#include "opencv2\highgui\highgui.hpp"
#include "Moravec.h"
using namespace cv;
using namespace std;
// CZQL_0107150120_2Dlg 对话框
class CZQL_0107150120_2Dlg : public CDialogEx
{
public:
bool bRead; //判断是否读取影像文件
Mat m_srcimg; //原始影像灰度矩阵
Mat m_dstimg; //结果影像矩阵
CString str_srcimgfile; //原始影像路径
CString str_dstimgfile; //结果影像路径
afx_msg void OnBnClickedBtnMoravec(); //Moravec算法特征点提取
afx_msg void OnBnClickedBtnOpen(); //打开影像
afx_msg void OnBnClickedBtnSave(); //保存结果影像
afx_msg void OnBnClickedCancel(); //退出对话框
};
// ZQL_0107150120_2Dlg.cpp : 实现文件
void CZQL_0107150120_2Dlg:: OnBnClickedBtnMoravec ()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
if (bRead!=TRUE)
{
MessageBox(_T("请读入影像文件"));
return;
}
CMoravec CM;
CM.Moravec(m_srcimg,m_dstimg);
}
void CZQL_0107150120_2Dlg::OnBnClickedBtnOpen()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
CFileDialog FileDlg(TRUE,"*.jpg;*.bmp","*.jpg;*.bmp",OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT," 影像文件()");
if (FileDlg.DoModal()!=IDOK)
{
return;
}
str_srcimgfile=FileDlg.GetPathName();
bRead=TRUE; //标记已经读入影像
UpdateData(FALSE); //将影像名字显示到对话框中
m_srcimg=imread(str_srcimgfile.GetBuffer(),CV_LOAD_IMAGE_GRAYSCALE); //打开影像
m_dstimg=imread(str_srcimgfile.GetBuffer(),CV_LOAD_IMAGE_COLOR); //打开影像
imshow(_T("原始影像"),m_srcimg); //图像显示
cvWaitKey();
//等待鼠标响应
}
void CZQL_0107150120_2Dlg::OnBnClickedBtnSave()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog FileDlg(FALSE,"*.jpg;*.bmp","*.jpg;*.bmp",OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT," 影像文件()");
if (FileDlg.DoModal()!=IDOK)
{
return;
}
str_dstimgfile=FileDlg.GetPathName(); //用于保存结果
if (str_dstimgfile!="")
{
imwrite(str_dstimgfile.GetBuffer(),m_dstimg);// 将结果影像写入文件
}
UpdateData(FALSE);
m_dstimg.release();
}
void CZQL_0107150120_2Dlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
CDialogEx::OnCancel();
}
四、运行结果
对话框
阈值40
阈值200
阈值200
五、注意事项
1.Morevec算法会提取出影像中的噪声,在进行特征提取前,先进行高斯滤波处理,降低噪声,得到平滑的影像。
2.以灰度的形式读取原始影像的同时,再以彩色的形式读取一次影像,在该影像上进行标记,便可以显示彩色标记。
3.经过高斯平滑后的影像进行特征提取时,会在影像边缘出现特征点。进行高斯滤波时,在影像边缘填充了灰度值为0的像素,导致特征提取算法提取到了错误的特征点。
解决办法:设高斯滤波的窗口大小为windowsize,进行特征点标记时,循环从int(windowsize/2)+1开始,
到rows-(int(windowsize/2)+1)或cols-(int(windowsize/2)+1)结束。