Morevec算子特徵提取 opencv C++ CSU

承接實驗一

實驗配置 C++ vs2017 opencv

實驗2 Morevec算子特徵提取

一、實驗目的

  • 編寫Morevec算子特徵提取程序,掌握特徵提取方法,用標準圖像進行測試

二、實驗內容與要求

1 以灰度的形式讀入圖像;

2 開闢和圖像大小的矩陣,用於保存興趣值;

3 計算每個像元的興趣值;

4 用閾值對分割興趣值,獲取候選點,

5 對候選點抑制局部極值。

6 將特徵提取結果輸出到文本,並將特徵點畫在原始圖像上。

三、設計與實現

3.1類的設計

在這裏插入圖片描述

3.2代碼及其屬性

在這裏插入圖片描述

按鈕ID CADPTION 對應函數
IDOK 打開並顯示灰度圖像 OnBnClickedOk()
IDC_read2 二值化圖像 OnBnClickedread2()
IDC_read3 Guass濾波/opencv的Guass濾波圖像 OnBnClickedread3()
IDC_read5 Moravec算子 OnBnClickedread5()
IDC_read4 幫助 OnBnClickedread4()
IDCANCEL 取消

3.3主要代碼

這裏僅展示涉及到Moravec算子相關功能的代碼。

3.3.1文件 < zrxCFeatureVector.h >

#pragma once
/***************************************************************************
類:zrxCFeatureVector
作用:Moraavec算子類 外接函數:Moravec()
Welcome to my Github and my CSDN blog , more information will be available about the project!
Github:https://github.com/Yiqingde
CSDN Blog:https://me.csdn.net/weixin_42348202
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
using namespace cv;
using namespace std;
class zrxCFeatureVector
{
private:
	CString strOut;//存儲輸出txt中內容
	void find(float a[], int m, float &max, float &min);//最大最小值
	float Moravec_core(Mat m_srcimg, int Moravecsize, int i, int j);//Moravec_core滑動窗口
	void Moravec_core2(Mat &m_srcimgrgb,int Moravecsize2, Mat &Morvec, vector<Point3i> &f);//Moravec_core2局部抑制函數
public:
	zrxCFeatureVector();
	~zrxCFeatureVector();
	void Reporttxt(CString temp);//輸出至txt函數
	void Moravec(Mat m_srcimg, Mat &m_srcimgrgb, int Moravecsize, int Moravecsize2, Mat &Morvec);//Moravec主要函數,調用上面的兩個函數
};

3.3.2文件 < zrxCFeatureVector.cpp >

#include "stdafx.h"
#include "zrxCFeatureVector.h"


zrxCFeatureVector::zrxCFeatureVector()
{
}


zrxCFeatureVector::~zrxCFeatureVector()
{
}

/***************************************************************************
函數:Reporttxt()
作用:輸出結果至txt,用到該類中變量strOut
參數:無
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
void zrxCFeatureVector::Reporttxt(CString runtime)

{
	if (strOut == _T("")) { AfxMessageBox(_T("請先輸入數據!")); }
	else {
		CFileDialog dlg(false, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("Txt Files(*.txt)|*.txt|All Files(*.*)|*.*|"), AfxGetMainWnd());
		CString strPath;
		if (dlg.DoModal() == IDCANCEL) return;
		else
		{
			strPath = dlg.GetPathName();

			if (strPath.Find(_T(".txt")) < 0)
			{
				if (strPath.Find(_T(".dat")) < 0)
				{
					strPath += _T(".txt");//默認儲存爲txt模式
				}
			}
		}
		TRY
		{
			CString cs;
			CStdioFile file(strPath, CFile::shareExclusive | CFile::modeWrite | CFile::modeCreate);
			setlocale(LC_CTYPE, ("chs")); //設置中文輸出
			file.WriteString(runtime);
			file.WriteString(strOut);
			file.Close();
			CString temp;
			temp.Format(_T("%s%s"),_T("點位信息已導出至"),strPath);
			AfxMessageBox(temp);
		}
			CATCH_ALL(e)
		{
			e->ReportError();
			return;
		}
		END_CATCH_ALL
	}
}

/***************************************************************************
函數:find()
作用:尋找數組的最大值 最小值
參數:float a[]  數組
      int m 數組前m個元素
	  float &max 最大值
	  float &min 最小值
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
void zrxCFeatureVector::find(float a[], int m, float &max, float &min)
{
	min = a[0];
	max = a[0];
	for (int i = 0; i < m; i++)
	{
		if (a[i] > max)
		{
			max = a[i];
			continue;
		}
		else if (a[i] < min)
		{
			min = a[i];
			continue;
		}
	}
}


/***************************************************************************
函數:Moravec_core()
作用:滑動窗口 求"*"狀最小值
參數:Mat m_srcimg 原始圖像
	  int Moravecsize 窗口大小
	  int i_x 像素(x,y)中的x
	  int j_y 像素(x,y)中的y
返回值:最小值
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
float zrxCFeatureVector::Moravec_core(Mat m_srcimg, int Moravecsize, int i_x, int j_y)
{
	int halfsize = (Moravecsize) / 2;//定義小半個窗口大小
	float temp4[4];//創立四個方向的數組來進行"*"型的平方和
	//數組初始化
	for (int i = 0; i < 4; i++)
	{
		temp4[i] = 0;
	}
	//累加*求平方和
	for (int i = 0; i < Moravecsize; i++)
	{
		float l = m_srcimg.at<uchar>(i_x - halfsize + i, j_y);//    | x方向 即北南
		temp4[0] += pow(m_srcimg.at<uchar>(i_x - halfsize + i, j_y) - m_srcimg.at<uchar>(i_x - halfsize + i + 1, j_y), 2);//    | x方向 即北南
		temp4[1] += pow(m_srcimg.at<uchar>(i_x, j_y - halfsize + i) - m_srcimg.at<uchar>(i_x, j_y - halfsize + i + 1), 2);//    - y方向
		temp4[2] += pow(m_srcimg.at<uchar>(i_x - halfsize + i, j_y - halfsize + i) - m_srcimg.at<uchar>(i_x - halfsize + i + 1, j_y - halfsize + i + 1), 2);//   \ 方向
		temp4[3] += pow(m_srcimg.at<uchar>(i_x - halfsize + i, j_y + halfsize - i) - m_srcimg.at<uchar>(i_x - halfsize + i + 1, j_y + halfsize - i - 1), 2);//   / 方向
	}
	float min, max;//定義兩個極大值 極小值
	find(temp4, 4, max, min);//給極小值賦值
	return min;//返回極小值
}


/***************************************************************************
函數:Moravec_core2()
作用:局部抑制 並開始畫圖
參數:Mat m_srcimgrgb 原始彩色圖像
	  int Moravecsize2 局部抑制窗口大小
	  Mat &Morvec 經之前Moravec窗口操作後並經過閾值後的圖像
	  vector<Point3i> &f 用來存儲展點
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
void zrxCFeatureVector::Moravec_core2( Mat &m_srcimgrgb,int Moravecsize2, Mat &Morvec, vector<Point3i> &f)
{
	int halfMoravecsize2 = Moravecsize2 / 2;//定義小半個窗口大小
	//四層循環作用:針對窗口除了邊框的每一個點來確保以它爲中心,只要存在有一個值不小於它的值 即給它做一個標記0 用來排除小於極大值的值
	for (int i = halfMoravecsize2; i < Morvec.rows - halfMoravecsize2 - 1; i++)
	{
		for (int j = halfMoravecsize2; j < Morvec.cols - halfMoravecsize2 - 1; j++)
		{
			//int tag = 0;
			float temp1 = Morvec.at<float>(i, j);
			for (int m = 0; m < Moravecsize2; m++)
			{

				for (int n = 0; n < Moravecsize2; n++)
				{
					float temp2 = Morvec.at<float>(i - halfMoravecsize2 + m, j - halfMoravecsize2 + n);
					if (temp1 < temp2)
					{
						Morvec.at<float>(i, j) = 0;
						//Morvec.at<float>(i - halfMoravecsize2 + m, j - halfMoravecsize2 + n) = -1;
						n = Moravecsize2;
						m = Moravecsize2;
					}

				}
				//if (tag = 1) { break; }
			}
		}
	}
	//開始進行存儲至 vector<Point3i> &f 中
	for (int i = halfMoravecsize2; i < Morvec.rows - halfMoravecsize2 - 1; i++)
	{
		for (int j = halfMoravecsize2; j < Morvec.cols - halfMoravecsize2 - 1; j++)
		{

			if (Morvec.at<float>(i, j) > 0)
			{
				Point3i temp;
				temp.x = i;
				temp.y = j;
				temp.z = Morvec.at<float>(i, j);
				f.push_back(temp);
			}
		}
	}
	
	//開始刪除同意一窗口值一樣的重複點,儘管出現概率較小,但較大的圖像往往某些窗口中會存在好幾個數值相等的極大值
	for (int i = 0; i < f.size() - 1; i++)
	{
		for (int j = i + 1; j < f.size(); j++)
		{
			if ((f.at(i).z == f.at(j).z))
			{
				if (abs(f.at(i).x - f.at(j).x) < Moravecsize2 || abs(f.at(i).y - f.at(j).y) < Moravecsize2)
				{
					f.erase(f.begin() + j);
					i = 0;
					break;
				}
			}
		}
	}
	/*
	//*********************冒泡排序
	for (int i = 0; i < f.size()-1; i++) {
		int tem = 0;
		// 內層for循環控制相鄰的兩個元素進行比較
		for (int j = i + 1; j < f.size(); j++) {
			if (f.at(i).z < f.at(j).z) {
				tem = f.at(j).z;
				f.at(j).z = f.at(i).z;
				f.at(i).z = tem;
			}
		}
	}
	*/
	/*********************以下注釋的D部分 本來是爲了修改顯示的值 如果大於50 只顯示前50個最大值,須於冒泡排序結合使用,但是效果不太好
	*/
	CString temp;
	temp.Format(_T("%s%d%s\n%s\n"),_T("*************************一共提取出:"),f.size(),_T("個點*************************"),_T("X座標,Y座標,Morevec後的值"));
	strOut += temp;
	
	//*D int tag2 = (f.size() > 50) ? 50 : f.size();
	for (size_t i = 0; i < f.size(); i++)
	{
		circle(m_srcimgrgb, Point(int(f.at(i).y), int(f.at(i).x)), 5, Scalar(0, 0, 255),1.67);
		temp.Format(_T("%d,%d,%d\n"), int(f.at(i).x), int(f.at(i).y), int(f.at(i).z));
		strOut += temp;
	}
}


/***************************************************************************
函數:Moravec()
作用:Moeavec主函數 調用以上函數
參數:Mat m_srcimg 原始圖像
      Mat m_srcimgrgb 原始彩色圖像
	  int Moravecsize  * 型窗口大小
	  int Moravecsize2 局部抑制窗口大小
	  Mat &Morvec 經之前Moravec窗口操作後並經過閾值後的圖像
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019926日        創建              ***
/**************************************************************************/
void zrxCFeatureVector::Moravec(Mat m_srcimg, Mat &m_srcimgrgb, int Moravecsize, int Moravecsize2,Mat &Morvec)
{
	//****************************存儲點的信息 查bug用
	vector<Point3i> f;
	GaussianBlur(m_srcimg, m_srcimg, Size(5, 5), 0, 0);//使用opencv自帶高斯濾波預處理
	Morvec.create(m_srcimg.rows, m_srcimg.cols, CV_32FC1);//開闢空間
	float sum = 0;
	for (int i = 5; i < m_srcimg.rows - 5; i++)
	{
		for (int j = 5; j < m_srcimg.cols - 5; j++)
		{
			float min = Moravec_core(m_srcimg, Moravecsize, i, j);
			Morvec.at<float>(i, j) = min;
			sum += min;
		}
	}
	//****************************對小於閾值的置爲零
	float mean = sum / (Morvec.rows*Morvec.cols);
	//if (mean < 60) { mean = 300; }
	for (int i = 0; i < Morvec.rows; i++)
	{
		for (int j = 0; j < Morvec.cols; j++)
		{
			if (Morvec.at<float>(i, j) < mean)
			{
				Morvec.at<float>(i, j) = 0;
			}
		}
	}
	//****************************局部抑制並顯示
	Moravec_core2(m_srcimgrgb, Moravecsize2, Morvec, f);
	Morvec.convertTo(Morvec, CV_8UC1);
	//Morvec2.convertTo(Morvec2, CV_8UC1);
}

3.3.3文件 < CBasic.h >

#pragma once
/***************************************************************************
類:CBasic
作用:封裝圖像操作函數,僅儲存按鈕操作功能 調用相應其他圖片處理類來完成圖片操作
Welcome to my Github and my CSDN blog , more information will be available about the project!
Github:https://github.com/Yiqingde
CSDN Blog:https://me.csdn.net/weixin_42348202
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
#include "time.h"
#include "zrxCImgPro.h"
#include "zrxCFeatureVector.h"
class CBasic
{
public:
	CBasic();
	~CBasic();
	Mat m_srcimg;//原始灰度圖像
	Mat m_srcimgrgb;//RGB顏色的圖像
	bool tag;//魯棒性
	zrxCImgPro function1;//創建zrxCImgPro圖像處理操作對象
	zrxCFeatureVector function2;//創建zrxCFeatureVector圖像處理操作對象
public://********************************存儲按鈕功能函數
	void OpenImg();//實現按鈕功能,打開圖像
	void Button_binaryImg();//實現按鈕功能,顯示二值圖像
	void Button_GaussImg();//實現按鈕功能,包括顯示自寫與opencv的gauss圖像,並顯示運行時間
	void Button_Help(); //實現按鈕功能,help
	void Button_Moravec();//Moravec按鈕
};

3.3.4文件 < CBasic.cpp >

#include "stdafx.h"
#include "CBasic.h"


CBasic::CBasic()
{
	this->tag = 0;//魯棒
}


CBasic::~CBasic()
{
}
/***************************************************************************
函數:OpenImg()
作用:實現按鈕功能,打開圖像並顯示灰度圖像,存儲到 m_srcimg中; tag是標籤、增加程序魯棒性
參數:無
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月20日        創建              ***
/**************************************************************************/
void CBasic::OpenImg()
{
	CFileDialog FileDlg(TRUE, "*.jpg;*.bmp", "*.jpg;*.bmp", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "影像文件()");
	if (FileDlg.DoModal() != IDOK)
	{
		return;
	}
	CString strImgName = FileDlg.GetPathName();
	//打開影像	
	m_srcimg = imread(strImgName.GetBuffer(), IMREAD_GRAYSCALE);//IMREAD_GRAYSCALE  以灰度形式打開
	m_srcimgrgb = imread(strImgName.GetBuffer(), IMREAD_COLOR);//IMREAD_COLOR  以灰度形式打開
	//resize(m_srcimg, m_srcimg, Size(m_srcimg.cols * 800 / m_srcimg.rows, 800), 0, 0, INTER_CUBIC);//自適應調整圖像大小
	//resize(m_srcimgrgb, m_srcimgrgb, Size(m_srcimgrgb.cols * 800 / m_srcimgrgb.rows, 800), 0, 0, INTER_CUBIC);//自適應調整圖像大小
	imshow("原始圖像", m_srcimgrgb); //顯示
	tag = 1;
}


/***************************************************************************
函數:Button_binaryImg()
作用:實現按鈕功能,顯示二值圖像
參數:無
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月20日        創建              ***
/**************************************************************************/
void CBasic::Button_binaryImg()
{
	if (tag == 0)
	{
		AfxMessageBox(_T("需首先打開影像才能進行操作!"));
		return;
	}
	//二值化操作
	Mat m_binary;
	function1.BinaryImage(m_srcimg, 100, m_binary);
	imshow("圖像二值化結果", m_binary); //顯示
	waitKey();
}


/***************************************************************************
函數:Button_GaussImg()
作用:實現按鈕功能,包括顯示自寫與opencv的gauss圖像,並顯示運行時間
參數:無
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月20日        創建              ***
/**************************************************************************/
void CBasic::Button_GaussImg()
{
	if (tag == 0)
	{
		AfxMessageBox(_T("需首先打開影像才能進行操作!"));
		return;
	}
	Mat distImg;
	clock_t start, finish;
	double totaltime;
	clock_t start1, finish1;
	double totaltime1;
	start = clock();
	function1.Gaussianfilter(m_srcimg, 5, 1.0, distImg);
	//namedWindow("ss", 1);
	imshow("高斯濾波結果", distImg); //顯示
	finish = clock();
	totaltime = (double)(finish - start) / CLOCKS_PER_SEC;

	waitKey(100); //等待操作,將窗口關閉\回車\ESC等操作會運行以下的程序
	//opencv自帶高斯濾波
	Mat dstImage;
	start1 = clock();
	GaussianBlur(m_srcimg, dstImage, Size(5, 5), 0, 0);
	//顯示效果圖
	imshow("opencv自帶高斯濾波效果圖", dstImage);
	finish1 = clock();
	totaltime1 = (double)(finish1 - start1) / CLOCKS_PER_SEC;

	waitKey(3000);
	CString runtime;
	runtime.Format("%s%f%s\r\n%s%f%s",
		_T("自寫Gauss濾波運行時間:"),
		totaltime,
		_T("s"),
		_T("opencvGauss濾波運行時間:"),
		totaltime1,
		_T("s")
	);
	AfxMessageBox(runtime);

}

/***************************************************************************
函數:Button_Help()
作用:實現按鈕功能,help
參數:無
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月20日        創建              ***
/**************************************************************************/
void CBasic::Button_Help()
{
	CString help;
	help.Format("%s\r\n%s\r\n%s\r\n\r\n%s\r\n%s\r\n%s\r\n", _T("感謝您使用本程序!提示:"), _T("需首先打開影像才能進行操作!"), _T("自寫Gauss濾波代碼、Moravec運行較慢,請耐心等待!"),
		_T("Welcome to my Github and my CSDN blog , more information will be available about the project!"),
		_T("Github : https://github.com/Yiqingde"),
		_T("CSDN Blog : https://me.csdn.net/weixin_42348202"));
	AfxMessageBox(help);
}
/***************************************************************************
函數:Button_Moravec()
作用:實現按鈕功能,Moravec
參數:無
返回值:無
歷史:**日期**         **理由**            **簽名**
	  2019年9月26日        創建              ***
/**************************************************************************/
void CBasic::Button_Moravec()
{
	if (tag == 0)
	{
		AfxMessageBox(_T("需首先打開影像才能進行操作!"));
		return;
	}
	Mat MoravecImg;
	clock_t start, finish;
	start = clock();
	function2.Moravec(m_srcimg, m_srcimgrgb, 5, 20,MoravecImg);//調用函數
	finish = clock();
	double totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
	CString runtime;
	runtime.Format(_T("%s%4f%s\n"),_T("************************* Morevec算子運行時間:"), totaltime,_T("s *************************"));
	imshow("Moravec單獨顯示", MoravecImg); //顯示
	waitKey();
	imshow("Moravec彩色顯示", m_srcimgrgb); //顯示
	function2.Reporttxt(runtime);//輸出txt
}

3.3.5文件 < ZRX0107170110Dlg.cpp >(僅展示部分)

/***************************************************************************
類:ZRX0107170110Dlg
作用:按鈕實現文件
歷史:**日期**         **理由**            **簽名**
	  2019年9月20日        創建              ***
/**************************************************************************/
CBasic t;//全局變量
/*************************************************
按鈕:讀取圖像函數
*************************************************/
void CZRX0107170110Dlg::OnBnClickedOk()
{
	// TODO: 在此添加控件通知處理程序代碼
	//CDialogEx::OnOK();
	t.OpenImg();
}


/*************************************************
按鈕:進行二值化顯示
*************************************************/
void CZRX0107170110Dlg::OnBnClickedread2()
{
	// TODO: 在此添加控件通知處理程序代碼
	t.Button_binaryImg();
}


/*************************************************
按鈕:進行自寫gauss顯示與opencv顯示且比較時間
*************************************************/
void CZRX0107170110Dlg::OnBnClickedread3()
{
	// TODO: 在此添加控件通知處理程序代碼
	t.Button_GaussImg();
}

/*************************************************
按鈕:help
*************************************************/
void CZRX0107170110Dlg::OnBnClickedread4()
{
	// TODO: 在此添加控件通知處理程序代碼
	t.Button_Help();
}

/*************************************************
按鈕:Moravec按鈕顯示  
*************************************************/
void CZRX0107170110Dlg::OnBnClickedread5()
{
	// TODO: 在此添加控件通知處理程序代碼
	t.Button_Moravec();
}

3.4運行結果

3.4.1界面

在這裏插入圖片描述

3.4.2 Moravec算子運算結果

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

總結

實現Morevec算子的實現過程,遇到的主要問題是局部抑制。
採取的方法是四層循環:
① 針對窗口除了邊框的每一個點來確保以它爲中心,只要存在有一個值不小於它的值,即給它做一個標記0,進行下一個點。
② 排除窗口邊框的點和邊框內小於零的點,然後存儲至 vector &f 中
③ 刪除同一窗口值一樣的重複點,儘管出現概率較小,但較大的圖像往往某些窗口中會存在好幾個數值相等的極大值
④ 嘗試了冒泡排序,最後取vector中最大的50個值,但是效果不好,隨註釋。
亮點:類的設計較好,層次較分明、功能獨立。
不足:拙於MFC控件功底,又讓步於界面簡潔,以至於閾值內定,MFC靈活性不強。另外循環多、運算速度比較慢。

代碼雖多不要貪杯~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章