Python实现检测小图是否为大图的一部分(支持水平缩放)
解决水平缩放小图的大图匹配问题
针对小图存在水平缩放时的大图匹配需求,cv2.matchTemplate()因模板尺寸固定无法适配缩放情况,以下提供两种无需分割图像的解决方案:
1. 定向多尺度模板匹配(针对水平缩放)
仅针对水平方向生成不同缩放比例的模板,遍历匹配后取最高相似度值:
import cv2 import numpy as np def is_scaled_template_in_img(img, templ, scale_range=(0.5, 2.0), step=0.1): max_similarity = 0.0 img_width = img.shape[1] templ_height, templ_width = templ.shape[:2] # 遍历水平方向的缩放比例 for scale in np.arange(scale_range[0], scale_range[1], step): # 仅水平缩放,保持高度不变 scaled_templ = cv2.resize(templ, (int(templ_width * scale), templ_height)) # 跳过宽度超过原图的模板 if scaled_templ.shape[1] > img_width: continue # 执行模板匹配 result = cv2.matchTemplate(img, scaled_templ, cv2.TM_CCORR_NORMED) current_max = cv2.minMaxLoc(result)[1] if current_max > max_similarity: max_similarity = current_max return max_similarity # 主逻辑 img = cv2.imread('img.jpg', 0) path = 'symbols' match_results = {} for symb_no in range(1, 10): symbol = cv2.imread(f"{path}/{symb_no}.png", 0) # 先做高度适配(根据需求调整) symbol = cv2.resize(symbol, (symbol.shape[1], int(symbol.shape[0] * img.shape[1]/img.shape[0]))) match_results[symb_no] = is_scaled_template_in_img(img, symbol) # 按相似度降序输出 sorted_results = sorted(match_results.items(), key=lambda x: -x[1]) print({k: v for k, v in sorted_results})
- 核心思路:针对水平方向生成一系列缩放模板,逐个匹配并记录最高相似度,以此判断小图是否存在。
- 优化建议:根据实际场景调整
scale_range和step,缩小范围或减小步长来平衡精度与效率。
2. SIFT特征匹配(尺度不变性方案)
SIFT特征自带尺度不变性,无需考虑缩放比例即可完成匹配:
import cv2 import numpy as np def sift_match(img, templ, threshold=0.7, match_count_thresh=5): # 初始化SIFT检测器 sift = cv2.SIFT_create() # 提取特征点与描述符 kp1, des1 = sift.detectAndCompute(img, None) kp2, des2 = sift.detectAndCompute(templ, None) # FLANN高效匹配器 index_params = dict(algorithm=1, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1, des2, k=2) # 筛选优质匹配点 good_matches = [] for m, n in matches: if m.distance < threshold * n.distance: good_matches.append(m) # 根据匹配点数量判断是否存在 return len(good_matches) > match_count_thresh # 主逻辑 img = cv2.imread('img.jpg', 0) path = 'symbols' match_results = {} for symb_no in range(1, 10): symbol = cv2.imread(f"{path}/{symb_no}.png", 0) match_results[symb_no] = sift_match(img, symbol) print(match_results)
- 核心优势:无需手动调整缩放比例,依赖特征点匹配稳定性更强,适合复杂场景。
- 注意事项:OpenCV 4.x需提前安装
opencv-contrib-python包;若小图特征较少,需调整match_count_thresh阈值。
内容的提问来源于stack exchange,提问作者istymisty
相关产品推荐
相关产品推荐

