Ultralytics YOLO27: 加入候补名单。
Get Started
计算机视觉基础·选择任务·课程 4/10
课程初级

框与掩码:什么时候你才真正需要分割

在真实图像上衡量掩码相比框多提供了什么,并根据下游使用方判断项目是否需要形状信息。

大多数人想象 AI 视觉时,脑海中浮现的就是掩码;而掩码也是最容易让团队耗尽标注预算的部分。掩码能告诉你目标的精确形状;框只能告诉你目标的位置。这种差异会彻底决定某些产品的方案,但对大多数产品毫无影响。

学习成果

在一张图像上衡量框与掩码的差异,并根据读取输出的代码,为项目选择框或掩码。

快速通道
如果你已经熟悉相关内容,以下是简要版。
  1. 框提供位置;掩码提供形状、面积和精确边缘。

  2. 需要面积、镂空区域或间隙测量?用掩码。需要计数、区域判断、跟踪或裁剪?用框。

  3. 按目标计数:实例分割。只需区域覆盖范围:语义分割。

  4. 在相同分辨率下比较像素计数:retina_masks=True。

  5. 在确定标注预算前,计时比较标注 20 个框和 20 个多边形所需的时间。

实践操作

掩码能增加什么#

Ultralytics YOLO 对城市交叉路口进行实例分割,分别为每辆汽车、公交车和每位行人生成掩码

对于每个目标,分割任务会返回检测器提供的所有信息——类别、框、置信度——并额外提供属于该目标的像素,因此上图中的每辆汽车都有自己的轮廓,与公交车和行人分开。你可以将掩码读取为多边形(masks.xy)或二值图像(masks.data)。

形状会对数值产生多大影响?比较每个目标的框面积和掩码面积:

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]

for box, mask in zip(result.boxes, result.masks.data):
    x1, y1, x2, y2 = box.xyxy[0].tolist()
    box_area = (x2 - x1) * (y2 - y1)
    mask_area = float(mask.sum())  # pixels inside the object's mask
    name, ratio = result.names[int(box.cls)], mask_area / box_area
    print(f"{name:7} box={box_area:7.0f} px  mask={mask_area:7.0f} px  ({ratio:.0%})")
person  box=  97116 px  mask=  49331 px  (51%)
person  box=  68102 px  mask=  21316 px  (31%)
bus     box= 410093 px  mask= 255265 px  (62%)
person  box=  55998 px  mask=  33524 px  (60%)
person  box=  23690 px  mask=  11109 px  (47%)

公交车只占其框面积的 62%,一个行人只占 31%。仅根据框计算的任何数值——涂漆面积、溢出面积、货架空置面积——误差都会达到这个幅度。如果你的产品要报告这类数值,就需要掩码。如果产品只负责计数或定位,额外的像素毫无作用。

在相同分辨率下比较像素数量

症状:掩码面积看起来比边界框面积小了 3–4 倍。原因:默认情况下,masks.data 返回的是推理尺寸的结果(这里为 640×480),而边界框使用的是原始图像像素坐标(1080×810)。解决方法:像上面那样传入 retina_masks=True,或自行缩放计数值。

根据使用方做决定,而不是看演示效果#

下游需求框够用吗?原因
计数目标或检查目标是否存在是位置就是全部所需信息
目标进入某个区域时发出警报是判断框是否越过一条线就够了
跨帧跟踪目标是跟踪器会关联各帧中的框
为第二个分类器裁剪图像是裁剪只需要框
测量面积或覆盖范围(杂草、锈蚀、溢出)否不规则形状会让框面积失去意义
抠出目标(背景移除、AR)否你需要精确的边缘
检查间隙或接触情况(机器人抓取、碰撞)否框会夸大目标尺寸
分离相互接触的不规则目标通常不够重叠的框难以区分像素分别属于哪个目标

如果产品需要的每一项都回答“是”,就部署检测器。只有在形状确实有用时,才选择 YOLO26 分割。

实例分割与语义分割#

实例分割会为每个目标生成独立掩码,因此三辆重叠的汽车仍然是三辆车。语义分割只为每个像素标注类别,不提供其他信息:所有汽车像素都会合并成一个“汽车”区域,目标数量也就无从得知。要计数或测量单个目标——例如细胞、水果、缺陷——使用实例分割。要测量区域覆盖范围——例如道路与人行道、作物与土壤——使用语义分割。

标注形状的成本#

标注一个框只需点击两次。标注一个优质多边形可能需要点击几十次,而且每条边界都可能引发标注人员分歧:袋子算不算人物的一部分?阴影算不算汽车的一部分?与其相信经验法则,不如测量你自己的成本:让一位标注人员分别用框和多边形标注 20 个目标,记录耗时差异,再乘以数据集规模。

AI 辅助工具可以大幅缩小这种差距。智能标注和批量标注可以根据一次点击或一个框生成掩码,让标注人员修正边缘,而不必描摹整个轮廓——但仍然需要有人检查每条边界。

在绘制第一个掩码之前写好边界规则

在开始标注之前,先决定如何处理遮挡、透明物体、阴影、被画面边缘截断的物体,以及附属物品(包、把手、货物),并把同一份说明提供给标注人员和审核人员。标注最佳实践介绍了如何落实这些规则;COCO-Seg 数据集展示了如何大规模保持掩码一致。

抽查掩码时,留意越界(掩码溢出边缘)、空洞(目标内部区域漏标)、伪边缘(掩码沿着 JPEG 压缩伪影延伸)和模糊部位(行李箱把手是否属于行李箱掩码?)。

试试看

在你自己项目(或 bus.jpg)的一张图像上运行框与掩码对比脚本。记录主要类别的掩码与框面积比,在使用方表格中找到符合下游需求的那一行,然后写一句话:“框”或“掩码”,并说明做出决定的使用方。

完成条件
满足以下所有条件,即表示你已完成本课程。
  • 你已经测得主要类别的掩码与框面积比。

  • 你已经写下一句话,选择使用框或掩码,并指出做出决定的下游使用方。

  • 如果你选择了掩码,你已经计时比较了 20 个框和 20 个多边形的标注耗时,并列出了边界规则。

显示解答

假设两个产品共用一台公交车摄像头:一个人数计数器,一个道路占用率报告,用于说明车辆覆盖了多少场景区域。用同一次运行测量这两个结果:

from ultralytics import YOLO

model = YOLO("yolo26n-seg.pt")
result = model("https://ultralytics.com/images/bus.jpg", retina_masks=True, verbose=False)[0]
height, width = result.orig_shape

for name in sorted({result.names[int(c)] for c in result.boxes.cls}):
    rows = [i for i, c in enumerate(result.boxes.cls) if result.names[int(c)] == name]
    union = result.masks.data[rows].amax(dim=0)  # merge masks so overlaps count once
    box_union = sum(float((b[2] - b[0]) * (b[3] - b[1])) for b in result.boxes.xyxy[rows])
    print(f"{name:6} {len(rows)} objects | masks cover {float(union.sum()) / (height * width):.0%} of the image, boxes {box_union / (height * width):.0%}")
bus    1 objects | masks cover 29% of the image, boxes 47%
person 4 objects | masks cover 13% of the image, boxes 28%
  • 人数计数器 → 框。无论使用哪种方式,计数都是 4;掩码会增加标注成本,却不会改变结果。
  • 占用率报告 → 掩码。框显示公交车覆盖了场景的 47%;实际覆盖率只有 29%。基于框的报告误差超过一半。
接下来做什么

下一节:两个专用任务——输出骨架的姿态估计,以及框会旋转的 OBB。

登录以保存学习进度