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

OpenCV不使用RETR_EXTERNAL去除形状轮廓双层重复边缘的方法问询

解决方法

核心思路是通过cv2.findContours返回的层级信息hierarchy识别成对的内外嵌套轮廓,再对对应点坐标取平均值得到合并后的轮廓。

具体实现步骤:

  • 提取轮廓时保留RETR_TREE参数,拿到完整的层级关系,层级数组的四个值分别为[下一个同层级轮廓索引, 上一个同层级轮廓索引, 第一个子轮廓索引, 父轮廓索引]
  • 遍历所有轮廓,筛选出有子轮廓的外层轮廓,和对应的子轮廓(内层轮廓)组成配对
  • 由于内外轮廓的顶点数通常是一致的(比如空心正方形内外都是4个顶点),直接对每个对应位置的顶点坐标求平均,得到中间轮廓
  • 如果顶点数不一致,可以先将轮廓点重采样到相同数量再计算平均值

修改后的代码如下:

import cv2
import numpy as np

def extract_contours():
    path = 'test.png'
    blank = np.zeros((184,184,3), np.uint8)
    blank[:] = (255,255,255)
    raw = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    raw = 255 - raw
    img = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY)
    contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    used_indices = set()
    merged_contours = []
    
    for i in range(len(contours)):
        if i in used_indices:
            continue
        # 获取当前轮廓的层级信息
        h = hierarchy[0][i]
        # 有子轮廓,说明是外层轮廓,子轮廓是内层
        if h[2] != -1:
            outer_cnt = contours[i]
            inner_cnt = contours[h[2]]
            # 过滤面积过小的轮廓
            if cv2.contourArea(outer_cnt) < 400 or cv2.contourArea(inner_cnt) < 400:
                continue
            # 多边形近似,保证顶点数一致
            epsilon_outer = 0.009 * cv2.arcLength(outer_cnt, True)
            approx_outer = cv2.approxPolyDP(outer_cnt, epsilon_outer, True)
            epsilon_inner = 0.009 * cv2.arcLength(inner_cnt, True)
            approx_inner = cv2.approxPolyDP(inner_cnt, epsilon_inner, True)
            
            # 顶点数一致直接取平均
            if len(approx_outer) == len(approx_inner):
                merged = np.zeros_like(approx_outer)
                for j in range(len(approx_outer)):
                    merged[j][0][0] = (approx_outer[j][0][0] + approx_inner[j][0][0]) // 2
                    merged[j][0][1] = (approx_outer[j][0][1] + approx_inner[j][0][1]) // 2
                merged_contours.append(merged)
            # 标记已经处理过的轮廓
            used_indices.add(i)
            used_indices.add(h[2])
        # 没有子轮廓的单独轮廓直接保留
        else:
            if cv2.contourArea(contours[i]) > 400:
                approx = cv2.approxPolyDP(contours[i], 0.009 * cv2.arcLength(contours[i], True), True)
                merged_contours.append(approx)
    
    # 绘制合并后的轮廓
    for cnt in merged_contours:
        cv2.drawContours(blank, [cnt], 0, (0, 0, 255), 1)
    cv2.imwrite('contours.png', blank)

extract_contours()

注意事项:

  • 如果遇到内外轮廓顶点顺序不一致的情况,可以先通过匹配轮廓起始点(比如找左上角顶点对齐)再计算平均值,避免出现错位的问题
  • 对于非规则的嵌套轮廓,可以先对两个轮廓做采样,生成相同数量的点后再逐个求平均,兼容性更强

内容的提问来源于stack exchange,提问作者Zhehao Chen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 11:45:03