You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 17:37:07