目錄
一、前言
遭遇了點突發情況,所以今天更新的有點晚,也不知道能不能等到今天發出去了。
終於可以從模板匹配開始了,說明基礎博客即將更新完畢,下一步,開始更新機器學習算法博客,感謝大家的鼎力支持。
如果想看其他有關於OpenCV學習方法介紹、學習教程、代碼實戰、常見報錯及解決方案等相關內容,可以直接看我的OpenCV分類:
【OpenCV系列】:https://blog.csdn.net/shuiyixin/article/category/7581855
如果你想了解更多有關於計算機視覺、OpenCV、機器學習、深度學習等相關技術的內容,想與更多大佬一起溝通,那就掃描下方二維碼加入我們吧!
二、模板匹配
1、模板匹配是個啥
我們先來了解一下模板匹配是個啥。
我們先不說定義,先從理解開始,單純的理解這個概念:
就是有一個模板,然後我有一個大圖像要去和模板匹配。
這個解釋很暴力有木有!
那接下來我們來介紹一下標準的解釋和定義。
模板匹配是一種最原始、最基本的模式識別方法,研究某一特定對象物的圖案位於圖像的什麼地方,進而識別對象物,這就是一個匹配問題。它是圖像處理中最基本、最常用的匹配方法。
根據上面的解釋呢,我們就能得到如下定義:
模板就是一副已知的小圖像,而模板匹配就是在一副大圖像中搜尋目標,已知該圖中有要找的目標,且該目標同模板有相同的尺寸、方向和圖像元素,通過一定的算法可以在圖中找到目標,確定其座標位置。
比如上面這個圖中,左邊大的是圖像,右面小的是模板。
這裏我們注意到一個描述:
相同的尺寸、方向和圖像元素
這就說明,我們要找的模板是裏圖像裏標標準準存在的,這裏說的標標準準,就是說,一旦圖像或者模板發生變化,比如旋轉,修改某幾個像素,圖像翻轉等操作之後,我們就無法進行匹配了,這也是這個算法的弊端。
所以這種匹配算法,相當於“人工智障式”匹配,就是在待檢測圖像上,從左到右,從上向下對模板圖像與小東西的圖像進行比對。
2、常用匹配算法
我們會經常使用到如下六個匹配算法:
1.平方差匹配 method=CV_TM_SQDIFF
2.標準平方差匹配 method=CV_TM_SQDIFF_NORMED
3.相關匹配 method=CV_TM_CCORR
4.標準相關匹配 method=CV_TM_CCORR_NORMED
5.相關匹配 method=CV_TM_CCOEFF
6.標準相關匹配 method=CV_TM_CCOEFF_NORMED
之後我們會講到一個API,裏面有一個參數method,它是指比較方法,主要方法在cv::TemplateMatchModes。就是如下幾個:
enum TemplateMatchModes {
TM_SQDIFF = 0, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2\f]
TM_SQDIFF_NORMED = 1, //!< \f[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}}\f]
TM_CCORR = 2, //!< \f[R(x,y)= \sum _{x',y'} (T(x',y') \cdot I(x+x',y+y'))\f]
TM_CCORR_NORMED = 3, //!< \f[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}}\f]
TM_CCOEFF = 4, //!< \f[R(x,y)= \sum _{x',y'} (T'(x',y') \cdot I'(x+x',y+y'))\f]
//!< where
//!< \f[\begin{array}{l} T'(x',y')=T(x',y') - 1/(w \cdot h) \cdot \sum _{x'',y''} T(x'',y'') \\ I'(x+x',y+y')=I(x+x',y+y') - 1/(w \cdot h) \cdot \sum _{x'',y''} I(x+x'',y+y'') \end{array}\f]
TM_CCOEFF_NORMED = 5 //!< \f[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} }\f]
};
1.平方差匹配-CV_TM_SQDIFF
從名字來理解,平方差匹配就是通過計算每個像素點的差的平方的和,和數學中統計裏面的平方差類似。但是因爲我們要的只是一個值,所以我們最後不需要求平均。
其公式如下:
如果相近,則每個差都很小,最終的和也很小,如果完全一致,則差爲0,所以最好的匹配就是這個值爲零的時候。值越大,匹配越差。
2.標準平方差匹配-CV_TM_SQDIFF_NORMED
這個只是對上面的進行了標準化處理,經過處理後,上面的值就不會太大。
3.相關匹配-CV_TM_CCORR
這類方法採用模板和圖像間的乘法操作,所以較大的數表示匹配程度較高,0標識最壞的匹配效果。
4.標準相關匹配-CV_TM_CCORR_NORMED
這個只是對上面的進行了標準化處理,經過處理後,上面的值就不會太大。
5.相關匹配-CV_TM_CCOEFF
這類方法將模版對其均值的相對值與圖像對其均值的相關值進行匹配,1表示完美匹配,-1表示糟糕的匹配,0表示沒有任何相關性(隨機序列)。
6.標準相關匹配-CV_TM_CCOEFF_NORMED
這個只是對上面的進行了標準化處理,經過處理後,上面的值就不會太大。
匹配算法很多,但是具體用哪一個,我們要根據自己的需要進行選擇, 也可能需要嘗試去對比。
3、API
接下來我們講一下API。
void matchTemplate(
InputArray image,
InputArray templ,
OutputArray result,
int method,
InputArray mask = noArray()
);
函數參數含義如下:
(1)InputArray類型的src ,運行搜索的圖像。它必須是8位或32位浮點。
(2)InputArray類型的templ,已搜索模板。它不能大於源映像,並且具有相同的數據類型。。
(3)OutputArray類型的result,比較結果的映射。它必須是單通道32位浮點。
(4)int類型的method,指定比較方法的參數,請參見cv::TemplateMatchModes。
(5)InputArray類型的mask,搜索模板的掩碼。它必須與temp具有相同的數據類型和大小。默認情況下不設置。
4、代碼展示
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat src; Mat templ; Mat result;
char* src_window = "【輸入圖像】";
char* result_window = "【結果窗口】";
int match_method;
int max_Trackbar = 5;
// 函數聲明
void MatchingMethod(int, void*);
int main()
{
src = imread("E:/image/girl2.png");
templ = imread("E:/image/girl2_1.png");
if (!src.data || !templ.data)
{
cout << "could not load image !";
return -1;
}
// 創建窗口
namedWindow(src_window, CV_WINDOW_AUTOSIZE);
namedWindow(result_window, CV_WINDOW_AUTOSIZE);
imshow(src_window, src);
// 創建滑動條
char* trackbar_label = "Method";
createTrackbar(trackbar_label, src_window, &match_method, max_Trackbar, MatchingMethod);
MatchingMethod(0, 0);
waitKey(0);
return 0;
}
void MatchingMethod(int, void*)
{
// 將被顯示的原圖像
Mat src_display;
src.copyTo(src_display);
// 創建輸出結果的矩陣
int result_cols = src.cols - templ.cols + 1;
int result_rows = src.rows - templ.rows + 1;
result.create(result_cols, result_rows, CV_32FC1);
// 進行匹配和標準化
matchTemplate(src, templ, result, match_method);
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
// 通過函數 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(src_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(src_window, src_display);
imshow(result_window, result);
return;
}
5、執行結果
大家也可以自己嘗試一下呀,一定要多做練習!