如何用OpenCV和Python检测近似匹配的模板图案(非深度学习)
近似匹配被划掉方框的OpenCV实现方案
方法1:多尺度模板匹配
matchTemplate仅支持同尺寸目标匹配,通过对模板进行多尺度缩放,在不同尺度下执行模板匹配,可覆盖大小近似的目标。步骤如下:
- 定义合理的缩放比例范围(比如0.5到1.5,步长0.1)
- 逐次缩放模板,执行模板匹配
- 设定匹配阈值,筛选符合条件的结果
代码示例:
import cv2 import numpy as np # 加载原图和模板(灰度模式) img = cv2.imread('data/wet/6.jpg', cv2.IMREAD_GRAYSCALE) template = cv2.imread('data/wet/9.jpg', cv2.IMREAD_GRAYSCALE) template_h, template_w = template.shape # 匹配阈值(需根据实际图像调整) threshold = 0.8 # 缩放比例范围 scale_range = np.arange(0.5, 1.5, 0.1) img_copy = img.copy() for scale in scale_range: # 缩放模板 scaled_template = cv2.resize(template, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA) scaled_h, scaled_w = scaled_template.shape if scaled_h > img.shape[0] or scaled_w > img.shape[1]: continue # 跳过比原图尺寸大的模板 # 用归一化相关系数执行匹配 res = cv2.matchTemplate(img, scaled_template, cv2.TM_CCOEFF_NORMED) # 提取所有超过阈值的匹配位置 loc = np.where(res >= threshold) # 绘制匹配框 for pt in zip(*loc[::-1]): cv2.rectangle(img_copy, pt, (pt[0] + scaled_w, pt[1] + scaled_h), 255, 2) # 保存并展示结果 cv2.imwrite('data/wet/multi_scale_match.jpg', img_copy) cv2.imshow('Multi-scale Match', img_copy) cv2.waitKey(0) cv2.destroyAllWindows()
方法2:基于Hu矩的轮廓匹配
Hu不变矩具备旋转、缩放、平移不变性,适合检测形状近似的目标。步骤如下:
- 对模板和原图做二值化处理,提取轮廓
- 计算模板轮廓的Hu矩
- 遍历原图轮廓,计算每个轮廓的Hu矩并与模板比对,用相似度阈值筛选匹配目标
代码示例:
import cv2 import numpy as np # 加载模板和原图(灰度模式) template = cv2.imread('data/wet/9.jpg', cv2.IMREAD_GRAYSCALE) img = cv2.imread('data/wet/6.jpg', cv2.IMREAD_GRAYSCALE) # 二值化(可根据图像明暗调整阈值,或用cv2.THRESH_OTSU自动计算) _, template_bin = cv2.threshold(template, 127, 255, cv2.THRESH_BINARY_INV) _, img_bin = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) # 提取模板轮廓 template_contours, _ = cv2.findContours(template_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not template_contours: print("未检测到模板轮廓") exit() # 取模板的最大轮廓 template_contour = max(template_contours, key=cv2.contourArea) # 计算模板的Hu矩并做对数转换增强区分度 template_hu = cv2.HuMoments(cv2.moments(template_contour)).flatten() template_hu = -np.sign(template_hu) * np.log10(np.abs(template_hu)) # 提取原图轮廓 img_contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) img_copy = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 转彩色方便绘制彩色框 # 相似度阈值(数值越小,形状越相似,需调整) sim_threshold = 0.2 for contour in img_contours: # 计算当前轮廓的Hu矩 hu = cv2.HuMoments(cv2.moments(contour)).flatten() hu = -np.sign(hu) * np.log10(np.abs(hu)) # 计算与模板Hu矩的欧式距离 distance = np.linalg.norm(template_hu - hu) if distance < sim_threshold: # 绘制bounding rectangle x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(img_copy, (x, y), (x+w, y+h), (0, 255, 0), 2) # 保存并展示结果 cv2.imwrite('data/wet/hu_match.jpg', img_copy) cv2.imshow('Hu Moment Match', img_copy) cv2.waitKey(0) cv2.destroyAllWindows()
方法3:边缘多尺度匹配
先提取模板和原图的边缘特征,再进行多尺度模板匹配,可忽略图像纹理细节,仅匹配形状轮廓。步骤如下:
- 对模板和原图执行Canny边缘检测
- 对边缘模板做多尺度缩放,执行模板匹配
- 设定阈值筛选匹配结果
代码示例:
import cv2 import numpy as np # 加载图像 img = cv2.imread('data/wet/6.jpg', cv2.IMREAD_GRAYSCALE) template = cv2.imread('data/wet/9.jpg', cv2.IMREAD_GRAYSCALE) # 提取边缘(Canny阈值需根据图像调整) img_edges = cv2.Canny(img, 50, 150) template_edges = cv2.Canny(template, 50, 150) template_h, template_w = template_edges.shape threshold = 0.7 scale_range = np.arange(0.6, 1.4, 0.1) img_copy = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) for scale in scale_range: scaled_template = cv2.resize(template_edges, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA) scaled_h, scaled_w = scaled_template.shape if scaled_h > img_edges.shape[0] or scaled_w > img_edges.shape[1]: continue res = cv2.matchTemplate(img_edges, scaled_template, cv2.TM_CCOEFF_NORMED) loc = np.where(res >= threshold) for pt in zip(*loc[::-1]): cv2.rectangle(img_copy, pt, (pt[0]+scaled_w, pt[1]+scaled_h), (255, 0, 0), 2) # 保存并展示结果 cv2.imwrite('data/wet/edge_multi_match.jpg', img_copy) cv2.imshow('Edge Multi-scale Match', img_copy) cv2.waitKey(0) cv2.destroyAllWindows()
注意事项
- 所有阈值参数需根据实际图像调试,建议从中间值开始逐步调整
- 若目标存在旋转,可在多尺度匹配基础上加入旋转角度遍历(如每隔10度旋转模板),但会增加计算量
- 二值化步骤可尝试
cv2.THRESH_OTSU自动计算阈值,适配不同明暗的图像
内容的提问来源于stack exchange,提问作者Sahan Kodituwakku
相关产品推荐
相关产品推荐

