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

OpenCV同色连通图形场景下的线段检测方法咨询

现有实现与问题

待检测图像-线段与图形异色
待检测图像-线段与图形异色

针对上图这类线段为蓝色、与连接图形颜色不同的场景,可通过HSV颜色掩码提取线段轮廓,核心代码如下:

img = cv2.imread("./more5.png")#"https://i.sstatic.net/LiLPv.png"
blueLower = np.array([50,50,50])
blueUpper = np.array([130,255,255])
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
blue_mask = cv2.inRange(hsv, blueLower, blueUpper)
blue = cv2.bitwise_and(img ,img ,mask=blue_mask)
contours, hierarchy = cv2.findContours(preprocess(blue), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

线段检测效果
线段检测效果

完整可运行实现代码如下:

import numpy as np
import cv2

def preprocess(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_blur = cv2.GaussianBlur(img_gray, (5, 5), 1)
    img_canny = cv2.Canny(img_blur, 50, 50) # edge cascade
    kernel = np.ones((3, 3))
    img_dilate = cv2.dilate(img_canny, kernel, iterations=1) 
    img_erode = cv2.erode(img_dilate, kernel, iterations=1)
    return img_erode

def find_tip(points, convex_hull):
    length = len(points)
    indices = np.setdiff1d(range(length), convex_hull)

    for i in range(2):
        j = indices[i] + 2
        if j > length - 1:
            j = length - j
        if np.all(points[j] == points[indices[i - 1] - 2]):
            return tuple(points[j])

img = cv2.imread("./more5.png")

blueLower = np.array([50,50,50])
blueUpper = np.array([130,255,255])

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
blue_mask = cv2.inRange(hsv, blueLower, blueUpper)
blue = cv2.bitwise_and(img ,img ,mask=blue_mask)

contours, hierarchy = cv2.findContours(preprocess(blue), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) 

print(f"Contours size {len(contours)}")

for cnt in contours:
    peri = cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, 0.004 * peri, True)
    hull = cv2.convexHull(approx, returnPoints=False)
    sides = len(hull)

    if 6 > sides > 3 and sides + 2 == len(approx):
        arrow_tip = find_tip(approx[:,0,:], hull.squeeze())
        if arrow_tip:
            cv2.drawContours(img, [cnt], -1, (0, 255, 0), 3)
            cv2.circle(img, arrow_tip, 3, (0, 0, 255), cv2.FILLED)

cv2.imshow("Image", img)
cv2.waitKey(0)

上述方案依赖颜色差异分割线段,当线段与其连接的图形颜色完全一致时,颜色掩码无法分离线段和主体图形,方法直接失效,同色测试场景如下图:
待检测图像-线段与图形同色
待检测图像-线段与图形同色

同色场景线段检测可行思路
  • 骨架提取筛选法:对整个同色连通域做细化(骨架提取)操作,将所有填充图形、线段压缩为单像素宽度的连通线条,再基于拓扑特征筛选线段:线段有且仅有一个端点和主体图形的骨架连接,另一端为自由端点,且线段本身无分叉、曲率极低,和封闭图形的骨架特征差异明显。OpenCV可通过cv2.ximgproc.thinning接口实现快速骨架提取。
  • 距离变换分割法:对二值化后的同色连通域做距离变换,计算每个前景像素到最近背景像素的距离。线段区域的距离值稳定等于线段宽度的1/2,而主体填充图形内部的距离值会远大于该数值,通过阈值即可把窄条状的线段区域从大连通域中分割出来,后续再复用原有轮廓检测逻辑即可。
  • 霍夫直线校验法:对边缘检测结果做概率霍夫直线检测,筛选出符合长度阈值的候选线段,再对每个候选线段做邻域校验:如果线段其中一端的邻域是大面积同色填充区域、另一端邻域为背景,且线段两侧的像素一侧为前景、一侧为背景,即可判定为连接在图形上的目标线段。
  • 角点切分法:用Shi-Tomasi角点检测接口识别同色连通域轮廓上的所有强角点,这类角点就是线段和主体图形的连接分界位置,沿角点把整个连通域轮廓切分为独立片段,再根据片段的长宽比筛选:线段对应的轮廓片段长宽比远大于主体图形的常规边,可直接通过阈值过滤。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:57:14