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窗口操作後並經過閾值後的圖像
返回值:無
歷史:**日期** **理由** **簽名**
2019年9月26日 創建 ***
/**************************************************************************/
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靈活性不強。另外循環多、運算速度比較慢。