模式识别—最邻近模板匹配法
简介
在模式识别中一个最基本的方法,就是模板匹配法(template matching),它基本上是一种统计识别方法。 为了在图像中检测出已知形状的目标物,我们使用这个目标物的形状模板(或窗口)与图像匹配,在约定的某种准则下检测出目标物图像,通常称其为模板匹配法。它能检测出图像中上线条、曲线、图案等等。它的应用包括:目标模板与侦察图像相匹配;文字识别和语音识别等。
原理
我们采用以下的算式来衡量模板T(m,n)与所覆盖的子图Sij(i,j)的关系,已知原始图像S(W,H),如图所示:
利用以下公式衡量它们的相似性:
上述公式中第一项为子图的能量,第三项为模板的能量,都和模板匹配无关。第二项是模板和子图的互为相关,随(i,j)而改变。当模板和子图匹配时,该项由最大值。在将其归一化后,得到模板匹配的相关系数:
当模板和子图完全一样时,相关系数R(i,j) = 1。在被搜索图S中完成全部搜索后,找出R的最大值Rmax(im,jm),其对应的子图Simjm即位匹配目标。显然,用这种公式做图像匹配计算量大、速度慢。我们可以使用另外一种算法来衡量T和Sij的误差,其公式为:
计算两个图像的向量误差,可以增加计算速度,根据不同的匹配方向选取一个误差阀值E0,当E(i,j)>E0时就停止该点的计算,继续下一点的计算。
最终的实验证明,被搜索的图像越大,匹配的速度越慢;模板越小,匹配的速度越快;阀值的大小对匹配速度影响大;
改进的模板匹配算法
将一次的模板匹配过程更改为两次匹配;
第一次匹配为粗略匹配。取模板的隔行隔列数据,即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; }