Supervision 关键点标注器全解析:Vertex、Edge 与 Ellipse 系列 Annotators 使用指南
【免费下载链接】supervisionWe write your reusable computer vision tools. 💜项目地址: https://gitcode.com/GitHub_Trending/su/supervision
导读
本篇文章基于 Supervision 官方文档 keypoint/annotators.md 编写,围绕人体姿态、手部骨骼与面部关键点可视化这一主题,系统讲解六个骨骼关键点标注器:VertexAnnotator、EdgeAnnotator、VertexLabelAnnotator、VertexEllipseAreaAnnotator、VertexEllipseOutlineAnnotator与VertexEllipseHaloAnnotator。读完本篇,你将掌握如何用统一的sv.KeyPoints数据结构驱动不同风格的关键点渲染,学会用源码级参数(颜色、半径、线宽、sigma 椭圆层级、透明度、防重叠标签等)快速产出可直接用于质检与展示的标注画面。
前置准备:一个可以被标注的KeyPoints
在 Supervision 中,关键点标注器的输入统一是sv.KeyPoints对象。它把来自不同框架的姿态/关键点结果标准化为一致的字段:
xy:形状(n, m, 2)的坐标数组,n为检测到的目标数,m为每个目标的特征点数;class_id:形状(n,)的目标类别 ID,可为None;keypoint_confidence:形状(n, m)的逐点置信度;visible:形状(n, m)的可选布尔矩阵,标记哪些关键点是可见的,None时全部视为可见(core.py 的字段定义见该文件 docstring);data:附加数据的字典,与关键点对齐。
你可以通过不同模型的适配器生成KeyPoints:如KeyPoints.from_ultralytics()(接收 YOLO pose 结果)、KeyPoints.from_mediapipe()、KeyPoints.from_inference()、KeyPoints.from_transformers()、KeyPoints.from_detectron2()与KeyPoints.from_yolo_nas(),详见 keypoint/core.md 与 core.py。
生成之后,即可像官方文档示例那样,把坐标交给任意标注器:
import supervision as sv image = ... # 读取一帧画面,numpy.ndarray 或 PIL.Image 均可 key_points = sv.KeyPoints(...) # 来自任意模型适配器接下来按能力分三组介绍六个标注器。
六个标注器速览
| 标注器 | 作用 | 核心参数(构造函数默认值见源码 annotators.py) |
|---|---|---|
VertexAnnotator | 在每个关键点画实心圆 | color=Color.ROBOFLOW、radius=4 |
EdgeAnnotator | 在关键点之间连线形成骨架 | color=Color.ROBOFLOW、thickness=2、edges=None |
VertexLabelAnnotator | 为每个关键点绘制带背景的文字标签 | color=Color.ROBOFLOW、text_color=Color.WHITE、text_scale=0.5、text_thickness=1、text_padding=10、border_radius=0、smart_position=False |
VertexEllipseAreaAnnotator | 基于协方差绘制多层级半透明填充椭圆 | sigma=(1.0, 2.0, 3.0)、color=(GREEN, YELLOW, RED)、opacity=0.4、max_axis=None |
VertexEllipseOutlineAnnotator | 基于协方差绘制多层级空心椭圆环 | 同上,另有thickness=2 |
VertexEllipseHaloAnnotator | 基于协方差绘制径向渐变发光椭圆 | 同 Area,opacity=0.6,内部_DECAY=2.0 |
需要留意的是:官方文档示例中的数值(如radius=10、thickness=5)属于演示取值,构造函数在源码中的默认值如上表所示,实际使用时两套值都合法。
VertexAnnotator:标注关键点顶点
VertexAnnotator负责把每个关键点画成实心圆(即骨骼的“顶点/关节点”)。
import supervision as sv image = ... key_points = sv.KeyPoints(...) vertex_annotator = sv.VertexAnnotator( color=sv.Color.GREEN, radius=10, ) annotated_frame = vertex_annotator.annotate( scene=image.copy(), key_points=key_points, )源码层面的执行规则(annotators.py 中VertexAnnotator.annotate):
- 遍历
key_points.xy,对每个目标内的每个点调用cv2.circle画实心圆,thickness=-1表示填充; - 坐标为
(0, 0)的占位点会被跳过; - 当
key_points.visible存在且对应位置为False时,该点被跳过(visible is None时不做过滤,全部绘制); key_points为空时直接返回原始scene,不会报错。
因此visible与“坐标零值”共同决定了哪些顶点会被画出,这对存在遮挡、模型输出置信度低的关键点尤为重要。
EdgeAnnotator:连接关键点绘制骨架边
EdgeAnnotator把关键点两两连线,得到完整的骨架结构:
import supervision as sv image = ... key_points = sv.KeyPoints(...) edge_annotator = sv.EdgeAnnotator( color=sv.Color.GREEN, thickness=5, ) annotated_frame = edge_annotator.annotate( scene=image.copy(), key_points=key_points, )edges参数有三种传法(源码中EdgeAnnotator.__init__与annotate的处理逻辑):
edges=None(默认)自动检测骨架:根据每个目标的关键点数量,在skeletons.py定义的SKELETONS_BY_VERTEX_COUNT字典中查找预设骨架。该字典由内建Skeleton枚举(COCO、HAND、GHUM、FACEMESH_TESSELATION_NO_IRIS等多个标准骨架)按去重后的顶点数自动索引构建(skeletons.py)。若按顶点数查不到匹配,会打印警告并跳过该目标。- 传
Sequence[tuple[int, int]](单骨架):对所有目标实例应用同一组边,例如edges=[(1, 2), (1, 3)]表示把 1 号点分别连向 2、3 号点。 - 传
dict[int, Sequence[tuple[int, int]]](按类别多骨架):以class_id为键、各自映射一组边,适合“同一批检测里混有多种骨架类型”的数据集,例如edges={0: [(1, 2), (1, 3)], 1: [(1, 2)]}。
关于边索引的 1-based 约定:公共 API 中的边索引采用1-based编号(从 1 到m),内部会通过_validate_edge_indices校验范围并转换为 0-based 下标后再访问xy数组(见源码annotators.py)。若索引越界(超出[1, m])会抛出ValueError。
绘制时若边两端任一坐标为零值,或任一端点被visible标记为不可见,则该边被跳过(源码annotate逻辑)。完整的多骨架用例可见源码 docstring 中的示例代码。
VertexLabelAnnotator:给关键点挂上文字标签
当需要直接读出“哪个点是什么关节”时,使用VertexLabelAnnotator在每个关键点旁绘制带圆角背景的文字框:
import supervision as sv image = ... key_points = sv.KeyPoints(...) vertex_label_annotator = sv.VertexLabelAnnotator( color=sv.Color.GREEN, text_color=sv.Color.BLACK, border_radius=5, ) annotated_frame = vertex_label_annotator.annotate( scene=image.copy(), key_points=key_points, )annotate()的labels参数同样支持三种形态(源码annotate/_resolve_labels):
None:默认以关键点的下标"0"、"1"、...作为标签;list[str]:长度必须等于关键点数量,作用于每个实例,如labels=["head", "L-foot", "R-foot"];dict[int, list[str]]:按class_id给不同骨架类型配置各自的标签列表。
颜色参数color与text_color既可以是单个Color,也可以是按关键点逐个配置的Color列表(长度需与点数一致,否则抛ValueError)。
细节特性(源码层面):
border_radius:背景圆角半径,设成较大值可把背景渲染成圆形;smart_position=True:利用pad_boxes先扩张每个标签框,再交给spread_out_boxes把重叠标签“摊开”,最后再内缩回原尺寸,从而避免标签互相遮挡(相关工具函数来自 detection/utils/boxes.py);- 标签框居中于关键点:先用
cv2.getTextSize测量文字尺寸得到文本包围盒,再以其中心对齐关键点坐标,背景矩形通过draw_rounded_rectangle绘制; - 与顶点标注一致:
visible为False的点、或visible为None时坐标为(0,0)的点会被跳过。
椭圆家族:把不确定度可视化出来
VertexEllipseAreaAnnotator、VertexEllipseOutlineAnnotator与VertexEllipseHaloAnnotator三个标注器用于不确定性/概率分布的可视化:它们不依赖模型输出的固定坐标,而是依据每个关键点的 2×2 协方差矩阵,画出与置信区间对应的椭圆。
数据要求:key_points.data["covariance"]
三个椭圆标注器共用一个私有基类_BaseVertexEllipseAnnotator,并依赖key_points.data中键为"covariance"的数据,其形状必须为(N, K, 2, 2)(N目标数、K每目标关键点数),矩阵以像素坐标表示。缺少该字段或形状不匹配时,_get_covariances会抛出ValueError。官方文档与本源码 docstring 均给出构造示例:
key_points = sv.KeyPoints( xy=..., class_id=..., data={ "covariance": np.array( [[[[800, 0], [0, 400]], # 第 1 个关键点的 2x2 协方差 [[400, 0], [0, 800]], # 第 2 个关键点 [[600, 0], [0, 600]]]], # 第 3 个关键点 dtype=np.float32, ) }, )sigma、color 与 max_axis 的语义
三个椭圆标注器共享sigma、color、max_axis三个参数(_BaseVertexEllipseAnnotator.__init__):
sigma:每个椭圆环的倍率,可传单个浮点数或浮点序列,默认(1.0, 2.0, 3.0)。内部会把 sigma 按降序排列,因此绘制顺序是“最外圈先画、最内圈最后画”。color:每个 sigma 层级对应的颜色,可传单个Color或颜色序列,默认(Color.GREEN, Color.YELLOW, Color.RED);颜色数量必须与 sigma 数量一致,否则抛ValueError。max_axis:椭圆半轴长度的像素上限(可选)。约束在max_axis非空时必须为正。
绘制几何的底层逻辑(源码_iter_ellipse_params):
- 对每个可见、非零坐标的关键点取出其 2×2 协方差矩阵;
- 用
np.linalg.eigh做特征分解,得到特征值与特征向量,按特征值降序排列(第一主轴方向对应最大方差方向,椭圆倾角即由该特征向量经arctan2换算为角度); - 每个 sigma 层级的椭圆半轴长度为
sigma * sqrt(特征值),中心落在关键点坐标上; - 协方差含非有限值或特征值非正的矩阵会被安全跳过,不做绘制。
Area / Outline / Halo 三者的差异
VertexEllipseAreaAnnotator —— 半透明实心填充
area_annotator = sv.VertexEllipseAreaAnnotator( color=sv.Color.GREEN, sigma=2.0, )将所有椭圆先在 overlay 副本上用cv2.ellipse(thickness=-1、抗锯齿LINE_AA)填充,再通过cv2.addWeighted(overlay, opacity, scene, 1 - opacity, 0)融合回原图,opacity默认 0.4。多层同心圆叠加后呈现“靶心”效果:内圈颜色代表更高的概率密度。
VertexEllipseOutlineAnnotator —— 空心描边环
outline_annotator = sv.VertexEllipseOutlineAnnotator( color=sv.Color.GREEN, sigma=2.0, thickness=2, )只画椭圆轮廓(cv2.ellipse使用给定的thickness),不做任何透明融合,适合需要清晰边界、不遮挡底层图像内容的场景。
VertexEllipseHaloAnnotator —— 径向渐变的辉光
halo_annotator = sv.VertexEllipseHaloAnnotator( color=sv.Color.GREEN, sigma=2.0, )不依赖 cv2.ellipse,而是对每个椭圆外接矩形 ROI 内的像素逐点计算归一化距离dist_sq,内部像素的透明度按(1 - dist_sq) ** decay衰减(_DECAY = 2.0的幂曲线),峰值透明度取opacity(默认 0.6),从而得到“中心最亮、边界渐隐”的柔光效果。
兼容别名:VertexEllipseAnnotator
官方文档特别注明:sv.VertexEllipseAnnotator是sv.VertexEllipseAreaAnnotator的兼容别名。这一点在源码中由模块级赋值VertexEllipseAnnotator = VertexEllipseAreaAnnotator直接落实(annotators.py),并且顶层包supervision同时导出了这两个名字(见init.py)。迁移/升级时代码若使用旧名仍能正常工作。
源码级共通细节与注意事项
场景类型自适应:所有标注器的
annotate(scene, key_points)都通过@ensure_cv2_image_for_class_method装饰器包装(来自 utils/conversion.py),因此scene同时接受numpy.ndarray与PIL.Image.Image,返回值与输入类型一致;若传入其他类型则抛出TypeError。文档示例统一用image.copy()传入,避免在原始帧上原地修改。统一基类与返回约定:六个标注器(除
VertexLabelAnnotator单独定义外)都继承自抽象基类BaseKeyPointAnnotator,接口完全一致,便于在视频处理管线里互相替换;空KeyPoints一律原样返回scene,不会中断逐帧处理流程。可见性过滤语义:顶点、边、标签三类标注都尊重
visible掩码;椭圆族还会额外跳过协方差矩阵异常的点。若想把低置信度关键点“不删除、只不画”,可参考KeyPoints.visible的文档建议:key_points.visible = key_points.keypoint_confidence > 0.3(core.py)。模块路径与兼容性:核心实现位于
supervision.key_points.annotators与supervision.key_points.core(单复数形式为key_points)。仓库中旧命名空间的 keypoint/init.py 会在导入时发出弃用警告,提示从0.27.0起改用supervision.key_points,请在新代码中统一使用后者。对应测试覆盖见 tests/key_points。
结语
从画点(VertexAnnotator)、连线(EdgeAnnotator)、打标签(VertexLabelAnnotator),到把预测不确定度可视化为多层椭圆(VertexEllipseArea/Outline/HaloAnnotator),Supervision 的关键点标注器家族覆盖了姿态、手部与面部等主流应用的全部基础渲染需求。实际使用时只需遵循三条规则:先用统一适配器得到sv.KeyPoints,再按需在data中提供协方差数据(椭圆族),最后选择标注器并合理设置visible掩码即可获得干净、专业且利于排查的可视化结果。
【免费下载链接】supervisionWe write your reusable computer vision tools. 💜项目地址: https://gitcode.com/GitHub_Trending/su/supervision
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考