Ultralytics YOLO27: 加入候补名单。
Get Started
计算机视觉基础·选择任务·课程 5/10
课程初级

姿态估计与 OBB

读取 YOLO26 的关键点和旋转框,在其基础上编写规则,并识别姿态或角度何时才是真正的信号。

养老院需要知道是否有人跌倒;港口需要在无人机图像中统计并排密集停靠的船只。普通边界框对这两种情况都无能为力:边界框无法说明一个人是否站立,而给斜放船只套上正立的边界框,框内大部分都是水。姿态估计 和 OBB 正是为这两类问题而设计的。

学习成果

根据 YOLO26 关键点编写姿态规则,并测量旋转框在航拍图像上能节省多少空间。

快速通道
如果你已经熟悉相关内容,以下是简要版。
  1. 姿态:每个人一个边界框和 17 个关键点(COCO 顺序:0 鼻子、5–6 肩膀、11–12 髋部、15–16 脚踝)。

  2. 使用关键点前先按置信度筛选;被遮挡的关节仍会有坐标。

  3. OBB:中心、尺寸和角度(xywhr,角度单位为弧度)。

  4. 姿态是姿势本身传递信号时的选择;俯视图中物体倾斜时选择 OBB;其他情况则用检测。

实践操作

姿态:姿势本身传递信号时#

海滨步道上的三名跑者,每个人都有一个人物边界框和一副由 Ultralytics YOLO 姿态估计生成的 17 个关键点骨架

姿态模型 会为每个人返回一个边界框和一组固定的关键点。在 COCO-Pose 上训练的模型会为每个人预测 17 个关键点——鼻子、眼睛、耳朵、肩膀、肘部、手腕、髋部、膝盖和脚踝——顺序始终相同,因此索引 0 是鼻子,11 是左髋,15 是左脚踝。每个关键点还带有一个置信度。

最简单实用的规则是:站立的身体高大于宽;躺在地板上的身体宽大于高。下面是在公交车图像上的示例,只使用模型有把握的关键点:

from ultralytics import YOLO

model = YOLO("yolo26n-pose.pt")
result = model("https://ultralytics.com/images/bus.jpg", verbose=False)[0]

for xy, conf in zip(result.keypoints.xy, result.keypoints.conf):
    visible = xy[conf > 0.5]  # drop keypoints the model couldn't see
    width = float(visible[:, 0].max() - visible[:, 0].min())
    height = float(visible[:, 1].max() - visible[:, 1].min())
    posture = "unknown" if len(visible) < 5 else "lying" if width > height else "upright"
    print(f"{len(visible):2d}/17 keypoints visible, body {width:3.0f} px wide x {height:3.0f} px tall -> {posture}")
16/17 keypoints visible, body 117 px wide x 427 px tall -> upright
 6/17 keypoints visible, body  80 px wide x 359 px tall -> upright
16/17 keypoints visible, body  91 px wide x 384 px tall -> upright
 1/17 keypoints visible, body   0 px wide x   0 px tall -> unknown
 2/17 keypoints visible, body   3 px wide x   6 px tall -> unknown

有三个人明显站立。最后两行分别是左边缘被截断的人,以及一个低置信度的重复检测:只有一两个可见关键点时,无法判断,所以规则会给出“未知”,而不是猜测。生产环境中的跌倒检测器还会通过跟踪加入时间条件——快速倒下后躺倒超过几秒。

隐藏关节也有坐标

症状:骨架的手腕悬在半空,姿态规则也会对只露出一部分的人触发。原因:即使模型看不到,keypoints.xy 也会返回全部 17 个关键点的位置。解决方法:像上面那样使用 keypoints.conf 进行筛选,并要求可见关键点达到最少数量后再做判断。

当答案取决于身体在做什么时,姿态估计才真正有用:用于安全监测的跌倒或弯腰搬举、运动中的关节角度和重复次数(如 运动监测),或牲畜姿态,以及采用自定义关键点布局的部件固定标记。要回答“这里有人吗?”,就用普通检测。

OBB:角度很重要时#

航拍图像中三艘以不同角度停泊的游艇,每艘都由 Ultralytics YOLO OBB 紧密旋转框住

普通边界框的边缘与图像边缘平行。有向边界框增加了旋转角度,存储为中心、宽度、高度和角度(xywhr)。在俯视图像中——卫星、无人机、俯拍传送带相机——物体可能朝向任意角度;给斜放物体套上正立边界框,框内大多是背景,上面的游艇就是如此。用一张码头的无人机照片来测量差异:

from ultralytics import YOLO

model = YOLO("yolo26n-obb.pt")
result = model("https://ultralytics.com/images/boats.jpg", verbose=False)[0]

obb = result.obb
rotated_area = obb.xywhr[:, 2] * obb.xywhr[:, 3]  # width x height of each rotated box
x1, y1, x2, y2 = obb.xyxy.T  # the axis-aligned box that would enclose the same object
upright_area = (x2 - x1) * (y2 - y1)

print(f"{len(obb)} objects, classes: {sorted({result.names[int(c)] for c in obb.cls})}")
cx, cy, w, h, angle = obb.xywhr[0].tolist()
print(f"first box: center=({cx:.0f}, {cy:.0f}) size={w:.0f}x{h:.0f} angle={angle:.2f} rad")
ratio = float((upright_area / rotated_area).median())
print(f"an axis-aligned box would be {ratio:.1f}x larger (median)")
173 objects, classes: ['harbor', 'ship']
first box: center=(619, 850) size=33x108 angle=-0.25 rad
an axis-aligned box would be 2.1x larger (median)

围住同一艘船时,正立边界框覆盖的面积约为旋转框的两倍,多出来的面积都是水。这会带来双重影响:NMS 会看到相邻船只的正立框彼此重叠,可能因此抑制真实目标;任何尺寸估计也会被放大。在 DOTA 等航拍数据集上训练的 OBB 模型能直接处理这类场景。注意角度单位是弧度:-0.25 rad 约为 -14°。

如何在检测、姿态估计和 OBB 之间选择#

场景最佳任务原因
统计人行道上的行人检测答案是是否存在以及所在位置
检测养老院中的跌倒姿态估计身体朝向是信号
统计健身房里的深蹲次数姿态估计膝关节和髋关节角度是信号
用无人机统计停靠的船只OBB船只斜向停放且排列密集
从上方观察传送带上的包裹OBB朝向各不相同,且相邻物体彼此接触
街道上的汽车检测汽车在画面中大致正向排列
姿态和 OBB 就是在检测上多加一项

姿态任务为每个边界框添加关键点;OBB 则添加角度。如果你的代码从不读取关键点或角度,那么普通检测器标注更快、运行更快,也更容易调试。

试试看

在码头图像上运行解决方案中的对比:将 COCO 预训练检测器与 OBB 模型进行比较。记录两者的计数,然后用一句话说明差异有多少来自边界框几何形状,又有多少来自各模型的训练数据。最后,指出你自己的工作中一个适合用姿态估计或 OBB 而非普通检测来解决的问题,并说明代码会应用什么规则。

完成条件
满足以下所有条件,即表示你已完成本课程。
  • 你已经获得 boats.jpg 中船只的两组计数,并用一句话解释了差异。

  • 你能指出姿态规则中筛除被遮挡关键点的代码行,并说明为什么需要这样做。

  • 你已经指出自己工作中一个适合使用姿态估计或 OBB 的问题,并说明代码会应用什么规则。

显示解答
from ultralytics import YOLO

image = "https://ultralytics.com/images/boats.jpg"
upright = YOLO("yolo26n.pt")(image, verbose=False)[0]
rotated = YOLO("yolo26n-obb.pt")(image, verbose=False)[0]
print(f"axis-aligned detector: {len(upright.boxes)} boxes")
print(f"OBB model: {len(rotated.obb)} rotated boxes")
axis-aligned detector: 0 boxes
OBB model: 173 rotated boxes

差异主要来自训练数据,而不是几何形状。yolo26n.pt 在 COCO 上训练,COCO 中的船只大多是从侧面拍摄的;从上方拍摄时,它在默认阈值下什么也检测不到(将 conf 降到 0.05 后,它会把其中几艘识别成瓶子)。OBB 模型则是在航拍图像上训练的。几何形状仍然会带来影响:即使检测器是在航拍船只图像上训练的,它画出的边界框也会大约大一倍,并且在密集排列的行列中与 NMS 冲突。任务和训练数据都必须与部署时的视角相匹配。

接下来做什么

码头示例表明,训练数据与任务同样重要。接下来:检查数据集是否符合实际部署环境。

登录以保存学习进度