opencv的模板匹配方法
模板匹配
在opencv中進行模板匹配使用cv2.matchTemplate
函式,簡單使用如下:
import cv2 import numpy as np img = cv2.imread('landscape.jpg', cv2.IMREAD_GRAYSCALE) tpl = cv2.imread('template.jpg', cv2.IMREAD_GRAYSCALE) result = cv2.matchTemplate(img, tpl, cv2.TM_CCORR_NORMED) # 返回的max_loc 是按照Opencv 的座標(x, y),不是numpy的(row, col) (_, max_val, _, max_loc) = cv2.minMaxLoc(result) # 利用最大值*0.99 作為閾值,閾值可根據情況設定 threshold = max_val * 0.99 # loc = [[x1, x2, x3...], [y1, y2, y3]] 注意和numpy的row col轉換 loc = np.where(result >= threshold) tr = img.copy() for pt in zip(*loc[::-1]): cv2.rectangle(tr, pt, (pt[0] + tw, pt[1] + th), (0, 0, 255), 1)
其中
result = cv2.matchTemplate(img, tpl, cv2.TM_CCORR_NORMED)
假設landscape圖片大小為5120x9760,讀取進來的img矩陣大小為9760*5120,template圖片大小為366x533,讀入tpl的大小為533x366。則我們可以推算出result的大小:(9760-533+1, 5120-366+1)=(9208, 4755)。陣列中記錄的是進行模板匹配的相合資料。result[m][n]
代表的是從img的m行n列畫素點開始進行匹配的結果。你可以把result[m][n]
轉化到PyQT的座標系,變為(n, m),那麼影象在PyQt的範圍就是(n, m) ~ (n+tw, m+th)。
loc
返回值是一個二維矩陣:[[row0, row1, row2, ...],[col0, col1, col2, ...]]
,(row0, col0)
是指在result中row0行col0列符合條件。
關於[::-1]的解釋:a = np.array([1, 2, 3, 4])
執行a[::-1]
後返回結果為[4, 3, 2, 1]
,對於二維矩陣b = np.array([[1, 2],[5, 6]])
執行b[::-1]
後返回結果為[[5, 6], [1, 2]]
。對於返回loc
執行這步操作是因為result
是按(x, y)順序排列,對於x來說其實代表的是列,如果我們想要行在前,必須反過來。
*loc[::-1]
a = np.array([[1, 2],[5, 6]])
,*a
為[1, 2], [5, 6]
。
上述的程式碼存在一些問題:匹配出的模板有重疊,例如,圖片((0, 0), (100, 100))區域匹配度比較高,但是((1, 1), (101, 101))的匹配度也很高,這就導致匹配出多個重疊區域。可以通過nms極大值抑制。
th, tw = tpl.shape[:2]
loc = np.where(result >= threshold)
score = result[result >= max_val*0.99] # 返回的是值,值原來順序變為一維矩陣
xmin = np.array(loc[1])
ymin = np.array(loc[0])
xmax = xmin + tw
ymax = ymin + th
# 全部轉為一維列向量
xmin, ymin, xmax, ymax = xmin.reshape(-1, 1), ymin.reshape(-1, 1), xmax.reshape(-1, 1), ymax.reshpae(-1, 1)
score = score.reshape(-1, 1)
data_hlist = []
data_hlist.append(xmin); data_hlist.append(ymin); data_hlist.append(xmax); data_hlist.append(ymax); data_hlist.append(score)
data_hstack = np.hstack(data_hlist) #按照 xmin, ymin, xmax, ymax, score 順序
thresh = 0.3 #設定NMS互動比
keep_dets = py_nms(data_hstack, thresh)
dets = data_hstack[keep_dets]
tr = img.copy()
for rec in dets:
cv2.rectangle(tr, (int(rec[0]), int(rec[1])), (int(rec[2]), int(rec[3])), (0, 0, 255), 2)
實現py_nms
函式:
# NMS 非極大值抑制
def py_nms(dets, thresh):
"""Pure Python NMS baseline."""
# x1、y1、x2、y2、以及score賦值
# (x1、y1)(x2、y2)為box的左上和右下角座標
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
# 每一個候選框的面積
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
# order是按照score降序排序的有,返回的是索引陣列,[::-1]為了將升序變為降序
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
# 計算當前概率最大矩形框與其他矩形框的相交框的座標,得到的是向量
xx1 = np.maximum(x1[i], x1[order[1:]]) # x1[i]雖然為一個數,但是由於numpy的broadcast機制,xx1是一個數組
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
# 計算相交框的面積,注意矩形框不相交時w或h算出來會是負數,用0代替
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
# 計算重疊度IOU:重疊面積/(面積1+面積2-重疊面積)
ovr = inter / (areas[i] + areas[order[1:]] - inter)
# 找到重疊度不高於閾值的矩形框索引
inds = np.where(ovr <= thresh)[0]
# 將order序列更新,由於前面得到的矩形框索引要比矩形框在原order序列中的索引小1,所以要把這個1加回來
order = order[inds + 1]
return keep