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

如何去除HED检测及cv2.ximgproc.thinning细化后二值轮廓的1px突出线条

二值细化轮廓1px突出毛刺去除方案

针对经过HED边缘检测+cv2.ximgproc.thinning细化得到的单像素宽轮廓,可通过以下两种OpenCV可实现的方案去除短突出毛刺:

方案1:形态学修剪(快速实现)

适合短毛刺长度固定的场景,通过迭代击中击不中变换检测并删除端点,实现修剪指定长度内的毛刺:

import cv2
import numpy as np

def morph_prune(src, prune_length=3):
    # 输入要求:二值图,前景255,背景0
    # 定义3x3击中击不中结构元,检测端点(8邻域仅1个前景像素)
    kernel = [
        np.array([[-1, -1, -1], [-1, 1, -1], [-1, 0, -1]], dtype=np.int8),
        np.array([[-1, -1, -1], [-1, 1, 0], [-1, -1, -1]], dtype=np.int8),
        np.array([[-1, -1, -1], [0, 1, -1], [-1, -1, -1]], dtype=np.int8),
        np.array([[-1, 0, -1], [-1, 1, -1], [-1, -1, -1]], dtype=np.int8),
        np.array([[-1, -1, 0], [-1, 1, -1], [-1, -1, -1]], dtype=np.int8),
        np.array([[0, -1, -1], [-1, 1, -1], [-1, -1, -1]], dtype=np.int8),
        np.array([[-1, -1, -1], [-1, 1, -1], [0, -1, -1]], dtype=np.int8),
        np.array([[-1, -1, -1], [-1, 1, -1], [-1, -1, 0]], dtype=np.int8),
    ]
    img = src.copy()
    for _ in range(prune_length):
        end_points = np.zeros_like(img)
        for k in kernel:
            hitmiss = cv2.morphologyEx(img, cv2.MORPH_HITMISS, k)
            end_points = cv2.bitwise_or(end_points, hitmiss)
        # 删除当前检测到的所有端点
        img = cv2.bitwise_and(img, cv2.bitwise_not(end_points))
    return img

# 调用示例
src = cv2.imread("your_thin_contour.png", 0)
_, binary = cv2.threshold(src, 127, 255, cv2.THRESH_BINARY)
pruned = morph_prune(binary, prune_length=3) # 修剪长度<=3px的毛刺

方案2:端点追踪修剪(精准可控)

适合需要避免误删主轮廓细节的场景,通过追踪端点路径长度判断是否为需要删除的毛刺:

def trace_prune(src, max_spur_length=3):
    img = src.copy()
    # 获取所有前景像素坐标
    y, x = np.where(img == 255)
    # 8邻域偏移
    offsets = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1)]
    
    for i in range(len(x)):
        cx, cy = x[i], y[i]
        if img[cy, cx] == 0:
            continue
        # 统计当前点8邻域前景数
        neighbor_count = 0
        neighbors = []
        for dx, dy in offsets:
            nx, ny = cx + dx, cy + dy
            if 0<=nx<img.shape[1] and 0<=ny<img.shape[0] and img[ny, nx] == 255:
                neighbor_count +=1
                neighbors.append((nx, ny))
        # 是端点
        if neighbor_count == 1:
            path = [(cx, cy)]
            current = (cx, cy)
            prev = None
            is_spur = True
            for _ in range(max_spur_length):
                nx, ny = neighbors[0] if neighbors[0] != prev else neighbors[1]
                prev = current
                current = (nx, ny)
                path.append(current)
                # 统计当前点邻域前景数
                neighbor_count = 0
                neighbors = []
                for dx, dy in offsets:
                    nnx, nny = current[0]+dx, current[1]+dy
                    if 0<=nnx<img.shape[1] and 0<=nny<img.shape[0] and img[nny, nnx] == 255:
                        neighbor_count +=1
                        neighbors.append((nnx, nny))
                # 走到交叉点,停止
                if neighbor_count >=3:
                    break
                # 走到另一个端点,说明是独立短线,也删除
                if neighbor_count ==1:
                    break
            else:
                # 长度超过阈值,不删除
                is_spur = False
            if is_spur:
                # 删除毛刺路径
                for (px, py) in path:
                    img[py, px] = 0
    return img

使用参考

待处理原图示例:
待处理轮廓示例

  • 若毛刺长度多为2-3px,设置prune_length=3即可完全去除,且不会损伤主轮廓
  • 若存在较长的有效突出细节,可降低阈值或使用方案2自定义保留规则

内容的提问来源于stack exchange,提问作者Angel Arias González

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 17:42:00