为何cv.matchShapes()对位置不同的相同图形检测出巨大差异?
问题
我写了一段对比两个几何图形的代码,把比较阈值设为0.1,大部分情况结果没问题,但遇到仅位置(x、y坐标)不同的相似图形时,cv.matchShapes()返回的差异值特别大(示例中differences=7.842170693643041),远超阈值。我希望这类情况能判定为图形相同或近似,该怎么改?
原代码如下:
def compare_two_figure(template, figure_for_compare): template = cv.imread(template, cv.IMREAD_GRAYSCALE) figure_for_compare = cv.imread(figure_for_compare, cv.IMREAD_GRAYSCALE) _, thresh_template = cv.threshold(template, 127, 255, 0) _, thresh_figure_for_compare = cv.threshold(figure_for_compare, 127, 255, 0) contours_template, _ = cv.findContours(thresh_template, cv.RETR_TREE, cv.CHAIN_APPROX_NONE) particular_contour_template = contours_template[1] contours_figure_for_compare, _ = cv.findContours(thresh_figure_for_compare, cv.RETR_TREE, cv.CHAIN_APPROX_NONE) particular_contour_figure_for_compare = contours_figure_for_compare[1] differences = cv.matchShapes(particular_contour_template, particular_contour_figure_for_compare, 1, 0.0) print(differences) if differences < 0.1: return "same" else: return "different" print(compare_two_figure('files/template_14_v2.jpg', 'searching_figure_14.jpg'))
解决方案
问题根源是cv.matchShapes()理论上支持平移不变,但实际提取轮廓时,图形在画布中的位置偏移、轮廓层级的不稳定,会导致匹配值异常。可以通过归一化轮廓特征+优化轮廓提取逻辑解决:
1. 核心思路
- 对提取的轮廓做平移归一化:将轮廓质心移至原点,消除位置差异
- 对轮廓做尺寸归一化:等比例缩放至固定尺寸,消除画布大小影响
- 优化轮廓提取:筛选有效轮廓,避免固定索引依赖层级的问题
2. 修改后的代码
import cv2 as cv import numpy as np def normalize_contour(contour, target_size=(200, 200)): # 计算轮廓质心 M = cv.moments(contour) if M["m00"] == 0: return contour cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) # 平移轮廓至原点 contour_shifted = contour - np.array([cx, cy]) # 等比例缩放至目标尺寸 x, y, w, h = cv.boundingRect(contour_shifted) max_dim = max(w, h) if max_dim == 0: return contour_shifted scale = target_size[0] / max_dim contour_scaled = contour_shifted.astype(np.float32) * scale return contour_scaled.astype(np.int32) def get_main_contour(thresh_img): # 提取最外层轮廓,避免层级干扰 contours, _ = cv.findContours(thresh_img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 返回面积最大的有效轮廓(排除噪点) if not contours: return None return max(contours, key=cv.contourArea) def compare_two_figure(template, figure_for_compare): template = cv.imread(template, cv.IMREAD_GRAYSCALE) figure_for_compare = cv.imread(figure_for_compare, cv.IMREAD_GRAYSCALE) _, thresh_template = cv.threshold(template, 127, 255, 0) _, thresh_figure_for_compare = cv.threshold(figure_for_compare, 127, 255, 0) # 获取主轮廓,替代固定索引取值 particular_contour_template = get_main_contour(thresh_template) particular_contour_figure_for_compare = get_main_contour(thresh_figure_for_compare) if particular_contour_template is None or particular_contour_figure_for_compare is None: return "different" # 归一化轮廓的位置与尺寸 norm_template = normalize_contour(particular_contour_template) norm_compare = normalize_contour(particular_contour_figure_for_compare) differences = cv.matchShapes(norm_template, norm_compare, cv.CONTOURS_MATCH_I1, 0.0) print(differences) if differences < 0.1: return "same" else: return "different" print(compare_two_figure('files/template_14_v2.jpg', 'searching_figure_14.jpg'))
关键修改说明
normalize_contour函数:通过质心平移消除位置差异,等比例缩放统一轮廓尺寸,让匹配仅关注形状特征get_main_contour函数:改用RETR_EXTERNAL提取最外层轮廓,再筛选面积最大的轮廓,避免原代码中contours[1]的层级依赖问题- 匹配时使用
cv.CONTOURS_MATCH_I1常量替代数字1,提升代码可读性
修改后,仅位置不同的相似图形会被正确判定为"same",同时保留对形状差异的识别能力。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

