简介
之前说过模糊匹配只是对于单个目标进行匹配,今天我们就来学习一下如何对多个目标进行匹配
一、多目标匹配
对于这个图片我们要匹配下面那个箭头,我们可以发现图中是有两个位置相同的箭头,之前我们说的模糊匹配是寻找匹配值最大的,那两个怎么寻找呢?
1.基本步骤
1.图像准备
模板图像:需要被匹配的目标图像,通常是一个较小的图像块。
输入图像:在其中进行搜索以找到与模板图像相似的多个区域的图像。
2.图像预处理
转换为灰度图像:在进行模板匹配之前,通常需要将输入图像和模板图像转换为灰度图像,因为灰度图像中的像素值仅表示亮度,不受颜色影响,更适合进行匹配。
降噪和增强:根据需要,可以对图像进行降噪处理以提高匹配准确性,或进行增强处理以突出目标特征。
3.执行模板匹配
使用模板匹配算法(如OpenCV中的cv2.matchTemplate()函数)在输入图像中搜索与模板图像相似的区域。
模板匹配算法会生成一个结果图像,其中每个像素的值表示该位置与模板图像的匹配程度。
4.定位匹配区域
使用cv2.minMaxLoc()等函数在结果图像中找到匹配度最高的区域(或多个区域,如果设置了适当的阈值)。
根据匹配位置在原图中绘制矩形框或其他标记,以指示匹配到的目标。
5.处理多个匹配
如果需要匹配多个目标,并且这些目标在图像中可能以不同的尺寸、方向或旋转角度出现,则可能需要使用更复杂的算法,如尺度不变特征变换(SIFT)、加速稳健特征(SURF)或ORB等。
对于简单的多目标匹配,可以通过设置较低的匹配阈值来找到多个匹配区域,并分别处理它们。
6.优化和验证
根据需要调整模板匹配算法的参数(如匹配方法、阈值等),以优化匹配结果。
对匹配结果进行验证,确保它们确实是所需的目标,并排除误匹配。
# 导入必要的库:cv2用于图像处理,numpy用于数值计算
import cv2
import numpy as np
# 读取原始彩色图像(默认读取为BGR格式,而非RGB)
img_rgb = cv2.imread('beijing.jpg')
# 将彩色图像转为灰度图:模板匹配通常在单通道灰度图上进行,减少计算量和干扰
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# 读取模板图像:flags=0表示以灰度模式读取(直接得到单通道图像)
template = cv2.imread('jiantou.jpg', flags=0)
# 获取模板的高和宽:shape返回(高, 宽, 通道数),[:2]取前两个值(高h和宽w)
h, w = template.shape[:2]
# 执行模板匹配:在灰度图上滑动模板,计算每个位置的匹配度
# 参数说明:
# - img_gray:待匹配的灰度图像(大图像)
# - template:模板图像(小图像)
# - cv2.TM_CCOEFF_NORMED:匹配方法(归一化相关系数匹配)
# 返回值res:是一个矩阵,每个元素表示模板在对应位置的匹配度(范围[-1,1],1为完美匹配)
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
# 设定匹配阈值:只有匹配度≥0.9的区域才被认为是有效匹配(过滤低匹配度的干扰)
threshold = 0.9
# 获取所有符合阈值的匹配点坐标:
# np.where(res >= threshold)返回满足条件的索引,格式为(行坐标数组, 列坐标数组)
# 例如:(array([5, 10]), array([3, 7]))表示有两个匹配点,坐标为(5,3)和(10,7)
loc = np.where(res >= threshold)
# 遍历所有匹配点,在原图上绘制矩形框标记
# zip(*loc[::-1])的作用:将坐标从(行,列)转为(列,行)(OpenCV中坐标是(x,y)即列在前,行在后)
for pt in zip(*loc[::-1]):
# 绘制矩形:
# - img_rgb:要绘制的图像(原始彩色图,方便直观查看)
# - pt:矩形左上角坐标(x,y)
# - (pt[0]+w, pt[1]+h):矩形右下角坐标(左上角x+模板宽,左上角y+模板高)
# - color=(0,0,255):矩形颜色(BGR格式,这里是红色)
# - thickness=1:矩形线宽
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), color=(0, 0, 255), thickness=1)
# 显示标记后的图像:第一个参数是窗口名称(空字符串表示默认窗口),第二个参数是图像
cv2.imshow('', img_rgb)
# 等待用户按键输入:0表示无限等待(直到按下任意键关闭窗口)
cv2.waitKey(0)
局限性:
只能匹配与模板方向、大小完全一致的目标。如果目标在图像中旋转了(比如箭头方向变了),则无法检测到。那我们就继续探索一下如何去匹配旋转的目标
二、图像旋转
为了解决上述局限性,需要先掌握如何旋转图像。这部分提供了两种旋转方法,用于后续生成不同角度的模板。
import cv2
import numpy as np
# 方法一:使用numpy的rot90函数旋转(按90度倍数旋转)
img = cv2.imread('../kele.png') # 读取图像(以彩色模式)
# np.rot90参数说明:
# - 第一个参数:要旋转的图像
# - k:旋转次数(每次90度),k=1表示逆时针转90度,k=-1(或3)表示顺时针转90度
rotated_image1 = np.rot90(img, k=-1) # 顺时针旋转90度
rotated_image2 = np.rot90(img, k=1) # 逆时针旋转90度
# 显示原图和旋转后的图像
cv2.imshow('yuantu', img) # 原图窗口
cv2.imshow('rotated_image1', rotated_image1) # 顺时针90度窗口
cv2.imshow('rotated_image2', rotated_image2) # 逆时针90度窗口
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows() # 关闭所有窗口,释放资源
# 方法二:使用OpenCV的rotate函数旋转(更直观,支持固定角度)
rotated_image = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) # 顺时针90度
rotated_image1 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) # 逆时针90度
rotated_image2 = cv2.rotate(img, cv2.ROTATE_180) # 旋转180度
# 显示旋转结果
cv2.imshow('shun90', rotated_image) # 顺时针90度窗口
cv2.imshow('ni90', rotated_image1) # 逆时针90度窗口
cv2.imshow('180', rotated_image2) # 180度窗口
cv2.waitKey(0) # 等待按键
两种方法对比:
np.rot90
:通过旋转次数控制(k=1/2/3/-1),适合 90 度倍数的旋转,但不够直观。cv2.rotate
:直接通过枚举值指定旋转方向(如ROTATE_90_CLOCKWISE
),可读性更强,推荐使用。
三、支持旋转角度的模板匹配
结合多目标匹配与旋转的知识,我们就可以结合一下实现对旋转角度的模板匹配
import cv2
import numpy as np
# 读取原始图像和模板
img_rgb = cv2.imread('beijing.jpg') # 原始彩色图像
template = cv2.imread('jiantou.jpg', 0) # 模板图像(以灰度模式读取)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) # 原始图像转灰度
# 方法二(cv2.rotate)生成不同角度的模板(方法一被注释,原理相同)
template_rot90_clockwise_cv = cv2.rotate(template, cv2.ROTATE_90_CLOCKWISE) # 顺时针90度
template_rot90_counterclockwise_cv = cv2.rotate(template, cv2.ROTATE_90_COUNTERCLOCKWISE) # 逆时针90度
template_rot180_cv = cv2.rotate(template, cv2.ROTATE_180) # 180度
# 定义模板列表:包含原始模板和所有旋转后的模板(需要检测的角度)
templates = [
template, # 原始模板(0度)
template_rot90_clockwise_cv, # 顺时针90度
template_rot90_counterclockwise_cv, # 逆时针90度
template_rot180_cv # 180度
]
# 设定匹配阈值:从0.9降低到0.8
# 原因:旋转后的模板与目标的匹配度可能略低(边缘、细节可能有偏差),降低阈值避免漏检
threshold = 0.8
# 遍历每个模板,分别进行匹配
for temp in templates:
h, w = temp.shape[:2] # 获取当前模板的高和宽(不同旋转角度的模板尺寸可能变化)
# 对当前模板执行匹配
res = cv2.matchTemplate(img_gray, temp, cv2.TM_CCOEFF_NORMED)
# 获取符合阈值的匹配点坐标
loc = np.where(res >= threshold)
# 遍历匹配点,绘制矩形框
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 1)
# 显示最终匹配结果
cv2.imshow('Result', img_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows() # 关闭窗口,释放资源
核心逻辑:
通过生成模板的多个旋转版本(0°、90° 顺时针、90° 逆时针、180°),分别与原始图像匹配,从而覆盖目标可能的旋转角度。这样即使目标在图像中旋转了,也能被检测到。