当前位置: 首页 > news >正文

OpenCV计算机视觉实战(21)——模板匹配详解

OpenCV计算机视觉实战(21)——模板匹配详解

    • 0. 前言
    • 1. 多尺度模板匹配
      • 1.1 实现过程
      • 1.2 优化思路
    • 2. 旋转不变性
      • 2.1 实现过程
      • 2.2 优化思路
    • 3. 零件定位实战
      • 3.1 实现过程
      • 3.2 优化思路
    • 小结
    • 系列链接

0. 前言

在工业视觉和自动化检测中,模板匹配是最直观、最易实现的目标定位手段。但面对目标尺寸、角度变化,单纯的 matchTemplate 往往捉襟见肘。本文将通过以下三部分深入探索模板匹配:

  • 多尺度模板匹配:在不同缩放比例上搜索最优匹配,解决尺寸变化问题
  • 旋转不变性处理:通过对模板多角度旋转,保证在任意方向上都能定位
  • 零件定位实战:结合上述方法,完成对生产线零件的精准识别

1. 多尺度模板匹配

在实际场景中,目标在图像中的大小会因距离或焦距不同而发生变化。多尺度模板匹配通过遍历若干缩放比例,分别对模板或待测图像进行缩放,并计算匹配分数,最终选取最佳尺度与位置。

1.1 实现过程

  • 读取大图与模板,获取其原始尺寸
  • 设定缩放比例列表(如 `0.5~1.5)
  • 遍历每个比例:
    • 对大图按比例缩放或对模板缩放
    • 调用 cv2.matchTemplate 计算匹配分数
    • 记录分数最高的尺度与位置
  • 最终绘制:在原图上按最佳尺度和坐标绘制矩形框
import cv2
import numpy as np# 功能:在不同缩放比例下对模板进行匹配,定位最佳位置与尺度
img = cv2.imread('4.jpeg')
template = cv2.imread('9.jpeg', cv2.IMREAD_GRAYSCALE)
h_t, w_t = template.shape[:2]best = {'max_val': -1, 'scale': 1.0, 'pt': (0,0)}# 1. 转为灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 2. 多尺度遍历
for scale in np.linspace(0.5, 1.5, 21):# 缩放大图resized = cv2.resize(gray, None, fx=scale, fy=scale, interpolation=cv2.INTER_LINEAR)if resized.shape[0] < h_t or resized.shape[1] < w_t:continue# 模板匹配res = cv2.matchTemplate(resized, template, cv2.TM_CCOEFF_NORMED)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)if max_val > best['max_val']:best.update({'max_val': max_val, 'scale': scale, 'pt': max_loc})# 3. 在原图绘制结果
scale, (x, y) = best['scale'], best['pt']
# 把坐标映射回原图
x0, y0 = int(x/scale), int(y/scale)
w, h = int(w_t/scale), int(h_t/scale)
output = img.copy()
cv2.rectangle(output, (x0, y0), (x0+w, y0+h), (0,0,255), 2)
cv2.imshow('Multi-scale Template Match', output)
cv2.waitKey(0)
cv2.destroyAllWindows()

多尺度模板匹配

关键函数解析:

  • cv2.resize(src, None, fx, fy, interpolation):图像缩放,INTER_LINEAR 在多尺度匹配中常用平衡速度与质量
  • cv2.matchTemplate(image, templ, method):模板匹配,TM_CCOEFF_NORMED 对应归一化相关系数法
  • cv2.minMaxLoc(res):从匹配结果中获取最优值与位置

1.2 优化思路

  • 暴力缩放 vs 图像金字塔:直接在若干缩放系数上遍历虽然最直观,但对每个尺度都做全图匹配开销大。使用高斯金字塔,对场景或模板逐层下采样,然后从粗到细逐层匹配——既能缩小搜索范围,也能显著提速
  • 预筛选与阈值过滤:在粗尺度下先排除大量不匹配区域,只在 top-K 区域做更细致的匹配
  • 边缘增强:对场景与模板先做 Canny 边缘检测,然后在二值边缘图上进行匹配,可抵抗光照变化
import cv2
import numpy as np# 多尺度匹配改进:使用图像金字塔 + Canny 边缘
scene = cv2.imread('output_paste.png')
template = cv2.imread('logo.png', cv2.IMREAD_GRAYSCALE)
h_t, w_t = template.shape[:2]# 预先提取模板边缘
tpl_edges = cv2.Canny(template, 50, 150)# 构建场景金字塔
pyramid = [scene.copy()]
for _ in range(4):pyramid.append(cv2.pyrDown(pyramid[-1]))best = {'val': -1, 'level':0, 'pt':(0,0)}for level, img in enumerate(pyramid):gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 场景边缘edges = cv2.Canny(gray, 50, 150)if edges.shape[0] < h_t or edges.shape[1] < w_t:continue# 匹配边缘res = cv2.matchTemplate(edges, tpl_edges, cv2.TM_CCOEFF_NORMED)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)if max_val > best['val']:best.update({'val':max_val, 'level':level, 'pt':max_loc})# 将最佳结果映射回原图
lvl, (x,y) = best['level'], best['pt']
scale = 1 / (2**lvl)
x0, y0 = int(x*  (2**lvl)), int(y* (2**lvl))
w0, h0 = int(w_t* (2**lvl)), int(h_t* (2**lvl))
out = scene.copy()
cv2.rectangle(out, (x0,y0), (x0+w0,y0+h0), (0,0,255), 2)
cv2.imshow('Pyramid+Edge Matching', out)
cv2.waitKey(0)
cv2.destroyAllWindows()

匹配结果

关键函数解析:

  • cv2.pyrDown(src):高斯平滑后下采样,构建图像金字塔
  • 在边缘图上做 matchTemplate,对抗明暗与纹理干扰
  • 结果坐标通过 scale = 1/(2**level) 反算回原图

2. 旋转不变性

当模板可能以任意角度出现在场景中时,仅靠尺度匹配无法定位。通过对模板以一定角度步长旋转,并在每个角度上进行匹配,即可获得旋转不变性。

2.1 实现过程

  • 读取并灰度化原图与模板
  • 设定旋转角度列表(如 0°~350°,步长 10°)
  • 遍历每个角度:
    • cv2.getRotationMatrix2Dcv2.warpAffine 旋转模板
    • 调用 matchTemplate,记录最优匹配值、角度与位置
  • 最终绘制:在原图上用最佳角度与坐标绘制旋转矩形
import cv2
import numpy as np# 功能:通过对模板多角度旋转,实现旋转不变的匹配定位
img = cv2.imread('4.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread('output_rotated_9.jpg', cv2.IMREAD_GRAYSCALE)
h_t, w_t = template.shape[:2]best = {'max_val': -1, 'angle': 0, 'pt': (0,0), 'size': (w_t,h_t)}# 1. 遍历旋转角度
for angle in range(0, 360, 10):# 旋转模板M = cv2.getRotationMatrix2D((w_t/2, h_t/2), angle, 1.0)rotated = cv2.warpAffine(template, M, (w_t, h_t), flags=cv2.INTER_LINEAR)# 匹配res = cv2.matchTemplate(gray, rotated, cv2.TM_CCOEFF_NORMED)_, max_val, _, max_loc = cv2.minMaxLoc(res)if max_val > best['max_val']:best.update({'max_val': max_val, 'angle': angle, 'pt': max_loc})# 2. 绘制旋转矩形(使用 cv2.boxPoints)
angle = -best['angle']
w, h = best['size']
(x, y) = best['pt']
rect = ((x + w/2, y + h/2), (w, h), angle)
box = cv2.boxPoints(rect).astype(int)
output = img.copy()
cv2.drawContours(output, [box], 0, (0,255,0), 2)
cv2.imshow('Rotation-invariant Match', output)
cv2.waitKey(0)
cv2.destroyAllWindows()

匹配结果

关键函数解析:

  • cv2.getRotationMatrix2D(center, angle, scale):生成二维仿射变换矩阵
  • cv2.warpAffine(src, M, dsize):应用仿射变换进行图像旋转
  • cv2.boxPoints(rect):将旋转矩形参数转换为四个顶点坐标,便于 drawContours 绘制

2.2 优化思路

  • 粗-细两阶段旋转:先以较大步长(如 15° )粗定位,再在最优角度邻域以小步长(如 )精细搜索
  • 仿射结合:对匹配后的矩形做仿射纠偏,对齐后再做一次微调
  • 频域匹配:对模板与 ROIFourier-Mellin 变换,可一次性实现旋转与尺度不变匹配
import cv2
import numpy as npscene = cv2.imread('4.jpeg')
gray = cv2.cvtColor(scene, cv2.COLOR_BGR2GRAY)
tpl = cv2.imread('output_rotated_9.jpg', cv2.IMREAD_GRAYSCALE)
h_t, w_t = tpl.shape[:2]# 1. 粗定位
best = {'val':-1, 'angle':0, 'pt':(0,0)}
for angle in range(0,360,15):M = cv2.getRotationMatrix2D((w_t/2,h_t/2), angle,1)rot = cv2.warpAffine(tpl, M, (w_t,h_t))res = cv2.matchTemplate(gray, rot, cv2.TM_CCOEFF_NORMED)_, mv, _, ml = cv2.minMaxLoc(res)if mv>best['val']:best.update({'val':mv,'angle':angle,'pt':ml})# 2. 精细旋转
angle0 = best['angle']
for da in np.linspace(angle0-14, angle0+14, 29):M = cv2.getRotationMatrix2D((w_t/2,h_t/2), da,1)rot = cv2.warpAffine(tpl, M, (w_t,h_t))res = cv2.matchTemplate(gray, rot, cv2.TM_CCOEFF_NORMED)_, mv, _, ml = cv2.minMaxLoc(res)if mv>best['val']:best.update({'val':mv,'angle':da,'pt':ml})# 绘制
x,y = best['pt']; ang=best['angle']
rect = ((x+w_t/2, y+h_t/2),(w_t,h_t),ang)
box = cv2.boxPoints(rect).astype(int)
out = scene.copy()
cv2.drawContours(out,[box],0,(0,255,0),2)
cv2.imshow('Two-stage Rotation Match', out)
cv2.waitKey(0)
cv2.destroyAllWindows()

模板匹配
关键函数解析:

  • 两阶段旋转:粗步长快速锁区,细步长提升精度
  • np.linspace(start, end, num):生成精细角度列表
  • cv2.boxPoints(rect):从旋转矩形参数获得顶点坐标,自动处理浮点角度

3. 零件定位实战

在生产线检测中,需对相机视野下的标准零件进行自动定位。结合多尺度与旋转不变模板匹配,可构建一个鲁棒的零件定位系统。

3.1 实现过程

  • 读取生产线图像与零件模板
  • 首轮多尺度匹配:快速定位大致位置与尺度范围
  • 在该子图中执行旋转不变匹配:细化角度与位置
  • 输出结果:在原图上绘制最终带角度的矩形框,并标注坐标与角度
import cv2, numpy as np# 功能:结合多尺度和旋转匹配,实现工业零件的精确定位
scene = cv2.imread('line.jpeg')
gray_scene = cv2.cvtColor(scene, cv2.COLOR_BGR2GRAY)
template = cv2.imread('part.jpg', cv2.IMREAD_GRAYSCALE)
h_t, w_t = template.shape[:2]# 1. 多尺度粗定位
best_scale = 1.0; best_val = -1; best_pt = (0,0)
for scale in np.linspace(0.7, 1.3, 13):temp = cv2.resize(template, None, fx=scale, fy=scale)th, tw = temp.shape[:2]if gray_scene.shape[0]<th or gray_scene.shape[1]<tw: continueres = cv2.matchTemplate(gray_scene, temp, cv2.TM_CCOEFF_NORMED)_, val, _, pt = cv2.minMaxLoc(res)if val>best_val:best_val, best_scale, best_pt = val, scale, pt# 2. 在 ROI 中旋转细化
x0, y0 = best_pt
scaled_t = cv2.resize(template, None, fx=best_scale, fy=best_scale)
th, tw = scaled_t.shape[:2]
roi = gray_scene[y0:y0+th, x0:x0+tw]best_ang, best_loc, best_val2 = 0, (0,0), -1
for angle in range(0,360,5):M = cv2.getRotationMatrix2D((tw/2, th/2), angle, 1.0)rot = cv2.warpAffine(scaled_t, M, (tw, th))res = cv2.matchTemplate(roi, rot, cv2.TM_CCOEFF_NORMED)_, val, _, loc = cv2.minMaxLoc(res)if val>best_val2:best_val2, best_ang, best_loc = val, angle, loc# 3. 绘制最终定位框
fx, fy = best_loc
center = (int(x0+fx+tw/2), int(y0+fy+th/2))
rect = (center, (int(tw), int(th)), best_ang)
box = cv2.boxPoints(rect).astype(int)
output = scene.copy()
cv2.drawContours(output, [box], 0, (255,0,0), 2)
cv2.putText(output, f'Scale:{best_scale:.2f} Angle:{best_ang}',(center[0], center[1]-10), cv2.FONT_HERSHEY_SIMPLEX,0.6, (0,0,255), 2)
cv2.imshow('Industrial Part Localization', output)
cv2.waitKey(0)
cv2.destroyAllWindows()

模板匹配

关键函数解析:

  • 多阶段 ROI 缩小:先用 matchTemplate 粗定位减少后续旋转匹配计算量
  • cv2.resize(template, fx, fy):在多尺度匹配中动态调整模板大小
  • cv2.matchTemplate(roi, rot, method):在 ROI 子图上执行旋转模板的精细匹配
  • cv2.boxPoints + drawContours:最终在原图上绘制带有角度信息的最优匹配区域

3.2 优化思路

  • 边缘+模板叠加:先用 Canny 提取场景边缘,再做模板匹配,兼顾结构与灰度信息
  • 多模板策略:针对零件正、侧、反面准备多模板,匹配后通过置信度最高者定位
  • 匹配后验证:在 ROI 内对比直方图或轮廓特征,剔除误匹配
import cv2, numpy as npscene = cv2.imread('line.jpeg')
gray = cv2.cvtColor(scene, cv2.COLOR_BGR2GRAY)
tpl = cv2.imread('part.jpg', cv2.IMREAD_GRAYSCALE)# 1. 提取场景边缘
edges_scene = cv2.Canny(gray, 50,150)
edges_tpl = cv2.Canny(tpl, 50,150)# 2. 多模板(原+180°)
templates = [edges_tpl, cv2.rotate(edges_tpl, cv2.ROTATE_180)]best = {'val':-1,'tpl':None,'pt':(0,0)}
for temp in templates:res = cv2.matchTemplate(edges_scene, temp, cv2.TM_CCOEFF_NORMED)_, mv, _, ml = cv2.minMaxLoc(res)if mv>best['val']:best.update({'val':mv,'tpl':temp,'pt':ml})# 3. 验证ROI直方图相似度
x,y = best['pt']; h,w = tpl.shape[:2]
roi = gray[y:y+h, x:x+w]
h1 = cv2.calcHist([roi],[0],None,[256],[0,256])
h2 = cv2.calcHist([tpl],[0],None,[256],[0,256])
sim = cv2.compareHist(h1,h2,cv2.HISTCMP_CORREL)
if sim<0.6:print("验证失败")
else:cv2.rectangle(scene,(x,y),(x+w,y+h),(0,0,255),2)cv2.imshow('Verified Localization', scene)cv2.waitKey(0)cv2.destroyAllWindows()

模板匹配

关键函数解析:

  • cv2.rotate(src, flag):快速生成旋转 180° 的模板
  • 双模板 边缘匹配:增强结构鲁棒性
  • cv2.compareHist(h1, h2, HISTCMP_CORREL):利用灰度直方图相关性验证匹配可靠性

小结

本文围绕模板匹配在实际场景中的应用展开,重点解决了目标因尺度变化与角度旋转所带来的匹配难题。首先通过多尺度模板匹配策略,使得模板在不同缩放比例下都能与场景图像进行有效比对,从而应对目标大小不一致的问题;接着引入旋转不变性匹配,通过对模板进行角度遍历旋转,实现了对任意方向目标的稳健识别;最后在零件定位实战中,将这两者有机结合,构建了一个既能适应尺度变化又能处理旋转干扰的鲁棒匹配系统。

系列链接

OpenCV计算机视觉实战(1)——计算机视觉简介
OpenCV计算机视觉实战(2)——环境搭建与OpenCV简介
OpenCV计算机视觉实战(3)——计算机图像处理基础
OpenCV计算机视觉实战(4)——计算机视觉核心技术全解析
OpenCV计算机视觉实战(5)——图像基础操作全解析
OpenCV计算机视觉实战(6)——经典计算机视觉算法
OpenCV计算机视觉实战(7)——色彩空间详解
OpenCV计算机视觉实战(8)——图像滤波详解
OpenCV计算机视觉实战(9)——阈值化技术详解
OpenCV计算机视觉实战(10)——形态学操作详解
OpenCV计算机视觉实战(11)——边缘检测详解
OpenCV计算机视觉实战(12)——图像金字塔与特征缩放
OpenCV计算机视觉实战(13)——轮廓检测详解
OpenCV计算机视觉实战(14)——直方图均衡化
OpenCV计算机视觉实战(15)——霍夫变换详解
OpenCV计算机视觉实战(16)——图像分割技术
OpenCV计算机视觉实战(17)——特征点检测详解
OpenCV计算机视觉实战(18)——视频处理详解
OpenCV计算机视觉实战(19)——特征描述符详解
OpenCV计算机视觉实战(20)——光流法运动分析

http://www.dtcms.com/a/349589.html

相关文章:

  • 将盾CDN:高防CDN和游戏盾有什么区别?
  • 宋红康 JVM 笔记 Day07|本地方法接口、本地方法栈
  • More Effective C++ 条款08:理解各种不同意义的new和delete
  • Genymotion 虚拟机如何安装 APK?(ARM 插件安装教程)
  • (操作系统)死锁是什么 必要条件 解决方式
  • 5分钟发布技术博客:cpolar简化Docsify远程协作流程
  • 《 nmcli网络管理学习》
  • [新启航]医疗器械深孔加工:新启航激光频率梳攻克 130mm 深度,实现 2μm 精度测量
  • Windows Server 2019 DateCenter搭建 FTP 服务器
  • MOLEX莫仕/莫莱克斯借助PCIe发展,引领数据中心的未来
  • 从Java全栈到前端框架的深度探索
  • gte2_common的作用
  • 数据集成平台-Kafka实时同步Doris能力演示
  • Appium学习笔记
  • 如何判断投手甲的认知比投手乙高?
  • “华生科技杯”2025年全国青少年龙舟锦标赛在海宁举行
  • 暴雨蓝色预警发布:我国多地将迎强降雨,局地伴有强对流天气 疾风气象大模型
  • 《李沐读论文》系列笔记:论文读写与研究方法【更新中】
  • 【机器学习】(11) --回归树算法
  • 【机器学习基础】朴素贝叶斯算法详解:从原理到实战
  • 机器学习-朴素贝叶斯
  • 机器学习采样方法深度详解:过采样、下采样与混合采样(附完整代码、可视化与多场景实战)
  • 机器学习:贝叶斯派
  • 【Linux | 网络】多路转接IO之poll
  • 编写Linux下usb设备驱动方法:usb设备驱动实现流程
  • AI-调查研究-60-机器人 机械臂技术发展趋势详解:工业、服务与DIY三大阵营全解析
  • rabbitmq集群
  • 基于RFM模型的客户群体大数据分析及用户聚类系统的设计与实现
  • AI+数据库:国内DBA职业发展与国产化转型实践
  • Torch入门小知识点--总结性语言