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

YOLO8实例分割单物体检测出现多轮廓的原因及筛选方法

问题:YOLO8分割Mask对应多个轮廓的原因及解决方法

经过进一步实验后,我已修改问题!

我正在进行YOLO8相关实验,编写了如下测试脚本以复现问题:

#
#  test script to explore multi contours for 1 found object.
import numpy as np
import cv2

img = cv2.imread('demo.jpg')
imgcopy = img.copy()


from ultralytics import YOLO

# Load a model
model = YOLO('yolov8n-seg.pt')  # load an official model
#model = YOLO('path/to/best.pt')  # load a custom model

# Predict with the model
results = model(img, retina_masks=True, save=True, imgsz = 640, conf=0.5, boxes=False, show_labels=False, show_conf=False, save_crop=True, save_txt=True, save_conf=True)  # predict on an image



for result in results:
    masks = result.masks
    for mask in masks.data:
        mask_np= mask.cpu().numpy().astype(np.uint8)
        #contours, _  = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contours, _  = cv2.findContours(mask_np, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

        print ('We found ' + str(len(contours)) + ' objects')

        # i know i get 3 contours so this works !
        imgcopy = cv2.drawContours(imgcopy, contours=contours[0], contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
        imgcopy = cv2.drawContours(imgcopy, contours=contours[1], contourIdx=-1, color=(255, 255, 0), thickness=2, lineType=cv2.LINE_AA)
        imgcopy = cv2.drawContours(imgcopy, contours=contours[2], contourIdx=-1, color=(255,0, 255), thickness=2, lineType=cv2.LINE_AA)

cv2.imwrite('result.jpg', imgcopy)

我预期单个检测到的物体对应1个轮廓,但在该示例中却得到了3个轮廓。

输入图片:输入图片

输出图片:输出图片

即单个检测物体对应的mask存在3个轮廓,请问为何会出现这种情况?应如何筛选出正确的轮廓?我发现首个轮廓并非总是正确的。


解答

一、出现多个轮廓的原因

  1. 轮廓检索模式的影响:你使用的cv2.RETR_TREE模式会提取所有层级的轮廓,包括物体的外部轮廓、内部孔洞轮廓(比如鞋子的鞋带孔、鞋头镂空部分),因此会出现多个轮廓结果。
  2. Mask的精细度:YOLO8的retina_masks=True会输出高精度分割掩码,保留了物体的细微镂空结构,这些结构在OpenCV轮廓检测时会被识别为独立的内部轮廓。

二、筛选正确轮廓的方法

方法1:改用外部轮廓检索模式

直接替换为cv2.RETR_EXTERNAL模式,该模式仅提取最外层的外部轮廓,忽略所有内部孔洞的轮廓,代码修改如下:

contours, _  = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

这是最直接的解决方式,能直接得到单个主物体轮廓。

方法2:通过轮廓面积筛选

如果需要保留内部轮廓但只关注主物体轮廓,可以通过计算轮廓面积排序,选择面积最大的轮廓(主物体外部轮廓面积远大于内部孔洞轮廓):

contours, _  = cv2.findContours(mask_np, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 按轮廓面积降序排序
contours_sorted = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
# 取面积最大的轮廓作为主物体轮廓
main_contour = contours_sorted[0]
# 绘制主轮廓
imgcopy = cv2.drawContours(imgcopy, [main_contour], -1, (0,255,0), 2, cv2.LINE_AA)

这种方法能避免依赖轮廓顺序,解决“首个轮廓并非总是正确”的问题。

方法3:直接使用YOLO8内置轮廓

Ultralytics YOLO8的mask对象已经提供了xy属性,可以直接获取处理好的轮廓点,无需再用OpenCV提取:

for result in results:
    masks = result.masks
    for mask in masks:
        # 直接获取轮廓点,转换为OpenCV需要的整数格式
        contour = mask.xy[0].astype(np.int32)
        imgcopy = cv2.drawContours(imgcopy, [contour], -1, (0,255,0), 2, cv2.LINE_AA)

这种方法更高效,且能避免手动提取轮廓带来的多轮廓问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:56:10