首页
/ Supervision `Detections` 完全指南:统一目标检测与分割结果的核心数据结构

Supervision `Detections` 完全指南:统一目标检测与分割结果的核心数据结构

2026-09-07 10:05:45作者:胡易黎Nicole

sv.Detections 是 supervision 目标检测模块的核心数据结构,它把来自 RF-DETR、Ultralytics YOLO、Transformers(DETR)、Roboflow Inference、SAM 乃至多种 VLM 等不同框架的推理结果,统一封装为 xyxy 边界框、mask 分割掩码、confidence 置信度、class_id 类别与 tracker_id 跟踪号等标准字段。读完本文,你将掌握如何从任意模型产出 Detections、如何进行过滤/切片/合并、如何调用内置 NMS 系列后处理与几何属性计算,并理解它与 tracker、annotator、tools 协同工作的底层约定。全文以 docs/detection/core.md 对应的 Detections API 文档为主体,源码证据均来自 src/supervision/detection/core.py

为什么需要 Detections:跨框架结果标准化

目标检测与分割生态中,每个推理框架(Ultralytics、Detectron2、MMDetection、Transformers、Roboflow Inference……)都返回各自定义的结果对象:字段命名不同(xyxy/xywh/归一化坐标)、数据在 GPU/CPU 上分布不同、分割掩码的编码方式更是千差万别。Detections 的作用就是把这些输出统一成一份一致的格式,从而让下游的 trackersannotators(画框、画掩码、画标签)与 tools(如 line_zonepolygon_zone)可以只面向一套 API 编程。

从源码看,core.py 中的 Detections 是一个 @dataclass,实例化时通过 __post_init__ 调用 _validate_detections_fields 对全部字段做一致性校验(如各字段长度是否与 xyxy 对齐),这保证了"非法的 Detections 对象很难被构造出来"这一基本不变量。此外,部分模型(如 RF-DETR)的 predict 方法会直接返回 sv.Detections 对象,无需任何转换步骤。

核心字段:一份检测结果里到底装了什么

Detections 的七个字段(core.py)构成了整份数据结构的骨架:

字段 类型 形状与含义 可否为 None
xyxy NDArray[np.number] (n, 4),边界框坐标 [x1, y1, x2, y2](必须提供)
mask NDArray[np.bool_] / CompactMask (n, H, W) 布尔分割掩码
confidence NDArray[np.floating] (n,) 置信度
class_id NDArray[np.integer] (n,) 类别 ID
tracker_id NDArray[np.integer] (n,) 跟踪器分配的 ID
data dict 每条检测的附加数据,值为 NumPy 数组或 list,默认 {}
metadata dict 集合级元数据,如视频名、相机参数、时间戳,适用于整批检测,默认 {}

需要重点区分的是两个"字典":

  • data逐条检测对齐的:例如 data["class_name"] 是一个长度为 n 的字符串数组,第 i 个元素对应第 i 个框的类别名;
  • metadata整个检测集合级别的,与某一条框没有一一对应关系。

data 里有两个由常量定义的固定 key,定义于 src/supervision/config.py

  • CLASS_NAME_DATA_FIELD = "class_name":类别名数组,几乎所有工厂方法都会回填;
  • ORIENTED_BOX_COORDINATES = "xyxyxyxy":旋转框(OBB)的四角坐标,值为 (n, 4, 2)float32 数组。该 key 的存在与否是判定"这份检测携带旋转框几何"的规范信号areawith_nmswith_nmm 的派发逻辑都据此判断。

Python 对象语义:len 与迭代

Detections 实现了序列语义:

  • len(detections) 返回检测数量,即 len(xyxy)
  • 直接 for 迭代时,每个元素是 (xyxy, mask, confidence, class_id, tracker_id, data) 六元组,mask 等缺失字段会以 None 占位(core.py)。

因此 Detections 可以像列表一样被循环消费,配合后面的索引操作使用非常顺手。

从任意检测框架构造 Detections:工厂方法全景

Detections 的构造入口有两类:一类是直接 sv.Detections(...) 手工传参(如把标注文件里的框读进来);另一类是成体系的 from_* 工厂方法,把各框架结果转换为统一格式。逐一列如下:

工厂方法 输入框架 关键行为
from_yolov5 Ultralytics YOLOv5(torch.hub) 提取 pred 中框/置信度/类别
from_ultralytics Ultralytics YOLOv8/v8+ 的 detect / segment / OBB 模型 自动识别 obbboxes 分支,并回填 class_name
from_yolo_nas Deci AI YOLO-NAS(super-gradients) 空结果返回 cls.empty()
from_tensorflow TensorFlow Hub 目标检测模型 需额外传 resolution_wh=(宽, 高),把归一化 [ymin,xmin,ymax,xmax] 反算为像素并重排为 xyxy
from_deepsparse Neural Magic DeepSparse 空结果返回 empty()
from_mmdetection OpenMMLab MMDetection / MMYOLO 可带 pred_instances.masks
from_detectron2 Facebook Detectron2 提取框/分数/类别,存在时带 pred_masks
from_transformers HuggingFace Transformers 兼容目标检测以及 panoptic/semantic/instance 分割结果;id2label 传入后回填类别名
from_inference Roboflow API / inference 检测+分割模型皆可;compact_masks 参数控制掩码形态
from_sam Segment Anything(SAM) 掩码按面积降序排列;兼容 dense 数组与 COCO RLE 两种 segmentation 编码
from_sam3 SAM 3(PVS / PCS 格式) resolution_wh;文本/点提示结果按 prompt 索引编码为 class_id
from_azure_analyze_image Azure AI Vision 4.0 class_map 为 None 时动态建类别映射
from_paddledet PaddleDetection 空结果返回 empty()
from_vlm 各类视觉语言大模型 见下文 VLM 专节
from_lmm 同上(已弃用 自 0.26.0 起弃用,计划 0.31.0 移除,请改用 from_vlm
from_easyocr EasyOCR 文本检测 OCR 文本写入 data["class_name"];四角点保留在 ORIENTED_BOX_COORDINATES
from_ncnn Tencent ncnn xywh 转换,空结果返回 empty()

实操示例:Ultralytics(最常用)

from supervision import _cv2 as cv2
import supervision as sv
from ultralytics import YOLO

image = cv2.imread("<SOURCE_IMAGE_PATH>")
model = YOLO("yolov8s.pt")
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)

from_ultralytics 的实现可以看出它实际做了三层判断:若结果带 obb 分支,则把 xyxyxyxy 旋转四角写入 data[ORIENTED_BOX_COORDINATES]、类别名写入 data[class_name];若结果只有掩码没有框(如某些分割输出),则退化为用 mask_to_xyxy 从掩码反推 xyxy;否则走常规 boxes 分支。若检测器什么都没检测到,返回空 Detectionsclass_name 为一个 (0,) 的空字符串数组。

实操示例:Transformers(DETR)

import torch
import supervision as sv
from PIL import Image
from transformers import DetrImageProcessor, DetrForObjectDetection

processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")

image = Image.open("<SOURCE_IMAGE_PATH>")
inputs = processor(images=image, return_tensors="pt")

with torch.no_grad():
    outputs = model(**inputs)

width, height = image.size
target_size = torch.tensor([[height, width]])
results = processor.post_process_object_detection(
    outputs=outputs, target_sizes=target_size)[0]
detections = sv.Detections.from_transformers(
    transformers_results=results,
    id2label=model.config.id2label)

from_transformers 内部会按结果结构自动分支:core.py 先判断是否为 segmentation 张量(语义/全景分割路径),再看是否含 masks/png_string(实例分割路径),最后才落到含 boxes 的检测路径;三者都不是则抛出明确 ValueError

实操示例:Roboflow Inference

from supervision import _cv2 as cv2
import supervision as sv
from inference import get_model

image = cv2.imread("<SOURCE_IMAGE_PATH>")
model = get_model(model_id="yolov8s-640")
result = model.infer(image)[0]
detections = sv.Detections.from_inference(result)

注意 from_inference 支持 compact_masks=True 参数,开启后掩码将包装为 CompactMask 而非稠密布尔数组。文档与源码给出了明确的取舍警告:只有当预测的掩码是"尺寸匹配整图的 COCO-RLE"时,该路径才会把掩码裁剪到检测框,可能丢失框外像素;多边形掩码与尺寸不匹配的 RLE 会以整幅图保留、不丢像素。因此 from_inference(r)from_inference(r, compact_masks=True) 的结果仅在原生整图 COCO-RLE 这一条路径上可能出现面积/IoU 差异,请仅在内存收益大于边界损失时开启。

空检测与集合合并:empty / is_empty / merge

很多工厂方法在"什么都没检出"时会返回空对象而非报错。空对象由 Detections.empty() 构造,其 xyxy 形状为 (0, 4)is_empty() 则直接判断 len(xyxy) == 0,例如对一张无目标的帧执行 detections[detections.class_id == 99]is_empty() 返回 True

Detections.merge([...]) 把多个检测对象拼接为一个:输入长度为 3 与 4 的两个对象会得到长度为 7 的结果,confidence/class_id/tracker_id/data 逐条堆叠。合并时有几条硬性约束(merge 文档):

  • 空对象会被忽略,全部为空则返回 empty()
  • maskconfidenceclass_idtracker_id 遵循"要么全为 None,要么全不为 None",否则抛 ValueError(源码通过 stack_or_none 检查);
  • 掩码合并策略:全为 CompactMask → 结果为 CompactMask;稠密数组 + CompactMask 混合 → 稠密掩码经 CompactMask.from_dense 转换后合并,且不为混合输入分配完整的 (N, H, W) 稠密栈(省内存,但 from_dense 会把掩码裁剪到各自 xyxy,框外像素有损,需要像素级保真时请使用全稠密路径);全为稠密数组 → 结果保持稠密 ndarray(向后兼容)。

合并示例:

import numpy as np
import supervision as sv

detections_1 = sv.Detections(
    xyxy=np.array([[15, 15, 100, 100], [200, 200, 300, 300]]),
    class_id=np.array([1, 2]),
    data={'feature_vector': np.array([0.1, 0.2])},
)
detections_2 = sv.Detections(
    xyxy=np.array([[30, 30, 120, 120]]),
    class_id=np.array([1]),
    data={'feature_vector': np.array([0.3])},
)
merged = sv.Detections.merge([detections_1, detections_2])
# xyxy → [[15,15,100,100],[200,200,300,300],[30,30,120,120]]
# data['feature_vector'] → [0.1, 0.2, 0.3]

索引、过滤与子集操作:select[]

Detections 支持整数、切片、整数列表与布尔数组四种索引方式,核心实现在 select__getitem__ 则进一步支持字符串 key 访问 data。典型用法(均取自 getitem 文档示例):

detections = sv.Detections(xyxy=...)

first_detection = detections[0]                       # 取第 1 条 → 新 Detections
first_10_detections = detections[0:10]                # 切片
some_detections = detections[[0, 2, 4]]               # 按索引列表取
class_0 = detections[detections.class_id == 0]        # 布尔掩码过滤(常用!)
high_conf = detections[detections.confidence > 0.5]   # 按置信度过滤

feature_vector = detections['feature_vector']         # 字符串 → data 里的值

select 的实现看,它永远返回一份新拷贝——xyxymaskconfidenceclass_idtracker_iddatametadata 均不与原对象共享内存,即使是空选择也会干净地复制空态。这意味着你可以放心地把过滤结果当作独立对象传递而不会意外改动原数据。

data 的写入也有类型约束:__setitem__ 只接受 np.ndarraylist,且值长度必须与检测数量一致,否则抛 TypeError/ValueError。常见用法是把类别 ID 映射成可读名字再存回去:

detections['names'] = [
    model.model.names[class_id]
    for class_id in detections.class_id
]

data 也可以承载任意浮点特征向量等非标准字段,为聚类、重识别等下游任务提供挂载点。

几何属性:面积、宽高比与锚点坐标

Detections 提供三个计算型 @property 与一个锚点方法,它们都返回与 xyxy 行数对齐的数组:

area——三级派发

area 按优先级选择几何来源(core.py):

  1. mask 存在 → 返回各掩码的真实像素面积(int64),旋转/不规则目标也不失真;
  2. 否则若 data[ORIENTED_BOX_COORDINATES] 存在 → 用鞋带公式(shoelace)计算旋转体的实际面积;
  3. 否则回落到 box_area(轴对齐面积)。

返回 dtype 依分支而定:掩码分支为 int64 像素计数,OBB 分支与整数坐标 AABB 为 float64,浮点 AABB 保持原 dtype。文档特别提示:携带 OBB 的检测必须把四角存到 config.ORIENTED_BOX_COORDINATES 对应的 "xyxyxyxy" key 且形状为 (N, 4, 2)area/with_nms/with_nmm 都以此为准。

box_areabox_aspect_ratio

box_area 只算轴对齐框面积(宽 × 高),不感知掩码;box_aspect_ratio 返回每个框的 宽/高,高度为 0 的位置输出 NaN。后者的典型用法是结合过滤筛掉细长误检:

ar = detections.box_aspect_ratio
# 筛掉比例异常的框
detections[(ar > 0.5) & (ar < 2.0)]

get_anchors_coordinates

计算每条检测的指定锚点(如底部中心、左上角等),返回 (n, 2) 坐标。anchor 参数来自 supervision.geometry.core 中的 Position 枚举(CENTERCENTER_OF_MASSTOP_LEFTBOTTOM_CENTERBOTTOM_RIGHT 等 9 个位置)。锚点选择顺序(源码注释):

  1. data 含 OBB 且锚点非 CENTER_OF_MASS → 基于旋转四角计算,锚点落在真实旋转体上;
  2. 锚点为 CENTER_OF_MASS → 返回掩码质心(没有掩码则抛 ValueError);
  3. 否则从轴对齐 xyxy 推导。

该方法是物体追踪时"取脚下点"(BOTTOM_CENTER)跨线计数、或标注标签锚定位置的标准工具,典型配合 line_zone 使用。

后处理:NMS / Soft-NMS / NMM

模型裸输出通常含大量冗余框,Detections 内置了完整的一套重叠后处理。三者共享一致的几何派发顺序:有掩码 → 用掩码 IoU;否则带 OBB → 用旋转框 IoU;否则用轴对齐框 IoUwith_soft_nms 的 OBB 检测目前回落为轴对齐 xyxy)。重叠度量可通过 overlap_metric 在 IoU/IoS 等指标间切换,底层实现在 src/supervision/detection/utils/iou_and_nms.py

with_nms——硬抑制

filtered = detections.with_nms(threshold=0.5, class_agnostic=False)
  • threshold:IoU 阈值,越低越严格,默认 0.5
  • class_agnosticTrue 时忽略类别跨类抑制,默认 False
  • 执行前提:confidence 必须存在;非 class-agnostic 时 class_id 也必须存在,否则抛 ValueError_build_nms_predictions 中的校验逻辑)。

with_soft_nms——高斯软抑制

与直接丢弃重叠框不同,Soft-NMS 对每个与高置信同类别框重叠的目标执行置信度衰减 score *= exp(-iou**2 / sigma)

  • sigma 控制衰减强度(须大于 0,越小衰减越强),默认 0.5
  • 默认 score_threshold=None一条都不会删,只把衰减后的分数写回拷贝;
  • 传入 score_threshold 后才把衰减到阈值以下的框真正滤除,得到与 with_nms 类似的真子集。

源码在 with_soft_nms 文档注释中特别提醒:任何 sigma 都无法复现 with_nms 的硬截断行为,Soft-NMS 本身不会自主删框。

with_nmm——非极大合并

NMM 把互相重叠的框合并成一个而非删掉,适合同一物理目标被切成多块的场景。对携带 OBB 的检测,合并组输出的旋转框是"在胜者(组内最高置信)旋转方向下包住全部角点的最紧矩形",轴对齐 xyxy 同步更新为该矩形的外接框;单元素组保持原框不变;掩码合并采用逻辑或。

紧凑掩码:to_compact_masks

对于大分辨率图像,(n, H, W) 的稠密布尔掩码极其耗内存。to_compact_masks() 返回一个掩码转换为 CompactMask(RLE 稀疏编码)的拷贝,相关设计与使用细节见 src/supervision/detection/compact_mask.pydocs/detection/compact_mask.md。几个易错点(源码注释):

  • 已带 CompactMask 或掩码为 None 时直接返回自身;
  • 转换时裁剪边界设为整幅图尺寸而非检测框,因此"按框裁剪的 O(box_area) 内存节省"在这里不存在——RLE 稀疏性仍比稠密数组省,但想进一步收紧需对结果调用 CompactMask.repack(代价是框外像素可能丢失)。

VLM 与多模态检测:from_vlm / from_lmm

当检测器从"经典目标检测模型"扩展到视觉语言大模型时,Detections 提供了文本/JSON 到标准结构的桥接。注意 from_lmm 已弃用(自 supervision 0.26.0 起,计划 0.31.0 移除),请改用 from_vlm,二者在源码中通过 LMM/VLM 镜像枚举值直接互转,行为等价。支持的模型一览(来自 from_vlm 文档):

模型 枚举(sv.VLM 任务 必需参数
PaliGemma / PaliGemma 2 PALIGEMMA 检测 resolution_wh
Qwen2.5-VL QWEN_2_5_VL 检测 resolution_wh, input_wh
Qwen3-VL QWEN_3_VL 检测 resolution_wh
Google Gemini 2.0 GOOGLE_GEMINI_2_0 检测 resolution_wh
Google Gemini 2.5 / 3.5 GOOGLE_GEMINI_2_5 / _3_5 检测、分割 resolution_wh
Moondream MOONDREAM 检测 resolution_wh
DeepSeek-VL2 DEEPSEEK_VL_2 检测 resolution_wh

各模型对原始输出的解析与坐标系还原(如 Gemini 输出归一化到 [0, 1000])由 src/supervision/detection/vlm.py 中的 from_paligemmafrom_qwen_2_5_vlfrom_google_gemini_* 等函数完成,from_vlm 只负责参数校验与结果分派。以 PaliGemma 为例:

import supervision as sv

paligemma_result = "<loc0256><loc0256><loc0768><loc0768> cat"
detections = sv.Detections.from_vlm(
    sv.VLM.PALIGEMMA,
    paligemma_result,
    resolution_wh=(1000, 1000),
    classes=['cat', 'dog'],
)
detections.xyxy    # array([[250., 250., 750., 750.]])
detections.class_id  # array([0])
detections.data['class_name']  # array(['cat'], ...)

其余模型会得到包含 bbox_2d/label 的 JSON 文本(Qwen、Gemini)或直接 dict(Moondream)。针对不同模型的提示词工程建议(如 Qwen 的 "Detect all objects…"、Gemini 的"图像在前、文本在后"的 parts 顺序、thinking_budget=0 等)都完整记录在 from_vlm/from_lmm 的 docstring 中,可作为实践参考。另外 docs/detection/utils/vlms.mddetections.vlm 目录下的示例(如 examples/heatmap_and_track)展示了与之配套的上游调用方式。

与下游模块衔接:tracker、annotator 与 tools

Detections 是整个 supervision 检测流水线的"流通货币":

  • 追踪ByteTrack().update_with_detections(detections) 返回带 tracker_id 的新对象,供跨帧关联;若工厂方法产出的结果本身带 tracker_id(如 Ultralytics 的跟踪模式),from_ultralytics 也会原样保留。详见 docs/trackers.mddocs/how_to/track_objects.md
  • 标注可视化BoxAnnotator/MaskAnnotator/LabelAnnotator 直接消费 Detectionsdocs/detection/annotators.md 给出了组合标注的完整示例。
  • 区域分析:把 Detections 交给 line_zonepolygon_zone 等工具做越线计数、区域内统计;examples/count_people_in_zone 提供了端到端参考实现。
  • 评价指标docs/detection/metrics.mdsrc/supervision/metricsMeanAveragePrecision 等类也接收 Detections

一致性校验与工程约定

作为收尾,梳理几条从源码确认的设计约定,便于你在项目里正确使用与二次开发:

  1. 构造时即校验:所有字段在 __post_init___validate_detections_fields 检查;合并、切片后也会再次校验,杜绝非法对象在管道中传播。
  2. 数组对齐是最高优先级from_inference 在只有部分预测带 tracker_id/掩码时,会整体丢弃这些字段以保证与 xyxy 严格对齐,而不是留下残缺列。
  3. 空结果不报错:各 from_*merge 都会优雅降级为空 Detections,业务代码应习惯用 is_empty() 做守卫。
  4. 拷贝语义select/过滤/with_nms/with_soft_nms 都返回新对象或显式拷贝,原始实例不被就地修改(__setitem__ 写入 data 除外)。
  5. data 是灵活挂载点,metadata 是集合说明:前者逐条对齐(feature_vectorclass_name 等),后者描述整批来源(视频名、时间戳、相机参数)。

源码级验证可继续阅读 src/supervision/detection/core.py、字段常量 src/supervision/config.py,配套测试位于 tests/detection/test_core.pytests/detection/utils/test_iou_and_nms.py,NMS 底层算法见 src/supervision/detection/utils/iou_and_nms.py。上手练习可对照 docs/how_to/filter_detections.mddocs/how_to/detect_and_annotate.md 与根目录 demo.ipynb 走一遍完整流程。

登录后查看全文
热门项目推荐
相关项目推荐