OpenCV-Python實(shí)現(xiàn)多模板匹配
模板匹配的作用在圖像識(shí)別領(lǐng)域作用可大了。那什么是模板匹配?
模板匹配,就是在一幅圖像中尋找另一幅模板圖像最匹配(也就是最相似)的部分的技術(shù)。
多模板匹配
在上一篇的實(shí)戰(zhàn)中,我們通過(guò)人物眼睛的子圖,找出了其在圖像中出現(xiàn)位置。但是,有些情況下,并不僅僅只有一次,比如我們講解傅里葉變換時(shí),曾介紹一張草原的獅子圖。如果匹配某個(gè)草,可能單個(gè)圖像內(nèi)會(huì)有很多,這個(gè)時(shí)候就要找出多個(gè)匹配結(jié)果。
而函數(shù)cv2.minMaxLoc()僅僅能找出最值,無(wú)法給出所有匹配區(qū)域的位置信息。所以,要想匹配多個(gè)結(jié)果,就需要進(jìn)行如下4個(gè)步驟:
獲取匹配位置的集合
首先,Numpy庫(kù)中的函數(shù)where()能夠獲取模板匹配位置的集合。對(duì)于不同的輸入,其返回值是不同的。
- 當(dāng)輸入是一維數(shù)組時(shí),返回值是一維索引,只是一組索引數(shù)組。
- 當(dāng)輸入是二維數(shù)組時(shí),返回的是匹配值的位置索引,因此會(huì)有兩組索引數(shù)組表示返回值的位置。
比如,我們的灰度圖像一般都是二維數(shù)組。下面,我們來(lái)查找一個(gè)二維數(shù)組中,值大于8的元素索引:
import numpy as np img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]]) result = np.where(img > 5) print(result)
運(yùn)行之后,控制臺(tái)會(huì)輸出如下內(nèi)容:

如果你對(duì)Numpy不是很了解的化。下面博主在將數(shù)據(jù)轉(zhuǎn)換以下,基本上都能看懂了。轉(zhuǎn)換之后,格式如下:

第一行為大于5的值的X坐標(biāo),第二行為大于5的值的Y坐標(biāo)。那么上面大于5的數(shù)組索引為:[0,2],[0,3],[0,4],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3]。你可以回溯對(duì)比看看是不是一致的。
通過(guò)np.where()函數(shù)可以找出在cv2.matchTemplate()函數(shù)的返回值中,哪些位置上的值是大于閾值threshold的。具體操作代碼如下:
loc=np.where(res>threshold)
循環(huán)
因?yàn)槲覀冋业降脑瓐D對(duì)應(yīng)的模板圖像不止一個(gè),要處理多個(gè)值,肯定會(huì)用到循環(huán)。因此,在獲取匹配值的索引后,可以采用如下語(yǔ)句遍歷所有匹配的位置,對(duì)這些位置做標(biāo)記:
for i in 匹配位置集合: 標(biāo)記匹配位置
在循環(huán)中使用zip()
函數(shù)zip()用可迭代的對(duì)象作為參數(shù),將對(duì)象中對(duì)應(yīng)的元素打包成一個(gè)個(gè)元組,然后返回由這些元組組成的列表。
例如,我們獲取的索引為x,y,z。下面我們使用zip()將它們打包成元組。代碼如下:
import cv2 import numpy as np import matplotlib.pyplot as plt img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]]) result = np.where(img > 5) for i in zip(*result): print(i)
這里我們還是使用上面的值,輸出結(jié)果如下:

這里自動(dòng)將我們剛才滿足條件的索引打包成了元素格式。是不是比剛才的控制臺(tái)輸出結(jié)果更加的直觀呢?
替換坐標(biāo)
我們上面得到的結(jié)果是符合條件的索引:(行號(hào),列號(hào)),但我們需要繪制匹配位置的矩形,需要的是(列號(hào),行號(hào))。
所以,在使用cv2.rectangle()繪制矩形前,要先將函數(shù)numpy.where()得到的位置索引行列互換,行列互換可以通過(guò)如下代碼實(shí)現(xiàn):
import numpy as np img = np.array([[2, 4, 6, 8, 10], [9, 60, 10, 30, 4], [55, 21, 11, 7, 5]]) result = np.where(img > 5) for i in zip(*result[::-1]): print(i)
運(yùn)行之后,輸出結(jié)果如下:

實(shí)戰(zhàn)多模板匹配
既然我們已經(jīng)了解了標(biāo)記繪制多個(gè)模板位置的4個(gè)步驟。下面,我們直接將上面的代碼整理以下,即可完成多模板的匹配。具體代碼如下所示:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("34.jpg", 0)
template = cv2.imread("4_1.jpg", 0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.9
loc = np.where(res >= 0.9)
for i in zip(*loc[::-1]):
cv2.rectangle(img, i, (i[0] + w, i[1] + h), 255, 1)
plt.imshow(img, cmap="gray")
plt.axis("off")
plt.show()
這里的代碼與上面4個(gè)步驟一摸一樣,具體就不做過(guò)多的講解了。運(yùn)行之后,多個(gè)模板也就匹配完成。

附錄:
模板圖

原圖

實(shí)例:基于opencv的多目標(biāo)模板匹配
利用opencv進(jìn)行多目標(biāo)模板匹配,只要是利用其matchTemplate函數(shù),但在多目標(biāo)(這里是討論目標(biāo)圖片中不同大小模板的匹配),以下貼出代碼和圖片,供大家參考:
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Point getNextMinLoc(Mat &result, Point minLoc, int maxValue, int templatW, int templatH);
int main(void)
{
Mat src = imread("1_2.png");
Mat srcCopy = src.clone();
Mat temp = imread("1_4.png");
Mat result;
if (src.empty() || temp.empty())
{
cout << "打開圖片失敗" << endl;
return 0;
}
vector<Mat> templat;
vector<float> minV;
vector<Point> minL;
int srcW, srcH, templatW, templatH, resultH, resultW;
srcW = src.cols;
srcH = src.rows;
templat.push_back(temp);
double minValue, maxValue;
Point minLoc, maxLoc;
for (int i=0;i<10;i++)
{
cout << i << ": ";
templatW = templat[i].cols;
templatH = templat[i].rows;
if (srcW < templatW || srcH < templatH)
{
cout << "模板不能比原圖大" << endl;
return 0;
}
resultW = srcW - templatW + 1;
resultH = srcH - templatH + 1;
result.create(Size(resultW, resultH), CV_32FC1);
matchTemplate(src, templat[i], result, CV_TM_SQDIFF_NORMED);
minMaxLoc(result, &minValue, &maxValue, &minLoc, &maxLoc);
cout << "min1: " << minValue << endl;
if (minValue<=0.070055)
{
rectangle(srcCopy, minLoc, Point(minLoc.x + templatW, minLoc.y + templatH), Scalar(0, 0, 255), 2, 8, 0);
Point new_minLoc;
new_minLoc = getNextMinLoc(result, minLoc, maxValue, templatW, templatH);
float *data = result.ptr<float>(new_minLoc.y);
cout << "min2: " << data[new_minLoc.x] << " ";
if (data[new_minLoc.x]<=0.5)
{
cout << "進(jìn)這個(gè)函數(shù)了:" << i << ":" << new_minLoc.x;
cout << " " << new_minLoc.y;
rectangle(srcCopy, new_minLoc, Point(new_minLoc.x + templatW, new_minLoc.y + templatH),
Scalar(0, 255, 0), 2, 8, 0);
new_minLoc = getNextMinLoc(result, new_minLoc, maxValue, templatW, templatH);
}
float *data1 = result.ptr<float>(new_minLoc.y);
cout << "min3: " << data1[new_minLoc.x] << " " << endl;
if (data1[new_minLoc.x] <= 0.4)
{
rectangle(srcCopy, new_minLoc, Point(new_minLoc.x + templatW, new_minLoc.y + templatH),
Scalar(255, 0, 0), 2, 8, 0);
}
}
cout << "#" << endl;
Mat temp_templat;
resize(templat[i], temp_templat, Size(templat[i].cols / 1.1, templat[i].rows / 1.1));
templat.push_back(temp_templat);
}
imshow("結(jié)果", srcCopy);
waitKey(0);
return 0;
}
Point getNextMinLoc(Mat &result, Point minLoc, int maxValue, int templatW, int templatH)
{
//imshow("result", result);
//cout << "maxvalue: " << maxValue << endl;
int startX = minLoc.x - templatW / 3;
int startY = minLoc.y - templatH / 3;
int endX = minLoc.x + templatW / 3;
int endY = minLoc.y + templatH / 3;
if (startX < 0 || startY < 0)
{
startX = 0;
startY = 0;
}
if (endX > result.cols - 1 || endY > result.rows - 1)
{
endX = result.cols - 1;
endY = result.rows - 1;
}
int y, x;
for (y = startY; y < endY; y++)
{
for (x = startX; x < endX; x++)
{
float *data = result.ptr<float>(y);
data[x] = maxValue;
}
}
double new_minValue, new_maxValue;
Point new_minLoc, new_maxLoc;
minMaxLoc(result, &new_minValue, &new_maxValue, &new_minLoc, &new_maxLoc);
//imshow("result_end", result);
return new_minLoc;
}


以下是結(jié)果圖:

到此這篇關(guān)于OpenCV-Python實(shí)現(xiàn)多模板匹配的文章就介紹到這了,更多相關(guān)OpenCV 多模板匹配內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!
版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非maisonbaluchon.cn所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。
關(guān)注官方微信