Ultralytics YOLO27: 加入候补名单。
Get Started
课程中级

标注并训练姿态模型

编写带有可见性信息的关键点标签,正确设置 kpt_shape 和 flip_idx,在 COCO8-pose 上训练,并将关键点转换为带置信度门槛的关节角度。

姿态估计会将每个人转换为 17 个带编号的点,而这些点可以用于计算产品所需的数值:符合人体工学的肘部角度、康复训练中的膝部角度、用于跌倒检测的髋部高度。数据集中的两个键决定这些点是否可信;任意一个设置错误都不会报错,只会让模型混淆左右,或悄无声息地放弃成本最低的数据增强。

学习成果

在 coco8-pose.yaml 上训练 yolo26n-pose.pt,并根据 result.keypoints 计算每个人左右肘部的角度;对于模型置信度不足的关节,跳过计算。

快速通道
如果你已经熟悉相关内容,以下是简要版。
  1. 标签行:类别、框(4 个值),然后是每个关键点的 x y v,所有值均已归一化;v 为 0(未标注)、1(被遮挡)或 2(可见)。

  2. 设置 kpt_shape: [K, 3],并将 flip_idx 设置为会互换所有左右对应点的映射。

  3. yolo pose train data=coco8-pose.yaml model=yolo26n-pose.pt epochs=1 imgsz=320.

  4. 读取 result.keypoints.xy 和 .conf;若关节置信度低于设定阈值,则跳过相应计算。

实践操作

关键点标签行#

Ultralytics YOLO 对三名跑步者进行姿态估计,每个人都有一个人体框和一个 17 个关键点组成的骨架

姿态标签行是在检测行后追加关键点。每个坐标都归一化到 0–1,并且每一行的关键点顺序固定且一致。下面是一条真实的 COCO8-pose标签行,截取至前四个关键点:

0 0.671279 0.617945 0.645759 0.726859 0.519751 0.381250 2.000000 0.550936 0.348438 2.000000 0.488565 0.367188 2.000000 0.642412 0.354687 2.000000 ...
值含义
0类别索引(person)
0.671279 0.617945 0.645759 0.726859框中心 x、中心 y、宽度、高度
0.519751 0.381250 2关键点 0(鼻子):x、y、可见性
…另外 16 个关键点遵循相同格式:5 + 17 × 3 = 每行 56 个值

可见性标记遵循 COCO 约定:2 表示可见,1 表示已标注但被遮挡,0 表示未标注。Ultralytics 会在损失计算中屏蔽带有 v = 0 的关键点,因此缺失的手腕不会让模型学着在 (0, 0) 处预测手腕。

Ultralytics Platform 标注编辑器中的 COCO8-pose 图像,正在切蛋糕的人身上绘制了 17 个关键点组成的骨架

标注你能确定位置的遮挡关节

对于你能有把握确定位置的遮挡关节,标记为 1——例如桌子后面的髋部、背后的手腕;这些关节仍可用于训练。只有无人能够确定位置的关节才标记为 0。把所有隐藏关节都标记为 0,会恰好丢掉模型需要学习的困难样本。

姿态数据集指南还介绍了没有可见性信息的数据集所用的双值变体(仅 x y、kpt_shape: [K, 2])。

kpt_shape 和 flip_idx#

数据集 YAML 会在常用的 path、train、val 和 names 之外增加两个键:

kpt_shape: [17, 3] # 17 keypoints, each x, y, visibility
flip_idx: [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15]

站立人物的 COCO 17 个关键点顺序,编号从 0(鼻子)到 16(右脚踝)

flip_idx 会告知水平翻转增强,每个关键点在镜像图像中对应哪个关键点。镜像翻转人物后,其左手腕(9)会出现在原来右手腕(10)的位置,因此 flip_idx 的第 9 个位置存放 10,第 10 个位置存放 9;鼻子(0)映射到自身。每种增强都会让骨架随像素移动,而翻转是唯一还需要索引映射的增强:

一名跑步者的姿势增强示例:原图、水平翻转、旋转、缩放、HSV 颜色变换和马赛克,每种变换后都显示关键点骨架

如果 flip_idx 有误会怎样?我们已在安装的源代码中核实:

  • 缺失:训练会记录警告,并将 fliplr 和 flipud 设为 0,因此你会失去成本最低的数据增强。
  • 长度错误:训练会停止,并显示一个指出不匹配情况的 ValueError——这是唯一会明确报错的情况。
  • 长度正确,但配对错误:不会有任何警告。一半翻转样本会教模型把左边当成右边,验证图像则会在对称姿势上出现左右互换。

在 COCO8-pose 上训练#

COCO8-pose 训练马赛克:人们分别在摩托车上、粉色雨伞下、滑板公园里和马背上,每个人都有一个人体框和一副彩色关键点骨架

from ultralytics import YOLO

model = YOLO("yolo26n-pose.pt")
model.train(data="coco8-pose.yaml", epochs=1, imgsz=320, device="cpu")
metrics = model.val()
print(f"box  mAP50-95: {metrics.box.map:.3f}")
print(f"pose mAP50-95: {metrics.pose.map:.3f}")
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95)     Pose(P          R      mAP50  mAP50-95)
                   all          4         14          1      0.783      0.893      0.643      0.966      0.571      0.575      0.326
box  mAP50-95: 0.643
pose mAP50-95: 0.326

与分割任务一样,表格分为两个部分。Box 对人体框进行评分;Pose 使用相同的 mAP 机制,通过对象关键点相似度(OKS)对关键点位置进行评分。对于姿势产品,Pose mAP50-95 是发布门槛——下一课会介绍 OKS 如何判断关键点是否正确。

从关键点到关节角度#

result.keypoints 包含 xy(N × K × 2,像素坐标)、xyn(归一化坐标)和 conf(N × K),顺序与上方所示的 COCO 顺序一致。关节角度是两条在该处相交的肢体向量之间的夹角——可用点积计算:

import numpy as np

from ultralytics import YOLO

model = YOLO("yolo26n-pose.pt")
result = model("https://ultralytics.com/images/bus.jpg")[0]
kpts = result.keypoints.cpu().numpy()  # people x 17 x (x, y, conf)

SHOULDER, ELBOW, WRIST = 5, 7, 9  # left arm, COCO keypoint order
for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
    if conf[[SHOULDER, ELBOW, WRIST]].min() < 0.5:
        print(f"person {person}: left arm not visible enough")
        continue
    a, b, c = xy[SHOULDER], xy[ELBOW], xy[WRIST]
    cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
    print(f"person {person}: left elbow angle {np.degrees(np.arccos(cos)):.0f} deg")
person 0: left elbow angle 71 deg
person 1: left elbow angle 96 deg
person 2: left elbow angle 59 deg
person 3: left arm not visible enough
person 4: left arm not visible enough

逐行来看:kpts.xy[p] 是第 p 个人的 17 个像素坐标点;a - b 和 c - b 分别是以肘部为起点的上臂和前臂向量;它们的归一化点积就是两者夹角的余弦值。手臂伸直时读数接近 180°,完全弯曲时接近 30°。第 3 和第 4 个人至少有一个左臂关节的置信度低于 0.5,因此脚本不会测量他们。

根据置信度筛选每个关节

症状:对于手臂超出画面的人员,模型仍给出看似可信的角度。原因:无论关键点是否被遮挡,模型都会为每个关键点返回坐标。解决方法:跳过置信度低于阈值(此处为 0.5)的关节所涉及的任何计算,并记录跳过频率——跳过率上升是摄像头或光照发生变化的早期迹象。

试试看

在 coco8-pose.yaml 上训练一个 epoch,并记录框和姿势的 mAP50-95。然后扩展 pose.py,让它也报告右肘角度(关键点 6、8、10),并列出公交车图像中双臂都可测量的人。

完成条件
满足以下所有条件,即表示你已完成本课程。
  • 训练已完成,你也记录了框和姿势的 mAP50-95。

  • 你的脚本会打印每个人的左右肘角度,并跳过任何置信度低于下限的手臂。

  • 你可以解释 flip_idx 缺失、长度错误或配对错误时会发生什么。

显示解答
import numpy as np

from ultralytics import YOLO

ARMS = {"left": (5, 7, 9), "right": (6, 8, 10)}  # shoulder, elbow, wrist
kpts = YOLO("yolo26n-pose.pt")("https://ultralytics.com/images/bus.jpg")[0].keypoints.cpu().numpy()

for person, (xy, conf) in enumerate(zip(kpts.xy, kpts.conf)):
    for side, (s, e, w) in ARMS.items():
        if conf[[s, e, w]].min() < 0.5:
            continue
        a, b, c = xy[s], xy[e], xy[w]
        cos = np.dot(a - b, c - b) / (np.linalg.norm(a - b) * np.linalg.norm(c - b))
        print(f"person {person}: {side} elbow {np.degrees(np.arccos(cos)):.0f} deg")
person 0: left elbow 71 deg
person 0: right elbow 94 deg
person 1: left elbow 96 deg
person 2: left elbow 59 deg
person 2: right elbow 135 deg

第 0 和第 2 个人的双臂都可测量。第 1 个人的右臂,以及第 3 和第 4 个人的双臂,置信度都低于 0.5 的下限,因此脚本会跳过这些手臂,而不是猜测。

缺少 flip_idx 时,翻转增强会被禁用并显示警告;长度错误时,训练会因 ValueError 而停止;看似有效但配对错误的 flip_idx 会导致模型在镜像标签上静默训练,并在验证时表现为左右互换。

接下来做什么

接下来,我们会为非人体对象设计关键点,并了解 OKS 如何判断关键点是否正确。

登录以保存学习进度