Supervision `Detections` 完全指南:统一目标检测与分割结果的核心数据结构
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 的作用就是把这些输出统一成一份一致的格式,从而让下游的 trackers、annotators(画框、画掩码、画标签)与 tools(如 line_zone、polygon_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 的存在与否是判定"这份检测携带旋转框几何"的规范信号,area、with_nms、with_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 模型 | 自动识别 obb 或 boxes 分支,并回填 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 分支。若检测器什么都没检测到,返回空 Detections 且 class_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(); mask、confidence、class_id、tracker_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 的实现看,它永远返回一份新拷贝——xyxy、mask、confidence、class_id、tracker_id、data、metadata 均不与原对象共享内存,即使是空选择也会干净地复制空态。这意味着你可以放心地把过滤结果当作独立对象传递而不会意外改动原数据。
data 的写入也有类型约束:__setitem__ 只接受 np.ndarray 或 list,且值长度必须与检测数量一致,否则抛 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):
- 若
mask存在 → 返回各掩码的真实像素面积(int64),旋转/不规则目标也不失真; - 否则若
data[ORIENTED_BOX_COORDINATES]存在 → 用鞋带公式(shoelace)计算旋转体的实际面积; - 否则回落到
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_area 与 box_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 枚举(CENTER、CENTER_OF_MASS、TOP_LEFT、BOTTOM_CENTER、BOTTOM_RIGHT 等 9 个位置)。锚点选择顺序(源码注释):
data含 OBB 且锚点非CENTER_OF_MASS→ 基于旋转四角计算,锚点落在真实旋转体上;- 锚点为
CENTER_OF_MASS→ 返回掩码质心(没有掩码则抛ValueError); - 否则从轴对齐
xyxy推导。
该方法是物体追踪时"取脚下点"(BOTTOM_CENTER)跨线计数、或标注标签锚定位置的标准工具,典型配合 line_zone 使用。
后处理:NMS / Soft-NMS / NMM
模型裸输出通常含大量冗余框,Detections 内置了完整的一套重叠后处理。三者共享一致的几何派发顺序:有掩码 → 用掩码 IoU;否则带 OBB → 用旋转框 IoU;否则用轴对齐框 IoU(with_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_agnostic:True时忽略类别跨类抑制,默认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.py 及 docs/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_paligemma、from_qwen_2_5_vl、from_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.md 与 detections.vlm 目录下的示例(如 examples/heatmap_and_track)展示了与之配套的上游调用方式。
与下游模块衔接:tracker、annotator 与 tools
Detections 是整个 supervision 检测流水线的"流通货币":
- 追踪:
ByteTrack().update_with_detections(detections)返回带tracker_id的新对象,供跨帧关联;若工厂方法产出的结果本身带tracker_id(如 Ultralytics 的跟踪模式),from_ultralytics也会原样保留。详见 docs/trackers.md 与 docs/how_to/track_objects.md。 - 标注可视化:
BoxAnnotator/MaskAnnotator/LabelAnnotator直接消费Detections,docs/detection/annotators.md给出了组合标注的完整示例。 - 区域分析:把
Detections交给 line_zone、polygon_zone 等工具做越线计数、区域内统计;examples/count_people_in_zone提供了端到端参考实现。 - 评价指标:
docs/detection/metrics.md与 src/supervision/metrics 中MeanAveragePrecision等类也接收Detections。
一致性校验与工程约定
作为收尾,梳理几条从源码确认的设计约定,便于你在项目里正确使用与二次开发:
- 构造时即校验:所有字段在
__post_init__经_validate_detections_fields检查;合并、切片后也会再次校验,杜绝非法对象在管道中传播。 - 数组对齐是最高优先级:
from_inference在只有部分预测带 tracker_id/掩码时,会整体丢弃这些字段以保证与xyxy严格对齐,而不是留下残缺列。 - 空结果不报错:各
from_*与merge都会优雅降级为空Detections,业务代码应习惯用is_empty()做守卫。 - 拷贝语义:
select/过滤/with_nms/with_soft_nms都返回新对象或显式拷贝,原始实例不被就地修改(__setitem__写入data除外)。 data是灵活挂载点,metadata是集合说明:前者逐条对齐(feature_vector、class_name等),后者描述整批来源(视频名、时间戳、相机参数)。
源码级验证可继续阅读 src/supervision/detection/core.py、字段常量 src/supervision/config.py,配套测试位于 tests/detection/test_core.py 与 tests/detection/utils/test_iou_and_nms.py,NMS 底层算法见 src/supervision/detection/utils/iou_and_nms.py。上手练习可对照 docs/how_to/filter_detections.md、docs/how_to/detect_and_annotate.md 与根目录 demo.ipynb 走一遍完整流程。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00