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


簡介

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM