Оценка позы и OBB
Извлеки ключевые точки и повёрнутые рамки из YOLO26, задай правило на их основе и определи, когда главным сигналом служат поза или угол.
В доме престарелых нужно знать, если кто-то упал; в порту — считать корабли, стоящие вплотную друг к другу на снимке с дрона. Обычные рамки не подходят ни для одной из задач: по рамке не понять, стоит ли человек прямо, а вертикальная рамка вокруг диагонально расположенного корабля захватывает в основном воду. Оценка позы и OBB созданы именно для этих двух задач.
Практические занятия
Оценка позы: когда важна поза#

Модель оценки позы возвращает рамку для каждого человека и фиксированный список ключевых точек. Модели, обученные на 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: когда важен угол#

У обычной рамки стороны параллельны краям изображения. Ориентированная ограничивающая рамка дополнительно учитывает поворот; её параметры — центр, ширина, высота и угол (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 видит пересечение вертикальных рамок соседних лодок и может удалить реальные объекты, а оценка размера оказывается завышенной. Модели OBB, обученные на аэрофотоснимках, например на DOTA, напрямую справляются с такими сценами. Учти, что угол задаётся в радианах: -0.25 рад — это примерно -14°.
Как выбрать между детекцией, оценкой позы и OBB#
| Сценарий | Подходящая задача | Почему |
|---|---|---|
| Подсчёт людей на тротуаре | Детекция | Нужно определить наличие и местоположение |
| Обнаружение падения в доме престарелых | Оценка позы | Важный сигнал — ориентация тела |
| Подсчёт приседаний в спортзале | Оценка позы | Важный сигнал — углы коленей и бёдер |
| Подсчёт пришвартованных лодок по снимку с дрона | OBB | Лодки расположены по диагонали и стоят вплотную друг к другу |
| Посылки на конвейере, вид сверху | OBB | Объекты повёрнуты по-разному и соприкасаются |
| Автомобили на уровне улицы | Детекция | Автомобили расположены в кадре примерно вертикально |
Pose добавляет ключевые точки к каждой рамке, а 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, где лодки в основном сняты сбоку; на снимке сверху модель не находит ни одной лодки при пороговом значении по умолчанию (снизь conf до 0.05 — и модель примет некоторые лодки за бутылки). Модель OBB обучалась на аэрофотоснимках. Геометрия тоже важна: даже детектор, обученный на снимках лодок с воздуха, построил бы рамки примерно вдвое большего размера и испытывал бы трудности с NMS на плотно расположенных рядах. И задача, и обучающие данные должны соответствовать условиям эксплуатации модели.