在模式識別中一個最基本的方法,就是模板匹配法(template matching),它基本上是一種統計識別方法。 爲了在圖像中檢測出已知形狀的目標物,咱們使用這個目標物的形狀模板(或窗口)與圖像匹配,在約定的某種準則下檢測出目標物圖像,一般稱其爲模板匹配法。它能檢測出圖像中上線條、曲線、圖案等等。它的應用包括:目標模板與偵察圖像相匹配;文字識別和語音識別等。算法
咱們採用如下的算式來衡量模板T(m,n)與所覆蓋的子圖Sij(i,j)的關係,已知原始圖像S(W,H),如圖所示:函數
利用如下公式衡量它們的類似性:spa
上述公式中第一項爲子圖的能量,第三項爲模板的能量,都和模板匹配無關。第二項是模板和子圖的互爲相關,隨(i,j)而改變。當模板和子圖匹配時,該項由最大值。在將其歸一化後,獲得模板匹配的相關係數:設計
當模板和子圖徹底同樣時,相關係數R(i,j) = 1。在被搜索圖S中完成所有搜索後,找出R的最大值Rmax(im,jm),其對應的子圖Simjm即位匹配目標。顯然,用這種公式作圖像匹配計算量大、速度慢。咱們可使用另一種算法來衡量T和Sij的偏差,其公式爲:指針
計算兩個圖像的向量偏差,能夠增長計算速度,根據不一樣的匹配方向選取一個偏差閥值E0,當E(i,j)>E0時就中止該點的計算,繼續下一點的計算。code
最終的實驗證實,被搜索的圖像越大,匹配的速度越慢;模板越小,匹配的速度越快;閥值的大小對匹配速度影響大;orm
改進的模板匹配算法對象
將一次的模板匹配過程更改成兩次匹配;blog
第一次匹配爲粗略匹配。取模板的隔行隔列數據,即1/4的模板數據,在被搜索土上進行隔行隔列匹配,即在原圖的1/4範圍內匹配。因爲數據量大幅減小,匹配速度顯著提升。同時須要設計一個合理的偏差閥值E0:圖片
E0 = e0 * (m + 1) / 2 * (n + 1) / 2
式中:e0爲各點平均的最大偏差,通常取40~50便可;
m,n爲模板的長寬;
第二次匹配是精確匹配。在第一次偏差最小點(imin, jmin)的鄰域內,即在對角點爲(imin -1, jmin -1), (Imin + 1, jmin + 1)的矩形內,進行搜索匹配,獲得最後結果。
算法實現的關鍵問題是進行匹配,求最小距離,其解決方法是和訓練集的樣品逐一進行距離的計算,最後找出最相鄰的樣品獲得類別號。
開發環境:Visual C++ 2015
圖片文字分割處理
分割前圖片:
分割後圖片:
// 圖片文字分割處理 void CHwrProjectApp::OnImgprcAll() { // TODO: 在此添加命令處理程序代碼 // 聲明一些必要的全局變量 CString strPathName; // 返回完整的文件路徑 HDIB m_hDIB; BOOL isOpen = TRUE; // 是否打開(不然爲保存) // 建立一個打開文件對話框,並返回完整的文件路徑 CString filter = L"256色位圖文件(*.bmp)|*.bmp||"; //文件過慮的類型 CFileDialog openFileDlg(isOpen, NULL, NULL, OFN_HIDEREADONLY | OFN_READONLY, filter, NULL); if (openFileDlg.DoModal() == IDOK) strPathName = openFileDlg.GetPathName(); else return; // 建立一個文件對象 CFile file; // 以只讀模式打開文件 file.Open(strPathName, CFile::modeRead); // 讀取文件到HDIB句柄中. 注意:此時只是讀取位圖文件中文件頭以後的部分,不含文件頭 m_hDIB = ::ReadDIBFile(file); // HDIB句柄: 就是一塊存儲位圖數據的內存區域的地址 // HDIB句柄包含:位圖信息頭、調色板(若是有的話)、DIB圖像數據 // 關閉文件 file.Close(); // 指向DIB的指針(指向位圖信息頭) BYTE* lpDIB = (BYTE*)::GlobalLock((HGLOBAL)m_hDIB); // 獲取DIB中顏色表中的顏色數目 WORD wNumColors; wNumColors = ::DIBNumColors((char*)lpDIB); // 判斷是不是256色位圖 if (wNumColors != 256) { // 提示用戶 MessageBox(NULL,(LPCWSTR)L"非256色位圖!", (LPCWSTR)L"系統提示", MB_ICONINFORMATION | MB_OK); // 解除鎖定 ::GlobalUnlock((HGLOBAL)m_hDIB); // 返回 return; } ImgprcAll(m_hDIB); MessageBox(NULL, (LPCWSTR)L"處理完成,請到目錄下查看圖片!", (LPCWSTR)L"系統提示", MB_ICONINFORMATION | MB_OK); }
加載template,進行識別
// 加載template void CHwrProjectApp::OnButtonOpen() { // TODO: 在此添加命令處理程序代碼 // CString name = _T("111.txt"); // char *dibFileName = classify.CStringToCharArray(name); // 加載template //CString curDir; //char curdir[256]; //::GetCurrentDirectory(256, (LPWSTR)curdir); //curDir.Format(_T("%s"), curdir); // classify.LoadFile("E:\\picture.bmp"); CFile TheFile(_T("E:\\template.dat"), CFile::modeRead); CArchive ar(&TheFile, CArchive::load, 40960); TheFile.SeekToBegin(); for (int i = 0; i<10; i++) { ar >> classify.pattern[i].number; for (int n = 0; n<classify.pattern[i].number; n++) for (int j = 0; j<25; j++) { ar >> classify.pattern[i].feature[n][j]; } } ar.Close(); TheFile.Close(); //CFileDialog dlg(TRUE, _T("BMP"), _T("*.BMP"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("位圖文件(*.BMP)|*.BMP|")); //if (IDOK == dlg.DoModal()) //{ // filename.Format(_T("%s"), dlg.GetPathName()); // char* dibFileName = (char*)(LPCTSTR)filename; // char *dibFileName = classify.CStringToCharArray(filename); // classify.LoadFile(dibFileName); //} int result; CString str; classify.LoadFile("E:\\part1.bmp"); // 最鄰近模板匹配法 classify.GetPosition(); classify.SetFeature(); result = classify.GetNumberByLeastDistance(); str.Format(_T("應用最小距離法,\n自動分類識別結果爲:%d"), result); AfxMessageBox(str, MB_OK, NULL); classify.LoadFile("E:\\part2.bmp"); // 最鄰近模板匹配法 classify.GetPosition(); classify.SetFeature(); result = classify.GetNumberByLeastDistance(); str.Format(_T("應用最小距離法,\n自動分類識別結果爲:%d"), result); AfxMessageBox(str, MB_OK, NULL); classify.LoadFile("E:\\part3.bmp"); // 最鄰近模板匹配法 classify.GetPosition(); classify.SetFeature(); result = classify.GetNumberByLeastDistance(); str.Format(_T("應用最小距離法,\n自動分類識別結果爲:%d"), result); AfxMessageBox(str, MB_OK, NULL); //MessageBox(NULL, (LPCWSTR)L"識別完成!", (LPCWSTR)L"系統提示", MB_ICONINFORMATION | MB_OK); }
Classification.h
#pragma once #include "GetFeature.h" struct number_no { int number; int no; }; class Classification : public GetFeature { public: Classification(); ~Classification(); // 計算兩個樣品的匹配程度 ,返回兩各樣品的的匹配程度。 double pipei(double s1[], double s2[]); // 最小距離法 ,返回數字類別和編號 最鄰近匹配模板法 number_no LeastDistance(); // 返回最鄰近匹配模板法Result int GetNumberByLeastDistance(); };
Classification.cpp
#include "stdafx.h" #include "Classification.h" Classification::Classification() { } Classification::~Classification() { } /****************************************************************** * 函數名稱:LeastDistance() * 函數類型:number_no,結構體 * 函數功能:最小距離法 ,返回數字類別和編號 ******************************************************************/ number_no Classification::LeastDistance() { double min = 10000000000; number_no number_no; for (int n = 0; n<10; n++) { for (int i = 0; i<pattern[n].number; i++) { if (pipei(pattern[n].feature[i], testsample)<min) { // 匹配的最小值 min = pipei(pattern[n].feature[i], testsample); number_no.number = n; // 樣品類別 number_no.no = i; // 樣品序號 } } } return number_no;// 返回手寫數字的類別和序號 } int Classification::GetNumberByLeastDistance() { double min = 10000000000; number_no number_no; for (int n = 0; n<10; n++) { for (int i = 0; i<pattern[n].number; i++) { if (pipei(pattern[n].feature[i], testsample)<min) { //匹配的最小值 min = pipei(pattern[n].feature[i], testsample); number_no.number = n;//樣品類別 number_no.no = i;//樣品序號 } } } return number_no.number;//返回手寫數字的類別和序號 } /**************************************************************** * 函數名稱:pipei(double s1[], double s2[]) * 函數類型:double * 參數說明:double s1[], double s2[]:兩個樣品的特徵 * 函數功能:計算兩個樣品的匹配程度 ,返回兩各樣品的的匹配程度。 ****************************************************************/ double Classification::pipei(double s1[], double s2[]) { double count = 0.0; for (int i = 0; i<25; i++) { count += (s1[i] - s2[i])*(s1[i] - s2[i]); } return count; }