OpenCV-python小玩意14 多模板多目标匹配
它是NumPy中一个非常强大的函数,用于基于条件筛选数组元素或返回满足条件的索引。condition: 布尔条件(可以是数组)。x: 当条件为True时返回的值(可选)。y: 当条件为False时返回的值(可选)。如果只传入condition,则返回满足条件的索引(坐标),记住返回的是索引,到时别蒙。(1) 返回满足条件的索引# 输出: (array([3, 4],)返回一个元组(即使是一维数组)
0.明确需求
今天要在模板匹配代码基础上,增加对多目标的支持。实际场景会是这样的:在一个图像中有几个目标存在,程序要找出所有高于阈值的目标保存它的置信度和中心点坐标,并在图中显示出来。
1.主要思路
类似多模板的支持一样,多目标版本使用np.where()获取所有高于阈值的匹配位置,收集所有有效匹配。
并介入非极大值抑制(NMS),添加max_overlap参数控制重叠阈值,使用cv2.dnn.NMSBoxes消除重叠框,保留置信度最高的非重叠匹配。
2. 相关函数介绍
2.1 np.where()
它是NumPy中一个非常强大的函数,用于 基于条件筛选数组元素 或 返回满足条件的索引。
函数原型:
np.where(condition, [x, y])
参数简介:
condition: 布尔条件(可以是数组)。x: 当条件为True时返回的值(可选)。y: 当条件为False时返回的值(可选)。
如果只传入 condition,则返回满足条件的 索引(坐标),记住返回的是索引,到时别蒙。
举几个栗子:
(1) 返回满足条件的索引
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
indices = np.where(arr > 3)
print(indices)
# 输出: (array([3, 4],)
返回一个元组(即使是一维数组),其中包含满足 arr > 3 的元素的索引。
- 多维数组示例:
arr_2d = np.array([[1, 2], [3, 4]])
rows, cols = np.where(arr_2d > 2)
print(rows)# 输出: [1, 1](行索引)
print(cols)# 输出: [0, 1](列索引)
(2) 条件替换(类似三元表达式)
arr = np.array([1, 2, 3, 4])
result = np.where(arr > 2, "大于2", "小于等于2")
print(result)
# 输出: ['小于等于2' '小于等于2' '大于2' '大于2']
注意:
条件判断 arr > 2:对数组 arr 的每个元素检查是否大于 2,生成一个布尔数组: [False, False, True, True]
如果条件为 True,选择第二个参数 “大于2”;如果条件为 False,选择第三个参数 “小于等于2”。
(3) 结合广播机制
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.where([True, False, True], a, b)
print(result)
# 输出: [1 5 3](True选a的值,False选b的值)
注意:
布尔数组 [True, False, True],这是一个长度为3的布尔数组,表示选择规则:
True:从数组 a 中取对应位置的元素。
False:从数组 b 中取对应位置的元素。
2.2 非极大值抑制(NMS)
2.2.1 相关背景概念
非极大值抑制(Non-Maximum Suppression, NMS)是计算机视觉中一种非常重要的后处理算法,主要用于消除目标检测或模板匹配中产生的冗余检测框。在实际匹配过程中,你会发现莫名其妙多了很多框,这些就是重叠框。它的核心思想是"保留局部最大值",对于相互重叠的候选框,只保留置信度最高的那个。
此时我们要引入一个IoU(交并比)的概念。
IoU(Intersection over Union,交并比)是衡量两个边界框重叠程度的指标,是两个区域交集面积与并集面积的比值。
简单公式:IoU = Area of Intersection / Area of Union
IoU的取值范围是[0, 1],值越大表示重叠程度越高。
NMS的算法流程文字描述为:
输入一组检测框的坐标和置信度得分和IoU阈值(通常0.3-0.5),将所有检测框按置信度从高到低排序,选择置信度最高的检测框A,将其加入最终结果集,计算A与所有剩余检测框的IoU,移除所有与A的IoU大于阈值的检测框,重复上述过程,直到没有检测框剩余。
2.2.2 NMS在OpenCV中的实现
def nms_opencv(boxes, scores, threshold):
"""
使用OpenCV的NMS实现
:param boxes: [[x1,y1,x2,y2], ...]
:param scores: 置信度数组
:param threshold: IoU阈值
:return: 保留的检测框索引
"""
# 转换为OpenCV需要的格式
boxes = np.array(boxes, dtype=np.float32)
scores = np.array(scores, dtype=np.float32)
# 使用OpenCV的NMSBoxes
indices = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(),
score_threshold=0.01, # 最低置信度
nms_threshold=threshold)
return indices.flatten()
cv2.dnn.NMSBoxes 是 OpenCV 深度学习模块(dnn)中提供的非极大值抑制(NMS)实现,是的,计算机视觉,深度学习迟早会来的。
函数原型:
indices = cv2.dnn.NMSBoxes(
boxes,
scores,
score_threshold,
nms_threshold,
eta=None,
top_k=None
)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
boxes |
List/Tuple/ndarray | 边界框列表,格式为 [[x1,y1,x2,y2], ...] 或 [[x1,y1,w,h], ...] |
scores |
List/Tuple/ndarray | 每个边界框对应的置信度分数 |
score_threshold |
float | 置信度阈值,低于此值的框会被直接过滤 |
nms_threshold |
float | NMS的IoU阈值(0-1之间),重叠度高于此值的框会被抑制 |
eta |
float (可选) | 自适应阈值系数,默认为1.0 |
top_k |
int (可选) | 保留的最大检测框数量,默认为0(不限制) |
返回值:返回一个NumPy数组,包含被保留的边界框的索引(在原boxes列表中的位置),形状为 (n,),其中n是保留的框数量。
多目标核心代码
try:
res = cv2.matchTemplate(img, scaled_template, method)
# 获取所有高于阈值的匹配位置
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
loc = np.where(
res <= (1 - threshold) if method == cv2.TM_SQDIFF_NORMED else (1 - threshold) * res.max())
confidences = 1 - res[loc]
else:
loc = np.where(res >= threshold)
confidences = res[loc]
# 转换为(x,y)坐标列表:loc[::-1]:元组中的元素顺序反转
# *将反转后的元组解包为两个独立的数组参数传递给zip()函数
# zip() 将两个数组中对应位置的元素配对组合
locations = list(zip(*loc[::-1]))
# 如果没有匹配结果则跳过
if not locations:
continue
# 为当前模板的所有匹配创建矩形框
rects = []
for (x, y), conf in zip(locations, confidences):
rects.append([x, y, scaled_w, scaled_h, conf])
# 应用非极大值抑制(NMS)消除重叠框
pick = []
if rects:
# 将矩形框转换为(x1,y1,x2,y2)格式
boxes = np.array([[x, y, x + w, y + h] for (x, y, w, h, _) in rects])
confidences = np.array([conf for (_, _, _, _, conf) in rects])
# 使用OpenCV的NMSBoxes
indices = cv2.dnn.NMSBoxes(boxes.tolist(), confidences.tolist(),
threshold, max_overlap)
if len(indices) > 0:
pick = indices.flatten()
# 保存筛选后的匹配结果
for i in pick:
x, y, w, h = rects[i][:4]
confidence = rects[i][4]
match_info = {
'template': template_path,
'top_left': (x, y),
'bottom_right': (x + w, y + h),
'center': (x + w // 2, y + h // 2),
'confidence': float(confidence),
'size': (w, h)
}
all_matches.append(match_info)
except cv2.error as e:
print(f"处理模板 {template_path} 时出错: {e}")
continue
运行结果如下:
#opencv #模板匹配 #多目标匹配 #np.where() #非极大值抑制NMS #IoU
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)