Ultralytics YOLO27: Встать в лист ожидания.
Get Started
Уроксредний

Пользовательские ключевые точки и метрики OKS

Спроектируй схему ключевых точек для объектов, не являющихся людьми, создай и проверь YAML-файл датасета, а также задай сигмы OKS в соответствии с точностью, с которой можно расположить каждую точку.

Оценка позы нужна не только для людей. Кончики вил погрузчика, углы этикетки посылки, нос и хвост животного — любой объект со стабильными точками, которым можно дать названия, подходит для датасета поз. Схема, которую ты задашь в первый день, останется неизменной: от неё зависят каждая метка, каждая пара для отражения и каждая метрика, а её изменение позже потребует повторной разметки всего датасета. В этом уроке ты правильно задашь схему до того, как кто-либо разметит тысячу изображений, и узнаешь, как именно метрика будет оценивать результат.

Результат

Создай YAML-файл датасета с пользовательской схемой ключевых точек, парами для отражения и сигмами OKS, проверку меток, успешно работающую на реальных данных, и прогноз допустимой ошибки в пикселях для каждой сигмы.

Ускоренное прохождение
Если ты уже освоился, вот краткая версия.
  1. Выбери от 3 до 12 однозначно определяемых точек, которые присутствуют в каждой позе и используются последующим кодом.

  2. Задай порядок, затем создай flip_idx, поменяв местами левые и правые парные точки; центральные точки должны соответствовать самим себе.

  3. Добавь kpt_oks_sigmas, если точки различаются по точности, с которой разметчики могут их расположить.

  4. До обучения запусти проверку меток: проверь количество столбцов, значения видимости и то, что flip_idx является сам обратным.

Практические занятия

Спроектируй схему ключевых точек#

У хороших ключевых точек есть три свойства: однозначность (два разметчика помещают точку в одно и то же место), стабильность (точка присутствует во всех возможных позах объекта) и практическая польза (её использует последующий расчёт). Рассмотрим погрузчик, снятый потолочной камерой, когда нужно определить, подняты ли вилы во время движения машины:

ИндексКлючевая точкаЗачем она нужнаПарная точка при отражении
0left_fork_tipДлина и направление вил1
1right_fork_tipДлина и направление вил0
2mast_topВысота вил относительно мачты2 (сама себе)
3mast_baseОпорная точка на шасси3 (сама себе)

Каждая точка нужна для расчёта: кончики вил показывают направление, а пара точек на мачте — высоту. Прежде чем добавить точку, проверь её по одним и тем же критериям:

Не добавляй точки «на всякий случай»: каждая увеличивает время разметки каждого экземпляра, а точки, которые никто не использует, лишь добавляют шум в метрику. Публичные датасеты показывают диапазон: Tiger-Pose использует 12 точек без значений видимости, Dog-Pose — 24, а Hand Keypoints — 21:

Схема ключевых точек руки с 21 пронумерованной точкой: от запястья под номером 0 через суставы каждого пальца до кончика мизинца под номером 20

Примеры Dog-Pose: щенок с рамкой собаки и цветными ключевыми точками на морде, лапах и туловище

Создай YAML-файл датасета#

path: forklift-pose
train: images/train
val: images/val

kpt_shape: [4, 3] # 4 keypoints, each x, y, visibility
flip_idx: [1, 0, 2, 3] # fork tips swap, mast points stay
kpt_oks_sigmas: [0.05, 0.05, 0.08, 0.08] # tips are sharp, mast ends are fuzzier

names:
  0: forklift

kpt_names:
  0:
    - left_fork_tip
    - right_fork_tip
    - mast_top
    - mast_base
  • kpt_names задаёт порядок точек для людей и инструментов отдельно для каждого класса.
  • flip_idx должен быть сам себе обратным: после двух применений каждый индекс должен вернуться к исходному значению.
  • kpt_oks_sigmas — необязательный параметр, который задаёт веса потерь ключевых точек при обучении и OKS при валидации; если он указан, в нём должно быть по одному положительному значению на ключевую точку, иначе запуск остановится с ValueError.

На Ultralytics Platform та же схема используется как шаблон скелета. Новый шаблон можно создать на основе существующего (Start from…), чтобы переименовать и удалить ненужные точки вместо размещения каждой ключевой точки вручную. Сохрани порядок индексов из таблицы схемы:

Диалоговое окно New Skeleton Template на Ultralytics Platform: скелет человека из 17 точек создан на основе существующего шаблона; для переименования перечислены ключевые точки от носа до левого запястья

Проверь метки перед обучением#

Ошибки в датасетах для оценки позы могут оставаться незамеченными: если в строке не хватает одной ключевой точки, все последующие значения смещаются, а значение видимости 3 из-за ошибки экспортёра обрабатывается как обычное число. Эта проверка загружает YAML через собственный загрузчик Ultralytics и проверяет каждую строку. Скрипт сразу работает с COCO8-pose; замени путь на путь к своему YAML-файлу:

from pathlib import Path

from ultralytics.data.utils import check_det_dataset

data = check_det_dataset("coco8-pose.yaml")  # resolves paths, downloads if needed
k, dims = data["kpt_shape"]
flip = data.get("flip_idx", [])
assert not flip or sorted(flip) == list(range(k)), "flip_idx must list every keypoint index once"
assert all(flip[flip[i]] == i for i in range(len(flip))), "flip_idx must undo itself: pairs swap, center points stay"

expected = 5 + k * dims  # class + 4 box values + k keypoints of `dims` values
bad = 0
for label in Path(data["path"], "labels").rglob("*.txt"):
    for n, row in enumerate(label.read_text().splitlines(), 1):
        values = row.split()
        if len(values) != expected:
            bad += 1
            print(f"{label.name}:{n} has {len(values)} values, expected {expected}")
        elif dims == 3 and not {float(v) for v in values[7::3]} <= {0, 1, 2}:
            bad += 1
            print(f"{label.name}:{n} has a visibility flag outside 0/1/2")
print(f"kpt_shape={data['kpt_shape']}, flip_idx ok, {bad} bad rows")
kpt_shape=[17, 3], flip_idx ok, 0 bad rows

values[7::3] начинается с первого флага видимости (класс, четыре значения рамки, затем x y v) и берёт каждое третье значение после него.

Как OKS оценивает ключевую точку#

В mAP для оценки позы вместо IoU рамок используется сходство ключевых точек объекта (OKS). Для каждой размеченной ключевой точки OKS преобразует расстояние в пикселях между предсказанием и меткой в оценку от 0 до 1, учитывая размер объекта и сигму этой ключевой точки, а затем усредняет оценки по размеченным точкам. Pose mAP50-95 — это средняя точность при порогах OKS от 0.50 до 0.95, как и mAP для рамок усредняется по порогам IoU; общий механизм описан в руководстве по метрикам производительности.

Проще всего понять сигмы на измерениях. Этот скрипт оценивает одно и то же предсказание погрузчика с двумя сигмами, используя функцию kpt_iou, которую вызывает валидатор оценки позы:

import torch

from ultralytics.utils.metrics import kpt_iou

# One labeled forklift, 4 keypoints (x, y, visibility) in pixels, inside a 200 x 120 px box
label = torch.tensor([[[40.0, 30, 2], [40, 90, 2], [170, 20, 2], [170, 100, 2]]])
area = torch.tensor([200 * 120 * 0.53])  # Ultralytics scales box area by 0.53 to approximate object area

for offset in (2, 5, 10, 20):
    pred = label.clone()
    pred[..., 0] += offset  # every predicted keypoint lands `offset` pixels to the right
    sharp = kpt_iou(label, pred, area, sigma=[0.025] * 4).item()
    loose = kpt_iou(label, pred, area, sigma=[0.10] * 4).item()
    print(f"{offset:2d} px off: OKS {sharp:.2f} with sigma 0.025, {loose:.2f} with sigma 0.10")
 2 px off: OKS 0.94 with sigma 0.025, 1.00 with sigma 0.10
 5 px off: OKS 0.67 with sigma 0.025, 0.98 with sigma 0.10
10 px off: OKS 0.21 with sigma 0.025, 0.91 with sigma 0.10
20 px off: OKS 0.00 with sigma 0.025, 0.67 with sigma 0.10

Для погрузчика размером 200 × 120 пикселей предсказание с ошибкой 10 пикселей не проходит порог OKS 0.50 при сигме 0.025, но проходит все пороги вплоть до 0.90 при сигме 0.10. Одна модель, одна ошибка — но значения mAP для позы сильно различаются.

СигмаЭффектПрименение
Малая (≈ 0.025–0.05)Даже ошибка в несколько пикселей уже снижает оценкуЧётко различимые точки: глаза, кончики вил, углы
Большая (≈ 0.08–0.10)Широкий допускНеоднозначные точки: бёдра, середина мачты
По умолчаниюСигмы COCO для kpt_shape: [17, 3]; в противном случае для каждой точки используется значение 1/KНачни с этого значения, затем измерь

Сигмы должны отражать, насколько согласованно разметчики располагают каждую точку. Измерь это: попроси двух человек разметить одни и те же 50 изображений и сравни расстояния для каждой ключевой точки. Для точек, по которым разметчики расходятся во мнениях, нужны более крупные сигмы; иначе метрика будет штрафовать модель за ошибки разметки.

Метрики сопоставимы только в пределах одной схемы

Изменение kpt_oks_sigmas меняет pose mAP, даже если модель остаётся прежней, как показывает таблица выше. Задай значения сигм до первого базового замера, записывай их с каждым результатом и никогда не сравнивай запуски, проверенные с разными значениями.

Попробовать

Спроектируй схему ключевых точек для объекта из своей предметной области: перечисли 3–8 ключевых точек, укажи для каждой причину в одной строке, парную точку при отражении и сигму. Создай YAML-файл, запусти check_pose_labels.py на COCO8-pose, а затем на нескольких собственных метках. Используй oks.py, чтобы определить, при какой ошибке в пикселях твоя самая чёткая точка перестаёт проходить порог OKS 0.5.

Критерии готовности
Урок считается завершённым, если выполнены все условия ниже.
  • В таблице схемы указаны все ключевые точки, их назначение, парные точки при отражении и сигмы.

  • В YAML-файле заданы kpt_shape, сам обратный параметр flip_idx и, если используются, положительные сигмы для всех ключевых точек. На предоставленном наборе данных проверка сообщает, что неверных строк нет.

  • Ты можешь указать ошибку в пикселях, при которой самая чёткая ключевая точка перестаёт учитываться при OKS 0.5.

Показать решение

Пример посылки на конвейере, снятой сверху, когда нужно определить, обращена ли этикетка вверх и в какую сторону она повёрнута:

ИндексКлючевая точкаНазначениеПарная точка при отраженииСигма
0label_top_leftОриентация этикетки10.04
1label_top_rightОриентация этикетки00.04
2label_bottom_rightОриентация этикетки30.04
3label_bottom_leftОриентация этикетки20.04
kpt_shape: [4, 3]
flip_idx: [1, 0, 3, 2]
kpt_oks_sigmas: [0.04, 0.04, 0.04, 0.04]

flip_idx проходит проверку на саму себя как обратное преобразование: 0 → 1 → 0 и 2 → 3 → 2. При отражении метки левая и правая стороны меняются местами, а верхняя и нижняя остаются на месте, поэтому верхняя левая точка образует пару с верхней правой, а не с нижней правой.

Чтобы определить допуск, задай sigma=[0.04] * 4 в oks.py и увеличивай смещения; масштаб задаётся рамкой этикетки, поэтому используй рамку размером с типичную этикетку на посылках в твоих изображениях. Смещение, при котором OKS впервые падает ниже 0.5, — это максимальная ошибка, которую допускает точность разметки.

Что дальше

Далее мы рассмотрим объекты, для которых важна ориентация, но форма представляет собой простой прямоугольник: ориентированные рамки.

Войди, чтобы сохранять прогресс