使用OpenCV&&C++進行模板匹配

一:課程介紹

1.1:學習目標

  •   學會用imread載入圖像,和imshow輸出圖像。
  •   用nameWindow建立窗口,用createTrackbar加入滾動條和其回調函數的寫法。
  •   熟悉OpenCV函數matchTemplate並學會經過該函數實現模板匹配。
  •      學會怎樣將一副圖片中本身感興趣的區域標記出來

1.2:什麼是模板匹配?

  在一副圖像中尋找和另外一幅圖像最類似(匹配)部分的技術。ios

1.3:案例展現

  輸入有兩幅圖像一副是 template.jpg 另外一幅是 original.jpg 。匹配完成的結果是result.jpg算法

 

 

二:實驗原理

  讓模板圖片在原圖片上的一次次滑動(從左到右,從上到下一個像素爲單位的移動),而後將兩張圖片的像素值進行比對,而後選擇類似度最高的部分進行標記,當遇到類似度更高的部分時更換標記部分。掃描完畢以後,將類似度最高的部分標記出來,做爲圖片進行輸出操做。數組

三:環境搭建

$ cd ~
$ sudo apt-get update
$ wget http://labfile.oss.aliyuncs.com/courses/671/opencv.sh
$ sudo chmod 777 opencv.sh
$ ./opencv.sh

在執行完以後執行以下語句,檢查是否安裝成功數據結構

./facedetect --cascade="/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml" --scale=1.5 lena.jpg

 四:實驗步驟

4.1定義頭文件

  在這裏咱們用了ide

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

  這三個頭文件:函數

    highgui.hpp:定義了建立窗口的flag,窗口事件的flag,Qt窗口的flag,事件回調函數原型,以及窗口/控件操做相關的系列函數,openGL的包裝函數;圖像輸入輸出顯示的相關函數;視頻攝像頭輸入輸出顯示的相關函數,VideoCapture,VideoWriter。學習

    imgproc.hpp:定義了圖像處理模塊之平滑和形態學操做。測試

    iostream:再也不贅述。ui

4.2:設計主要功能並對其解析(從main函數入口開始分析

imread函數:

  img = imread("original.jpg");//載入元圖像
  templ = imread("template.jpg");//載入模版圖像

 

  imread函數能夠將圖片讀取而後放到Mat容器裏面用於後續操做。spa

nameWindow函數:

  namedWindow( image_window, CV_WINDOW_AUTOSIZE ); // 窗口名稱,窗口標識CV_WINDOW_AUTOSIZE是自動調整窗口大小以適應圖片尺寸。
  namedWindow( result_window, CV_WINDOW_AUTOSIZE );

  建立窗口。第一個參數是窗口名稱,第二個窗口是int類型的Flag能夠填寫如下的值

  1. WINDOW_NORMAL設置了這個值,用戶即可以改變窗口的大小(沒有限制)
  2. WINDOW_AUTOSIZE若是設置了這個值,窗口大小會自動調整以適應所顯示的圖像,而且不能手動改變窗口大小

createTrackba函數:

 

/// 建立滑動條
  createTrackbar("匹配方法", image_window, &match_method, max_Trackbar, MatchingMethod ); //滑動條提示信息,滑動條所在窗口名,匹配方式(滑塊移動以後將移動到的值賦予該變量),回調函數。

  建立滑動條,第一個參數是匹配方法,第二個參數是肯定滑動條所在窗口,第三個參數是對應滑動條的值,第四個參數是滑動條的最大值,第五個參數是回調函數。

本身寫的回調函數

MatchingMethod( 0, 0 );//初始化顯示

  先調用回調函數,在沒有滑動滑塊的時候也有圖像。

waitkey函數:

 waitKey(0); //等待按鍵事件,若是值0爲則永久等待。

  其取值能夠是<=0或大於0.當取值爲<=0的時候,若是沒有鍵盤觸發則一直等待,不然返回值爲按下去的ascll對應數字。

 Mat::copyto函數:

Mat img_display;
img.copyTo( img_display ); //將 img的內容拷貝到 img_display

建立Mat類型數據結構img_display。並將img內容賦值給img_display

Mat::create函數:

/// 建立輸出結果的矩陣
  int result_cols =  img.cols - templ.cols + 1;     //計算用於儲存匹配結果的輸出圖像矩陣的大小。
  int result_rows = img.rows - templ.rows + 1;

  result.create( result_cols, result_rows, CV_32FC1 );//被建立矩陣的列數,行數,以CV_32FC1形式儲存。

matchTemplate (模版匹配)函數

matchTemplate( img, templ, result, match_method ); //待匹配圖像,模版圖像,輸出結果圖像,匹配方法(由滑塊數值給定。)

咱們在createTrackba函數那裏見到過match_method變量,這個是決定匹配方法的變量,由滑塊肯定。

normalize(歸一化函數)

normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );//輸入數組,輸出數組,range normalize的最小值,range normalize的最大值,歸一化類型,當type爲負數的時候輸出的type和輸入的type相同。

  歸一化就是要把須要處理的數據通過處理後(經過某種算法)限制在你須要的必定範圍內。首先歸一化是爲了後面數據處理的方便,其次是保證程序運行時收斂加快。歸一化的具體做用是概括統同樣本的統計分佈性。歸一化在0-1之間是統計的機率分佈,歸一化在某個區間上是統計的座標分佈。歸一化有同1、統一和合一的意思。

minMaxLoc函數

minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );//用於檢測矩陣中最大值和最小值的位置

  用於尋找距震中的最大值和最小值

不一樣方法之間選擇最佳精確度

  if( match_method  == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
    { matchLoc = minLoc; }
  else
    { matchLoc = maxLoc; }

  對於方法CV_TM_SQDIFF,和CV_TM_SQDIFF_NORMED,越小的數值表明越準確匹配結果,而對於其餘方法,數值越大匹配的準確度越高。

將最後獲得的結果顯性的標記出來

 

  /// 讓我看看您的最終結果
  rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar(0,0,255), 2, 8, 0 ); //將獲得的結果用矩形框起來
  rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar(0,0,255), 2, 8, 0 );

 

  第一個參數(img):將要被操做的圖像,第二個和第三個參數分別是一個矩形的對角點。第四個(color)參數是線條的顏色(RGB)。第五個參數(thickness):組成矩陣線條的粗細程度。第六個參數(line_type):線條的類型,見cvLine的描述。第七個參數shift:座標點的小數點位數

4.3:應用算法解析

  matchTemplate實現了末班匹配散發:其中可用的方法有六個:

    1:  平方差匹配: method = CV_TM_SQDIFF

          這類方法利用平方差來進行匹配最好匹配爲0.匹配差越大,匹配值越大。

          R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2

    2:  標準平方差匹配:method = CV_TM_SQDIFF_NORMED

          R(x,y)= \frac{\sum_{x',y'} (T(x',y')-I(x+x',y+y'))^2}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}

          這類方法利用平方差來進行匹配最好匹配爲0.匹配差越大,匹配值越大。

    3:  相關匹配method=CV_TM_CCORR

          R(x,y)= \sum _{x',y'} (T(x',y')  \cdot I(x+x',y+y'))

          這類方法採用模板和圖像間的乘法操做,因此較大的數表示匹配程度較高,0標識最壞的匹配效果.

    4:  標準相關匹配 method=CV_TM_CCORR_NORMED

          R(x,y)= \frac{\sum_{x',y'} (T(x',y') \cdot I'(x+x',y+y'))}{\sqrt{\sum_{x',y'}T(x',y')^2 \cdot \sum_{x',y'} I(x+x',y+y')^2}}

          同標準平方差和平方差,如下再也不贅述。

    5:  相關匹配 method=CV_TM_CCOEFF

          R(x,y)= \sum _{x',y'} (T'(x',y')  \cdot I(x+x',y+y'))

          這類方法將模版對其均值的相對值與圖像對其均值的相關值進行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示沒有任何相關性(隨機序列).

    6:  標準相關匹配 method=CV_TM_CCOEFF_NORMED

          R(x,y)= \frac{ \sum_{x',y'} (T'(x',y') \cdot I'(x+x',y+y')) }{ \sqrt{\sum_{x',y'}T'(x',y')^2 \cdot \sum_{x',y'} I'(x+x',y+y')^2} }

          一般,隨着從簡單的測量(平方差)到更復雜的測量(相關係數),咱們可得到愈來愈準確的匹配(同時也意味着愈來愈大的計算代價). 最好的辦法是對全部這些設置多作一些測試實驗,以便爲本身的應用選擇同時兼顧速度和精度的最佳方案.

有了上述相關知識以後相信你就能夠看懂而且對下方代碼進行改造的能力了

五:試驗程序

  這裏就是完整的代碼,上面對這些代碼已經作了完整的解析。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>


using namespace std;
using namespace cv;

Mat img; Mat templ; Mat result;
char* image_window = "Source Image"; //窗口名稱定義
char* result_window = "Result window";  //窗口名稱定義


int match_method;
int max_Trackbar = 5;




void MatchingMethod( int, void* )
{

  Mat img_display;
  img.copyTo( img_display ); //將 img的內容拷貝到 img_display

  /// 建立輸出結果的矩陣
  int result_cols =  img.cols - templ.cols + 1;     //計算用於儲存匹配結果的輸出圖像矩陣的大小。
  int result_rows = img.rows - templ.rows + 1;

  result.create( result_cols, result_rows, CV_32FC1 );//被建立矩陣的列數,行數,以CV_32FC1形式儲存。

  /// 進行匹配和標準化
  matchTemplate( img, templ, result, match_method ); //待匹配圖像,模版圖像,輸出結果圖像,匹配方法(由滑塊數值給定。)
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );//輸入數組,輸出數組,range normalize的最小值,range normalize的最大值,歸一化類型,當type爲負數的時候輸出的type和輸入的type相同。

  /// 經過函數 minMaxLoc 定位最匹配的位置
  double minVal; double maxVal; Point minLoc; Point maxLoc;
  Point matchLoc;

  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );//用於檢測矩陣中最大值和最小值的位置

  /// 對於方法 SQDIFF 和 SQDIFF_NORMED, 越小的數值表明更高的匹配結果. 而對於其餘方法, 數值越大匹配越好
  if( match_method  == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED )
    { matchLoc = minLoc; }
  else
    { matchLoc = maxLoc; }

  /// 讓我看看您的最終結果
  rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar(0,0,255), 2, 8, 0 ); //將獲得的結果用矩形框起來
  rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar(0,0,255), 2, 8, 0 );

  imshow( image_window, img_display );//輸出最終的到的結果
  imwrite("result.jpg",img_display); //將獲得的結果寫到源代碼目錄下。
  imshow( result_window, result );   //輸出匹配結果矩陣。

  return;
}


int main( int argc, char** argv )
{

  img = imread("original.jpg");//載入待匹配圖像
  templ = imread("template.jpg");//載入模版圖像

  /// 建立窗口
  namedWindow( image_window, CV_WINDOW_AUTOSIZE ); // 窗口名稱,窗口標識CV_WINDOW_AUTOSIZE是自動調整窗口大小以適應圖片尺寸。
  namedWindow( result_window, CV_WINDOW_AUTOSIZE );

  /// 建立滑動條
  createTrackbar("jackchen", image_window, &match_method, max_Trackbar, MatchingMethod ); //滑動條提示信息,滑動條所在窗口名,匹配方式(滑塊移動以後將移動到的值賦予該變量),回調函數。

  MatchingMethod( 0, 0 );//初始化顯示

  int logo = waitKey(5000); //等待按鍵事件,若是值0爲則永久等待。

  return 0;
}

 

 六:完整實驗流程

  在源代碼存放的文件夾裏面有兩幅圖片,一幅original.jpg是待匹配的圖片,另外一幅是template.jpg是模版圖片,而後輸出一副匹配成功的圖片。

 一下分別是 original.jpg,template,result.jpg

 

 

 

 

 

 

 

相關文章
相關標籤/搜索