1、基本应用(平移无形变场合)
OpenCV 提供了多种模板匹配方法,如平方差匹配(cv2.TM_SQDIFF)、标准化平方差匹配(cv2.TM_SQDIFF_NORMED)、相关匹配(cv2.TM_CCORR)、归一化相关匹配(cv2.TM_CCORR_NORMED)、相关系数匹配(cv2.TM_CCOEFF)和归一化相关系数匹配(cv2.TM_CCOEFF_NORMED)等。这些方法的基本思想是将模板图像在目标图像上滑动(平移),在每个位置计算模板图像和目标图像子区域之间的相似度。不同的方法采用不同的相似度度量方式。例如,平方差匹配是计算模板和目标子区域之间像素差的平方和,值越小表示匹配度越高;相关系数匹配是计算模板和目标子区域之间的相关系数,值越大表示匹配度越高。
归一化相关匹配(cv2.TM_CCORR_NORMED)的demo:
python
import cv2
template = cv2.imread('template.jpg') # 模板图像
target = cv2.imread('target.jpg') # 目标图像
result = cv2.matchTemplate(target, template, cv2.TM_CCOEFF_NORMED) # 匹配方法选择归一化相关系数
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # 获取匹配结果的最大值和对应坐标
top_left = max_loc # 假设使用的是归一化相关系数匹配方法
bottom_right = (top_left[0] + template.shape[1], top_left[1] + template.shape[0])
cv2.rectangle(target, top_left, bottom_right, (0, 0, 255), 2) # 标注矩形框
cv2.imshow('Matched Image', target)
cv2.waitKey(0)
cv2.destroyAllWindows()
目标图片:
模板图片:

运行结果:

2、当模板图片存在大小和角度偏差
如果模板和目标图片存在一定程度的尺寸和角度偏差,可以使用基于特征点的匹配方法。OpenCV 提供了多种特征检测和描述算法,如 SIFT(尺度不变特征变换)、SURF(加速稳健特征)、ORB(定向快速和旋转不变BRIEF)等。这些算法能够检测出图像中的关键点,并生成对应的描述子,然后通过匹配描述子来找到相似的特征点,从而实现对模板的定位。
SIFT 特征匹配方法
比如,当模板图片存在尺寸和角度的偏差(原目标图片不变):

SIFT(尺度不变特征变换)是一种经典的计算机视觉算法,核心目标是从图像中提取具有尺度、旋转、光照不变性的局部特征点,用于图像匹配、目标识别、全景拼接等场景,由 David Lowe 于 2004 年提出。其核心逻辑是 "让特征点不受图像缩放、旋转、亮度变化的影响,从而稳定识别同一物体 / 场景",
以下是使用 SIFT 特征匹配方法的实现步骤:
python
import cv2
import numpy as np
template = cv2.imread('template2.jpg', cv2.IMREAD_GRAYSCALE) # 模板图像,灰度模式读取
target = cv2.imread('target.jpg', cv2.IMREAD_GRAYSCALE) # 目标图像,灰度模式读取
sift = cv2.SIFT_create() # 创建SIFT特征提取器
kp1, des1 = sift.detectAndCompute(template, None) # 模板图像的特征点和描述子
kp2, des2 = sift.detectAndCompute(target, None) # 目标图像的特征点和描述子
FLANN_INDEX_KDTREE = 1 # FLANN算法的索引类型
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) # FLANN算法的索引参数
search_params = dict(checks=50) # FLANN算法的搜索参数
flann = cv2.FlannBasedMatcher(index_params, search_params) # 创建FLANN匹配器
matches = flann.knnMatch(des1, des2, k=2) # 使用FLANN算法进行匹配
good_matches = [] # 存储好的匹配点
for m, n in matches:
if m.distance < 0.75 * n.distance: # 过滤掉不好的匹配点
good_matches.append(m)
if len(good_matches) > 4:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) # 模板图像的特征点
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) # 目标图像的特征点
H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) # 使用RANSAC算法计算单应性矩阵
img_matches = cv2.drawMatches(template, kp1, target, kp2, good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) # 绘制匹配结果
cv2.imshow('Matches', img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果:

进一步的,绘制出模板在目标图片中的位置
python
h, w = template.shape # 模板图像的尺寸
pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2) # 模板图像的四个角点
dst = cv2.perspectiveTransform(pts, H) # 将模板图像的四个角点变换到目标图像中
target = cv2.polylines(target, [np.int32(dst)], True, (0, 255, 0), 3, cv2.LINE_AA) # 在目标图像中绘制变换后的模板图像
cv2.imshow('Located Template', target)
cv2.waitKey(0)
cv2.destroyAllWindows()

SURF方法
SURF加速稳健特征和 SIFT(尺度不变特征变换)类似,均为计算机视觉中用于特征检测与匹配的经典算法,核心目标是从图像中提取具有 "尺度不变性""旋转不变性" 的关键特征(如角点、边缘交点等),用于图像匹配、目标识别、拼接等场景,但 SURF 通过算法优化实现了更快的计算速度。 但是从opencv3.4以后SURF算法并不是直接包含在标准的 OpenCV 库中,而是作为额外的模块提供。可以使用ORB算法作为取代。
ORB方法
重点学习ORB方法。
ORB(Oriented FAST and Rotated BRIEF)是一种高效的特征检测与描述算法,专为解决实时场景下的图像特征提取需求设计,核心是结合两种经典算法的优势并弥补其缺陷,在计算机视觉领域(如目标跟踪、图像拼接、SLAM)应用广泛。
1. 核心设计逻辑:解决 "速度" 与 "旋转不变性" 痛点
ORB 的诞生源于对两种基础算法的改进:
- FAST(Features from Accelerated Segment Test) :一种快速角点检测算法,通过比较像素与周围邻域像素的灰度差异来判断是否为角点,优势是速度极快 ,但缺点是不具备旋转不变性(图像旋转后特征点可能无法匹配),且特征点的 "方向"(主方向)未定义。
- BRIEF(Binary Robust Independent Elementary Features) :一种二进制特征描述子,通过随机选取图像局部区域的像素对、比较灰度值生成 0/1 二进制串,优势是存储量小、匹配速度快 ,但同样缺乏旋转不变性(旋转后像素对的相对位置变化,导致描述子失效)。
ORB 的核心思路就是:用 "带方向的 FAST"(Oriented FAST)解决检测阶段的旋转问题,用 "旋转后的 BRIEF"(Rotated BRIEF)解决描述阶段的旋转问题,同时保留两者的速度优势。
2. 关键步骤解析
ORB 算法主要分为 "特征检测" 和 "特征描述" 两步:
(1)特征检测:Oriented FAST(带方向的角点检测)
-
第一步:快速角点筛选沿用 FAST 的核心逻辑:在图像中任选像素 p,以 p 为中心取半径 3 像素的圆(共 16 个邻域像素),若存在连续 12 个(或更多)邻域像素的灰度值与 p 的灰度值差异超过阈值(排除噪声),则 p 为候选角点。(优化:通过先检测圆上 4 个正交像素(上下左右),若其中 3 个不满足差异阈值,直接排除 p,进一步提升速度)。
-
第二步:计算特征点主方向(解决旋转不变性) 为每个 FAST 角点定义 "主方向":以角点为中心,取一个 31×31 的邻域,计算该邻域内所有像素的灰度质心(灰度加权的坐标均值);将 "角点坐标" 与 "质心坐标" 的连线方向,作为该角点的主方向。这一步让特征点具备了 "方向属性",后续描述子可围绕该方向旋转,确保旋转后特征一致。
(2)特征描述:Rotated BRIEF(旋转后的二进制描述子)
-
**第一步:预定义 "旋转不变的像素对集"**BRIEF 的缺陷是像素对随机选取,旋转后相对位置变化;ORB 则预先生成一组 "固定的像素对模板",并根据特征点的主方向 θ,将所有像素对围绕特征点旋转 θ 角,得到 "旋转后的像素对"。(优化:ORB 通过统计学习筛选出 128 对 "相关性低、区分度高" 的像素对,最终生成 128 位二进制描述子,平衡匹配精度与速度)。
-
第二步:生成二进制描述子对旋转后的每对像素((x1,y1) 和 (x2,y2)),比较两者灰度值:若 x1 灰度>x2 灰度,描述子对应位记为 1,否则记为 0;最终生成 128 位二进制串,即 ORB 特征描述子。
3. 核心优势与应用场景
优势:
- 高效性:检测(FAST)和描述(二进制)均为轻量计算,速度远超 SIFT、SURF 等传统算法,可满足实时需求(如嵌入式设备、实时跟踪)。
- 鲁棒性 :通过 "主方向定义" 和 "旋转像素对",具备旋转不变性;同时对光照变化、尺度变化(通过图像金字塔实现)有一定抵抗能力。
- 无专利限制:SIFT、SURF 受专利保护,ORB 为开源算法,可自由商用。
典型应用:
- 实时目标跟踪(如视频中跟踪特定物体);
- 图像拼接(如全景图合成,匹配不同图像的重叠区域);
- SLAM(同步定位与地图构建,如无人机、机器人的环境感知);
- 图像匹配(如物体识别、手势识别中的特征比对)。
简言之,ORB 是 "速度" 与 "鲁棒性" 平衡的经典特征算法,是实时计算机视觉任务中的核心工具之一。
demo:
python
import cv2
import numpy as np
# 1. 读取图像
template = cv2.imread('template2.jpg', cv2.IMREAD_GRAYSCALE)
target = cv2.imread('target.jpg', cv2.IMREAD_GRAYSCALE)
# 确保图像读取成功
if template is None or target is None:
raise ValueError("无法读取图像,请检查文件路径是否正确")
# 2. 初始化ORB检测器并提取特征
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(template, None) # 模板特征
kp2, des2 = orb.detectAndCompute(target, None) # 目标图像特征
# 3. 特征匹配与筛选(保留优质匹配)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance) # 按距离排序(距离越小越优)
# 筛选前N个优质匹配(可根据实际情况调整,建议10-50个)
good_matches = matches[:30]
# 4. 准备单应性矩阵计算所需的点集
# 获取匹配对对应的坐标(模板点:pts1,目标点:pts2)
pts1 = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
pts2 = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
# 5. 计算单应性矩阵(使用RANSAC算法剔除异常值)
# 单应性矩阵H可将模板坐标映射到目标图像坐标
H, mask = cv2.findHomography(pts1, pts2, cv2.RANSAC, 5.0)
matches_mask = mask.ravel().tolist() # 标记有效匹配点
# 6. 定义模板图像的边界框(四个角点)
h, w = template.shape # 模板的高和宽
template_corners = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)
# 7. 将模板边界框投影到目标图像中
if H is not None: # 确保单应性矩阵计算成功
target_corners = cv2.perspectiveTransform(template_corners, H)
# 转换为整数坐标(便于绘制)
target_corners = np.int32(target_corners)
else:
raise Exception("单应性矩阵计算失败,可能匹配点不足或质量太差")
# 8. 绘制结果(在彩色目标图像上绘制)
target_color = cv2.cvtColor(target, cv2.COLOR_GRAY2BGR)
# 绘制投影后的模板边界框(红色,线宽2)
cv2.polylines(target_color, [target_corners], isClosed=True, color=(0, 0, 255), thickness=2)
# 9. 绘制带有效匹配标记的匹配图(可选,用于验证匹配质量)
img_matches = cv2.drawMatches(
template, kp1, target_color, kp2, good_matches, None,
matchColor=(0, 255, 0), # 有效匹配线:绿色
singlePointColor=None,
matchesMask=matches_mask, # 只显示有效匹配点
flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
)
# 10. 显示结果
cv2.imshow('Template Matching Result (Target Image)', target_color)
cv2.imshow('Good Matches', img_matches)
# 等待按键关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
# 可选:保存结果图像
cv2.imwrite('target_with_matching_box.jpg', target_color)
cv2.imwrite('good_matches.jpg', img_matches)

4、 调整参数,改进匹配结果
还是上面的demo代码,换了两张图后运行:

看得出,完全没有匹配得上。
将代码中的这一行:
python
orb = cv2.ORB_create()
修改为下面内容后再次运行:
python
orb = cv2.ORB_create(nfeatures=2000, scaleFactor=1.1, patchSize=51)

匹配效果大为改善。