Python OpenCV中的模板匹配(Template Matching)详解

文章目录
更多有趣的代码示例,可参考【Programming】
1、功能描述
基于 opencv-python 实现模板匹配算法

2、原理分析
算法流程
(1)滑动窗口
将模板图像在目标图像上逐点滑动。
对于每个位置,计算模板与当前窗口区域的相似度。
(2)相似度计算
使用特定的匹配方法计算模板与当前窗口区域的相似度。
常用的匹配方法包括相关性、平方差等。
(3)结果矩阵
将每个位置的相似度值存储在结果矩阵中。
结果矩阵的大小为 (W – w + 1, H – h + 1),其中 W 和 H 是目标图像的宽和高,w 和 h 是模板图像的宽和高。
(4)最佳匹配定位
根据匹配方法,找到结果矩阵中的最大值或最小值位置。
该位置即为模板在目标图像中的最佳匹配位置。
优点
缺点
应用场景
改进与优化
3、代码实现
导入必要的库函数
import cv2
import time
import numpy as np
读入目标图像(Input Image),转换为灰度图,读入模板图像(Template Image),以灰度图模式读取
配置匹配置信度相关参数
img_rgb = cv2.imread('1.jpg') # 需要检测的图片
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) # 转化成灰色
template_img = cv2.imread('4.jpg', 0) # 模板小图
template_threshold = 0.8 # 模板置信度
调用模板匹配算法
dets = template(img_gray, template_img, template_threshold)
可视化结果,并将结果保存
for coord in dets:
cv2.rectangle(img_rgb, (int(coord[0]), int(coord[1])), (int(coord[2]), int(coord[3])), (0, 0, 255), 2)
cv2.imwrite("result4.jpg", img_rgb)
下面看看 template 函数的实现
def template(img_gray, template_img, template_threshold, nms=False):
'''
img_gray:待检测的灰度图片格式
template_img:模板小图,也是灰度化了
template_threshold:模板匹配的置信度
'''
h, w = template_img.shape[:2]
res = cv2.matchTemplate(img_gray, template_img, cv2.TM_CCOEFF_NORMED)
start_time = time.time()
loc = np.where(res >= template_threshold) # 大于模板阈值的目标坐标
score = res[res >= template_threshold] # 大于模板阈值的目标置信度
# 将模板数据坐标进行处理成左上角、右下角的格式
xmin = np.array(loc[1])
ymin = np.array(loc[0])
xmax = xmin + w
ymax = ymin + h
xmin = xmin.reshape(-1, 1) # 变成n行1列维度
xmax = xmax.reshape(-1, 1) # 变成n行1列维度
ymax = ymax.reshape(-1, 1) # 变成n行1列维度
ymin = ymin.reshape(-1, 1) # 变成n行1列维度
score = score.reshape(-1, 1) # 变成n行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、yamx、scores按照列进行拼接
thresh = 0.3 # NMS里面的IOU交互比阈值
if nms:
keep_dets = py_nms(data_hstack, thresh) # (203, 5)
else:
keep_dets = list(range(data_hstack.shape[0]))
print("nms time:", time.time() - start_time) # 打印数据处理到nms运行时间
dets = data_hstack[keep_dets] # 最终的nms获得的矩形框
return dets
核心算法是调用了 res = cv2.matchTemplate(img_gray, template_img, cv2.TM_CCOEFF_NORMED)
把返回的结果改变下格式保存
经过 keep_dets = py_nms(data_hstack, thresh) 非极大值抑制算法后,输出最终的模板匹配结果
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降序排序的
order = scores.argsort()[::-1]
# print("order:",order)
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
# 计算当前概率最大矩形框与其他矩形框的相交框的坐标,会用到numpy的broadcast机制,得到的是向量
xx1 = np.maximum(x1[i], x1[order[1:]])
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]
# print("inds:",inds)
# 将order序列更新,由于前面得到的矩形框索引要比矩形框在原order序列中的索引小1,所以要把这个1加回来
order = order[inds + 1]
return keep
4、效果展示
输入图片

模板1

结果

模板2

结果

模板3

结果

输入图片

模板

输出结果

变成灰度图匹配的缺点,哈哈,石头草堆也都错误匹配了
输入图片,阈值 0.6

模板1

结果

这就是没有角度鲁棒性的缺点
模板

结果

模板

结果

阈值设置的低 0.6,不管炮口的朝向了
5、完整代码
import cv2
import time
import numpy as np
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降序排序的
order = scores.argsort()[::-1]
# print("order:",order)
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
# 计算当前概率最大矩形框与其他矩形框的相交框的坐标,会用到numpy的broadcast机制,得到的是向量
xx1 = np.maximum(x1[i], x1[order[1:]])
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]
# print("inds:",inds)
# 将order序列更新,由于前面得到的矩形框索引要比矩形框在原order序列中的索引小1,所以要把这个1加回来
order = order[inds + 1]
return keep
def template(img_gray, template_img, template_threshold, nms=False):
'''
img_gray:待检测的灰度图片格式
template_img:模板小图,也是灰度化了
template_threshold:模板匹配的置信度
'''
h, w = template_img.shape[:2]
res = cv2.matchTemplate(img_gray, template_img, cv2.TM_CCOEFF_NORMED)
start_time = time.time()
loc = np.where(res >= template_threshold) # 大于模板阈值的目标坐标
score = res[res >= template_threshold] # 大于模板阈值的目标置信度
# 将模板数据坐标进行处理成左上角、右下角的格式
xmin = np.array(loc[1])
ymin = np.array(loc[0])
xmax = xmin + w
ymax = ymin + h
xmin = xmin.reshape(-1, 1) # 变成n行1列维度
xmax = xmax.reshape(-1, 1) # 变成n行1列维度
ymax = ymax.reshape(-1, 1) # 变成n行1列维度
ymin = ymin.reshape(-1, 1) # 变成n行1列维度
score = score.reshape(-1, 1) # 变成n行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、yamx、scores按照列进行拼接
thresh = 0.3 # NMS里面的IOU交互比阈值
if nms:
keep_dets = py_nms(data_hstack, thresh) # (203, 5)
else:
keep_dets = list(range(data_hstack.shape[0]))
print("nms time:", time.time() - start_time) # 打印数据处理到nms运行时间
dets = data_hstack[keep_dets] # 最终的nms获得的矩形框
return dets
if __name__ == "__main__":
img_rgb = cv2.imread('1.jpg') # 需要检测的图片
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) # 转化成灰色
template_img = cv2.imread('4.jpg', 0) # 模板小图
template_threshold = 0.8 # 模板置信度
dets = template(img_gray, template_img, template_threshold)
for coord in dets:
cv2.rectangle(img_rgb, (int(coord[0]), int(coord[1])), (int(coord[2]), int(coord[3])), (0, 0, 255), 2)
cv2.imwrite("result4.jpg", img_rgb)
6、涉及到的库函数
cv2.matchTemplate
函数签名
cv2.matchTemplate(image, templ, method[, result[, mask]]) -> result
参数说明
image:
templ:
method:
result:
mask:
返回值
result:
每个元素表示模板在对应位置的匹配度。
7、参考
Python+Opencv实现图像匹配——模板匹配
https://docs.opencv.org/5.x/de/da9/tutorial_template_matching.html
更多有趣的代码示例,可参考【Programming】
作者:bryant_meng