模式识别—最邻近模板匹配法

简介

在模式识别中一个最基本的方法,就是模板匹配法(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;
}

 

posted @ 2016-04-23 12:21  Bobby0322  阅读(12550)  评论(0编辑  收藏  举报