模式識別—最鄰近模板匹配法

簡介

在模式識別中一個最基本的方法,就是模板匹配法(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;
}
相關文章
相關標籤/搜索