模板匹配的做用在圖像識別領域做用可大了。那什麼是模板匹配?ios
模板匹配,就是在一幅圖像中尋找另外一幅模板圖像最匹配(也就是最類似)的部分的技術。算法
說的有點抽象,下面給個例子說明就很明白了。函數
在上面這幅全明星照中,咱們想找出姚明頭像的位置,並把它標記出來,能夠作到嗎?
測試
能夠,這就是模板匹配的要作的事情。ui
其實模板匹配實現的思想也是很簡單很暴力的,就是拿着模板圖片(姚明頭像)在原圖(全明星照)中從左上至右下依次滑動,直到遇到某個區域的類似度低於咱們設定的閾值,那麼咱們就認爲該區域與模板匹配了,也就是咱們找到了姚明的位置,並把它標記出來。spa
OpenCV中是經過MtachTemplate函數完成。code
#include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <stdio.h> using namespace std; using namespace cv; int main() { Mat img, templ, result; img = imread("nba.jpg"); templ = imread("76.png"); int result_cols = img.cols - templ.cols + 1; int result_rows = img.rows - templ.rows + 1; result.create(result_cols, result_rows, CV_32FC1); matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);//這裏咱們使用的匹配算法是標準平方差匹配 method=CV_TM_SQDIFF_NORMED,數值越小匹配度越好 normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat()); double minVal = -1; double maxVal; Point minLoc; Point maxLoc; Point matchLoc; cout << "匹配度:" << minVal << endl; minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat()); cout << "匹配度:" << minVal << endl; matchLoc = minLoc; rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0); imshow("img", img); waitKey(0); return 0; }
結果看來,大姚的頭像位置確實被綠框標記出來了!很準!orm
我還在程序中特地打印出匹配度的最小值,由於咱們知道這個算法是數值越小匹配度越高,由輸出的結果看來這個數值還真的很小,說明匹配度真的至關高!
blog
既然咱們能夠取得匹配度的數值,那咱們是否是也能夠利用該數值進行閾值對比呢?好比我想把在閾值範圍以內的頭像都標記出來。能夠這麼作:圖片
//閾值判別,小於0.01才認爲匹配成功,纔將頭像框出來 if (minVal < 0.001) { rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0); }
同理,若是是數值越大代表匹配度越大的算法,就使用maxVal來對比就能夠了。
上面的模板匹配咱們使用了標準平方差匹配 CV_TM_SQDIFF_NORMED算法,看起來效果還不錯,那還有其餘算法嗎?
問得好。OpenCV經過函數 matchTemplate 實現了模板匹配算法。可用的方法有6個:
一般,隨着從簡單的測量(平方差)到更復雜的測量(相關係數),咱們可得到愈來愈準確的匹配(同時也意味着愈來愈大的計算代價)。
最好的辦法是對全部這些設置多作一些測試實驗,以便爲本身的應用選擇同時兼顧速度和精度的最佳方案。
你想採用哪一種算法,只須要將對應的傳進函數matchTemplate裏就能夠了。
下面給出利用trackbar顯示出多種模板那匹配算法的代碼。
#include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; Mat g_srcImage, g_tempalteImage, g_resultImage; int g_nMatchMethod; int g_nMaxTrackbarNum = 5; void on_matching(int, void*) { Mat srcImage; g_srcImage.copyTo(srcImage); int resultImage_cols = g_srcImage.cols - g_tempalteImage.cols + 1; int resultImage_rows = g_srcImage.rows - g_tempalteImage.rows + 1; g_resultImage.create(resultImage_cols, resultImage_rows, CV_32FC1); matchTemplate(g_srcImage, g_tempalteImage, g_resultImage, g_nMatchMethod); normalize(g_resultImage, g_resultImage, 0, 2, NORM_MINMAX, -1, Mat()); double minValue, maxValue; Point minLocation, maxLocation, matchLocation; minMaxLoc(g_resultImage, &minValue, &maxValue, &minLocation, &maxLocation); if (g_nMatchMethod == TM_SQDIFF || g_nMatchMethod == CV_TM_SQDIFF_NORMED) { matchLocation = minLocation; } else { matchLocation = maxLocation; } rectangle(srcImage, matchLocation, Point(matchLocation.x + g_tempalteImage.cols, matchLocation.y + g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8, 0); rectangle(g_resultImage, matchLocation, Point(matchLocation.x + g_tempalteImage.cols, matchLocation.y + g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8, 0); imshow("原始圖", srcImage); imshow("效果圖", g_resultImage); } int main() { g_srcImage = imread("nba.jpg"); if (!g_srcImage.data) { cout << "原始圖讀取失敗" << endl; return -1; } g_tempalteImage = imread("76.png"); if (!g_tempalteImage.data) { cout << "模板圖讀取失敗" << endl; return -1; } namedWindow("原始圖", CV_WINDOW_AUTOSIZE); namedWindow("效果圖", CV_WINDOW_AUTOSIZE); createTrackbar("方法", "原始圖", &g_nMatchMethod, g_nMaxTrackbarNum, on_matching); on_matching(0, NULL); waitKey(0); return 0; }
固然也會有一些算法匹配失敗的.
實驗證實,該段程序效果很不錯,但注意的是,模板配在原圖摳出模板圖的形式下準確率才比較高,否則的話可能準確度就不過高了。
那麼模板匹配能在哪些項目有應用呢?我說一下個人經驗。
最近我在參與實驗室的一個項目,作的是發票的分類,分類的方法我首先採用的是模板匹配,也就是從一類發票中摳出一些特徵區域,以此做爲模板,本身設定閾值,低於閾值就是算是跟該類發票匹配了,就能夠 對其進行分類。在個人測試看來,準確率還能夠,不過也隱藏這一個比較大的隱患就是,一旦發票種類多了,好比100種,那麼檢測時間就會指數上升,這是不可取的。