欢迎光临
我们一直在努力

Supervision :从 YOLO 检测结果可视化到视频跟踪、计数与数据处理

文章目录

    • 一、Supervision 是什么
    • 二、为什么要用 Supervision
    • 三、环境安装
      • 1. 基础安装
      • 2. 配合 YOLO 使用
      • 3. Conda 安装
      • 4. 检查版本
    • 四、核心概念:Detections
      • 1. Detections 常见字段
      • 2. xyxy 坐标格式
    • 五、Supervision 和 OpenCV 的区别
      • 1. OpenCV 画框
      • 2. Supervision 画框
      • 3. 怎么选择
    • 六、快速上手:YOLO 检测并画框
      • 1. 安装依赖
      • 2. 示例代码
      • 3. 核心代码解释
    • 七、手动构造 Detections
      • 1. 单个目标
      • 2. 多个目标
      • 3. 必须注意 shape
    • 八、常用 Annotator 标注器
      • 1. BoxAnnotator
      • 2. LabelAnnotator
      • 3. MaskAnnotator
      • 4. TraceAnnotator
    • 九、过滤检测结果
      • 1. 按置信度过滤
      • 2. 按类别过滤
      • 3. 多条件过滤
    • 十、NMS 去重
    • 十一、视频处理
      • 1. 获取视频信息
      • 2. 逐帧读取视频
      • 3. 保存视频
      • 4. 使用 process_video
    • 十二、目标跟踪
      • 1. YOLO + ByteTrack 视频跟踪
      • 2. 注意
    • 十三、线计数 LineZone
      • 1. 基本概念
      • 2. 线计数需要 tracker_id
      • 3. 示例代码
    • 十四、区域过滤 PolygonZone
      • 1. 定义多边形区域
      • 2. 过滤区域内目标
      • 3. 画出区域
      • 4. 完整示例
    • 十五、小目标检测:InferenceSlicer
      • 1. 基本思路
      • 2. 示例代码
    • 十六、和昇腾 310B / OM 推理结果结合
      • 1. 从 OM 后处理结果构造 Detections
      • 2. 画框
      • 3. 为什么 OpenCV 能画,Supervision 不显示

一、Supervision 是什么

Supervision 是 Roboflow 开源的 Python 计算机视觉工具库。

它不是一个训练框架,也不是一个模型本身,而是一个用于构建计算机视觉应用的辅助库。

它主要解决这些问题:

模型输出格式不统一
检测框可视化麻烦
视频逐帧处理重复代码多
目标跟踪、计数、区域判断要自己写很多逻辑
数据集格式转换和评估流程繁琐

Supervision 官方文档中提到,它提供了统一的 Detections 对象,可以兼容 Ultralytics、Roboflow Inference、Transformers、SAM、Detectron2、MMDetection、YOLO-NAS、PaddleDet、NCNN 等多种模型输出。

简单说:

Supervision = 检测结果管理 + 可视化 + 跟踪 + 计数 + 视频处理 + 数据集工具


二、为什么要用 Supervision

如果你只想画一个框,用 OpenCV 就够了:

cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)

但是实际项目里经常要做:

画检测框
画类别名和置信度
不同类别不同颜色
视频逐帧处理
目标跟踪 ID
画运动轨迹
统计越线数量
判断目标是否在区域内
保存处理后视频
过滤低置信度目标
对检测结果做 NMS

这些如果都用 OpenCV 手写,会越来越乱。

Supervision 的价值就是把这些常见流程封装好。

能力OpenCVSupervision
画框 可以,底层直接 可以,封装更方便
画标签 要自己写文字位置 内置 LabelAnnotator
多类别颜色 要自己维护颜色表 内置颜色策略
YOLO 结果转换 要手动解析 Detections.from_ultralytics()
视频处理 要自己写循环和保存 process_video()、VideoSink
目标跟踪 需要额外算法 可接 ByteTrack
区域计数 要自己写几何判断 PolygonZone
越线计数 要自己维护 ID 和方向 LineZone

三、环境安装

1. 基础安装

pip install supervision

Supervision 要求 Python 版本通常为:

Python >= 3.9

2. 配合 YOLO 使用

如果你要结合 Ultralytics YOLO:

pip install ultralytics supervision opencv-python

3. Conda 安装

conda install -c conda-forge supervision

4. 检查版本

import supervision as sv

print(sv.__version__)


四、核心概念:Detections

sv.Detections 是 Supervision 最核心的对象。

它的作用是:

把不同模型的检测结果,统一成同一种格式

1. Detections 常见字段

字段含义形状
xyxy 检测框坐标 (N, 4)
confidence 置信度 (N,)
class_id 类别 ID (N,)
tracker_id 跟踪 ID (N,)
mask 分割 mask (N, H, W)
data 额外数据 字典

其中最常用的是:

xyxy
confidence
class_id

2. xyxy 坐标格式

Supervision 使用的检测框格式是:

[x1, y1, x2, y2]

含义:

x1, y1 = 左上角坐标
x2, y2 = 右下角坐标

示例:

xyxy = [100, 50, 300, 220]

表示:

左上角:(100, 50)
右下角:(300, 220)

注意,它不是 YOLO 标签里的:

x_center, y_center, width, height

也不是 OpenCV 里有时用的:

x, y, w, h


五、Supervision 和 OpenCV 的区别

1. OpenCV 画框

OpenCV 更底层:

import cv2

cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(
image,
"person 0.90",
(x1, y1 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.6,
(0, 255, 0),
2
)

优点:

直接
灵活
依赖少
调试方便

缺点:

代码重复
标签布局要自己处理
多类别颜色要自己维护
跟踪、计数、区域判断都要自己写

2. Supervision 画框

Supervision 更高级:

box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()

image = box_annotator.annotate(scene=image, detections=detections)
image = label_annotator.annotate(scene=image, detections=detections, labels=labels)

优点:

结果对象统一
可视化方便
扩展到视频、跟踪、计数很自然
代码结构更清晰

缺点:

要求 Detections 格式正确
坐标、shape、dtype 错了可能不显示
版本 API 变化需要注意

3. 怎么选择

场景建议
只想快速画一个框 OpenCV
YOLO 结果可视化 Supervision
视频跟踪 Supervision
区域计数 Supervision
昇腾 OM 推理调试坐标 先 OpenCV,再 Supervision
正式工程封装 Supervision

六、快速上手:YOLO 检测并画框

1. 安装依赖

pip install ultralytics supervision opencv-python

2. 示例代码

import cv2
import supervision as sv
from ultralytics import YOLO

IMAGE_PATH = "bus.jpg"
MODEL_PATH = "yolov8s.pt"

model = YOLO(MODEL_PATH)
image = cv2.imread(IMAGE_PATH)

results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)

box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()

labels = [
f"{model.names[class_id]} {confidence:.2f}"
for class_id, confidence
in zip(detections.class_id, detections.confidence)
]

annotated_image = image.copy()
annotated_image = box_annotator.annotate(
scene=annotated_image,
detections=detections
)
annotated_image = label_annotator.annotate(
scene=annotated_image,
detections=detections,
labels=labels
)

cv2.imshow("result", annotated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

3. 核心代码解释

results = model(image)[0]

使用 YOLO 对图片进行推理。

detections = sv.Detections.from_ultralytics(results)

把 Ultralytics 的结果转换为 Supervision 的统一 Detections 对象。

box_annotator.annotate(...)

在图片上画检测框。

label_annotator.annotate(...)

在图片上画类别标签和置信度。


七、手动构造 Detections

有时你的模型不是 Ultralytics,比如你在昇腾 310B 上跑 .om,得到的是自己解析出来的框。

这时可以手动构造 Detections。

1. 单个目标

import numpy as np
import supervision as sv

detections = sv.Detections(
xyxy=np.array([
[100, 50, 300, 220]
], dtype=np.float32),
confidence=np.array([0.92], dtype=np.float32),
class_id=np.array([0], dtype=np.int32)
)

2. 多个目标

import numpy as np
import supervision as sv

xyxy = np.array([
[100, 50, 300, 220],
[400, 80, 520, 260]
], dtype=np.float32)

confidence = np.array([0.92, 0.81], dtype=np.float32)
class_id = np.array([0, 1], dtype=np.int32)

detections = sv.Detections(
xyxy=xyxy,
confidence=confidence,
class_id=class_id
)

3. 必须注意 shape

字段正确形状
xyxy (N, 4)
confidence (N,)
class_id (N,)

错误示例:

xyxy = np.array([100, 50, 300, 220])

这是 (4,),不是 (1, 4)。

正确写法:

xyxy = np.array([[100, 50, 300, 220]])


八、常用 Annotator 标注器

Annotator 的作用是把检测结果画到图片或视频帧上。

1. BoxAnnotator

画矩形检测框。

box_annotator = sv.BoxAnnotator(thickness=2)
image = box_annotator.annotate(scene=image, detections=detections)

常用参数:

参数含义
thickness 框线粗细
color 框颜色或颜色表
color_lookup 按类别、索引或跟踪 ID 映射颜色

2. LabelAnnotator

画类别文本。

label_annotator = sv.LabelAnnotator()
image = label_annotator.annotate(
scene=image,
detections=detections,
labels=labels
)

标签通常这样构造:

labels = [
f"{class_names[class_id]} {confidence:.2f}"
for class_id, confidence
in zip(detections.class_id, detections.confidence)
]

3. MaskAnnotator

用于实例分割结果。

mask_annotator = sv.MaskAnnotator()
image = mask_annotator.annotate(scene=image, detections=detections)

前提:

detections.mask 不为空

如果你是普通 YOLO 检测模型,只有框,没有 mask,就不要用它。

4. TraceAnnotator

用于画目标运动轨迹。

通常配合跟踪器使用:

trace_annotator = sv.TraceAnnotator()
image = trace_annotator.annotate(scene=image, detections=detections)

前提:

detections.tracker_id 不为空


九、过滤检测结果

Detections 支持用布尔条件过滤。

1. 按置信度过滤

detections = detections[detections.confidence > 0.5]

意思是只保留置信度大于 0.5 的目标。

2. 按类别过滤

假设 0 是 person:

detections = detections[detections.class_id == 0]

只保留人。

3. 多条件过滤

detections = detections[
(detections.confidence > 0.5) &
(detections.class_id == 0)
]

意思是:

只保留 class_id 为 0 且置信度大于 0.5 的目标


十、NMS 去重

NMS 全称:

Non-Maximum Suppression
非极大值抑制

作用是去掉重复框。

如果同一个目标被预测出多个重叠框,只保留最可信的框。

Supervision 中可以这样调用:

detections = detections.with_nms(threshold=0.5)

参数:

参数含义
threshold IoU 阈值,越小去重越严格
class_agnostic 是否忽略类别做 NMS

常见设置:

detections = detections.with_nms(threshold=0.45)

如果你导出 YOLO ONNX 时用了:

nms=False

那么后处理时就需要在代码里做置信度过滤和 NMS。


十一、视频处理

Supervision 提供了几个视频处理工具:

工具作用
VideoInfo 获取视频宽、高、FPS、总帧数
get_video_frames_generator 逐帧读取视频
VideoSink 保存视频
process_video 用 callback 快速处理视频
FPSMonitor 统计处理 FPS

1. 获取视频信息

import supervision as sv

video_info = sv.VideoInfo.from_video_path("input.mp4")

print(video_info.width)
print(video_info.height)
print(video_info.fps)
print(video_info.total_frames)
print(video_info.resolution_wh)

2. 逐帧读取视频

import supervision as sv

frames_generator = sv.get_video_frames_generator("input.mp4")

for frame in frames_generator:
# frame 是 OpenCV BGR 图像
pass

3. 保存视频

import supervision as sv

video_info = sv.VideoInfo.from_video_path("input.mp4")
frames_generator = sv.get_video_frames_generator("input.mp4")

with sv.VideoSink(target_path="output.mp4", video_info=video_info) as sink:
for frame in frames_generator:
sink.write_frame(frame)

注意:

VideoSink 写入的 frame 应该是 BGR 格式

4. 使用 process_video

import numpy as np
import supervision as sv

def callback(frame: np.ndarray, frame_index: int) > np.ndarray:
# 在这里处理每一帧
return frame

sv.process_video(
source_path="input.mp4",
target_path="output.mp4",
callback=callback
)

callback 的输入:

参数含义
frame 当前视频帧
frame_index 当前帧编号

返回值:

处理后的 frame


十二、目标跟踪

检测只能告诉你:

这一帧有哪些目标

跟踪可以告诉你:

这一帧的目标和上一帧是不是同一个目标

跟踪后每个目标会有一个:

tracker_id

例如:

person #1
person #2
car #3

1. YOLO + ByteTrack 视频跟踪

import numpy as np
import supervision as sv
from ultralytics import YOLO

model = YOLO("yolov8s.pt")
tracker = sv.ByteTrack()

box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
trace_annotator = sv.TraceAnnotator()

def callback(frame: np.ndarray, frame_index: int) > np.ndarray:
results = model(frame)[0]
detections = sv.Detections.from_ultralytics(results)

detections = detections[detections.confidence > 0.5]
detections = tracker.update_with_detections(detections)

labels = [
f"#{tracker_id} class:{class_id} {confidence:.2f}"
for tracker_id, class_id, confidence
in zip(detections.tracker_id, detections.class_id, detections.confidence)
]

annotated_frame = frame.copy()
annotated_frame = box_annotator.annotate(
scene=annotated_frame,
detections=detections
)
annotated_frame = label_annotator.annotate(
scene=annotated_frame,
detections=detections,
labels=labels
)
annotated_frame = trace_annotator.annotate(
scene=annotated_frame,
detections=detections
)

return annotated_frame

sv.process_video(
source_path="input.mp4",
target_path="tracked_output.mp4",
callback=callback
)

2. 注意

官方文档中提到,Supervision 内置的 sv.ByteTrack 包装后续更推荐使用外部 trackers 包的新实现。你学习和快速实验时可以先用 sv.ByteTrack(),正式项目要关注版本变更。


十三、线计数 LineZone

LineZone 用于统计目标穿过一条线的数量。

典型场景:

统计车辆进出
统计人流量
统计物体通过输送线

1. 基本概念

一条线由两个点构成:

start = sv.Point(x=100, y=400)
end = sv.Point(x=1000, y=400)

创建线计数器:

line_zone = sv.LineZone(start=start, end=end)

2. 线计数需要 tracker_id

LineZone 要判断“同一个目标从线的一边移动到另一边”,所以必须依赖跟踪 ID。

也就是说:

只检测不跟踪,不能可靠做越线计数

正确流程:

YOLO 检测
-> Detections
-> ByteTrack 分配 tracker_id
-> LineZone.trigger()
-> LineZoneAnnotator 画线和计数

3. 示例代码

import numpy as np
import supervision as sv
from ultralytics import YOLO

model = YOLO("yolov8s.pt")
tracker = sv.ByteTrack()

line_zone = sv.LineZone(
start=sv.Point(x=100, y=400),
end=sv.Point(x=1000, y=400)
)

box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
line_annotator = sv.LineZoneAnnotator()

def callback(frame: np.ndarray, frame_index: int) > np.ndarray:
results = model(frame)[0]
detections = sv.Detections.from_ultralytics(results)
detections = detections[detections.confidence > 0.5]
detections = tracker.update_with_detections(detections)

line_zone.trigger(detections=detections)

labels = [
f"#{tracker_id}"
for tracker_id in detections.tracker_id
]

annotated_frame = frame.copy()
annotated_frame = box_annotator.annotate(annotated_frame, detections)
annotated_frame = label_annotator.annotate(annotated_frame, detections, labels)
annotated_frame = line_annotator.annotate(annotated_frame, line_counter=line_zone)

return annotated_frame

sv.process_video(
source_path="input.mp4",
target_path="line_count_output.mp4",
callback=callback
)


十四、区域过滤 PolygonZone

PolygonZone 用于判断目标是否在一个多边形区域内。

典型场景:

只统计停车区内的车辆
只检测禁区内的人
只保留某个 ROI 区域中的目标

1. 定义多边形区域

import numpy as np
import supervision as sv

polygon = np.array([
[100, 100],
[600, 100],
[600, 500],
[100, 500]
])

zone = sv.PolygonZone(polygon=polygon)

2. 过滤区域内目标

mask = zone.trigger(detections=detections)
detections_in_zone = detections[mask]

3. 画出区域

zone_annotator = sv.PolygonZoneAnnotator(zone=zone)
frame = zone_annotator.annotate(scene=frame)

4. 完整示例

import cv2
import numpy as np
import supervision as sv
from ultralytics import YOLO

model = YOLO("yolov8s.pt")

image = cv2.imread("street.jpg")
results = model(image)[0]
detections = sv.Detections.from_ultralytics(results)

polygon = np.array([
[100, 100],
[600, 100],
[600, 500],
[100, 500]
])

zone = sv.PolygonZone(polygon=polygon)
mask = zone.trigger(detections=detections)
detections = detections[mask]

box_annotator = sv.BoxAnnotator()
zone_annotator = sv.PolygonZoneAnnotator(zone=zone)

annotated = image.copy()
annotated = zone_annotator.annotate(scene=annotated)
annotated = box_annotator.annotate(scene=annotated, detections=detections)

cv2.imshow("zone", annotated)
cv2.waitKey(0)
cv2.destroyAllWindows()


十五、小目标检测:InferenceSlicer

小目标检测经常遇到一个问题:

整张大图缩放到 640×640 后,小目标变得更小,模型很难识别

解决思路:

把大图切成多个小块
每个小块分别推理
再把结果合并回原图

Supervision 提供了 InferenceSlicer 来处理这个流程。

1. 基本思路

原图
-> 切片
-> 每个切片推理
-> 合并检测框
-> NMS/NMM 去重
-> 返回完整图检测结果

2. 示例代码

import cv2
import supervision as sv
from ultralytics import YOLO

model = YOLO("yolov8s.pt")

def callback(image_slice):
results = model(image_slice)[0]
return sv.Detections.from_ultralytics(results)

slicer = sv.InferenceSlicer(callback=callback)

image = cv2.imread("large_image.jpg")
detections = slicer(image)

box_annotator = sv.BoxAnnotator()
annotated = box_annotator.annotate(scene=image.copy(), detections=detections)

cv2.imshow("sliced result", annotated)
cv2.waitKey(0)
cv2.destroyAllWindows()

适合场景:

无人机小目标
高分辨率监控
遥感图像
密集小物体检测


十六、和昇腾 310B / OM 推理结果结合

如果你的模型已经转成 .om 在昇腾 310B 上推理,Supervision 不能直接帮你跑 OM。

Supervision 负责的是:

拿到后处理后的框
转换成 Detections
画框、跟踪、计数、可视化

你的完整流程应该是:

读取图像/视频帧
-> 前处理 letterbox / resize
-> OM 推理
-> 解析输出
-> 置信度过滤
-> NMS
-> 坐标还原到原图
-> 构造 sv.Detections
-> 使用 Supervision 画框/跟踪/计数

1. 从 OM 后处理结果构造 Detections

假设你的后处理已经得到:

boxes = [
[100, 50, 300, 220],
[400, 80, 520, 260]
]
scores = [0.92, 0.81]
class_ids = [0, 0]

转换成 Supervision:

import numpy as np
import supervision as sv

detections = sv.Detections(
xyxy=np.array(boxes, dtype=np.float32),
confidence=np.array(scores, dtype=np.float32),
class_id=np.array(class_ids, dtype=np.int32)
)

2. 画框

class_names = ["drone"]

labels = [
f"{class_names[class_id]} {confidence:.2f}"
for class_id, confidence in zip(detections.class_id, detections.confidence)
]

box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()

frame = box_annotator.annotate(scene=frame, detections=detections)
frame = label_annotator.annotate(scene=frame, detections=detections, labels=labels)

3. 为什么 OpenCV 能画,Supervision 不显示

常见原因:

问题说明
xyxy 形状错 必须是 (N, 4)
坐标格式错 Supervision 要 [x1, y1, x2, y2]
坐标没有映射回原图 框可能在错误位置
坐标超出图像范围 建议 np.clip
confidence 被过滤没了 检查阈值
class_id 和标签不匹配 class_id 不能越界
传入的是灰度图 建议用 BGR 三通道

建议加一段检查:

print("xyxy:", detections.xyxy)
print("confidence:", detections.confidence)
print("class_id:", detections.class_id)
print("len:", len(detections))
print("frame shape:", frame.shape)

赞(0)
未经允许不得转载:171主机测评 » Supervision :从 YOLO 检测结果可视化到视频跟踪、计数与数据处理
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址