Training моделей оценки позы: ViTPose, YOLOv8-pose, MediaPipe
Pose estimation — определение координат ключевых точек тела (скелет, руки, лицо). Задача используется в спортивной аналитике, охране труда, реабилитации, управлении жестами. Архитектурный выбор зависит от требований к точности и latency: от MediaPipe (мобайл, <10ms) до ViTPose-H (максимальная точность, ~50ms на A100).
COCO Keypoints и кастомная онтология скелета
COCO определяет 17 keypoints (нос, плечи, локти, запястья, бёдра, колени, щиколотки). Для промышленных задач нужна расширенная онтология:
# Кастомная онтология для оценки позы рабочего (охрана труда)
WORKER_SKELETON = {
'keypoints': [
'nose', 'left_eye', 'right_eye', 'left_ear', 'right_ear',
'left_shoulder', 'right_shoulder',
'left_elbow', 'right_elbow',
'left_wrist', 'right_wrist',
'left_hip', 'right_hip',
'left_knee', 'right_knee',
'left_ankle', 'right_ankle',
# Расширение для промышленности
'left_hand_center', 'right_hand_center', # для детекции хватки
'head_top', # для шлема
'neck'
],
'skeleton': [
[16, 14], [14, 12], [17, 15], [15, 13], [12, 13],
[6, 12], [7, 13], [6, 7], [6, 8], [7, 9],
[8, 10], [9, 11], [2, 3], [1, 2], [1, 3],
[2, 4], [3, 5], [4, 6], [5, 7],
[10, 18], [11, 19], [1, 21], [1, 20] # кастомные соединения
]
}
ViTPose: обучение и fine-tuning
ViTPose — трансформерная архитектура для pose estimation, state-of-the-art на COCO. Fine-tuning на кастомной онтологии:
import torch
import torch.nn as nn
from mmpose.apis import init_model, inference_topdown
from mmpose.models import build_posenet
from mmengine.config import Config
def build_vitpose_custom(
num_keypoints: int = 21, # кастомное количество точек
pretrained_checkpoint: str = 'vitpose_base_coco.pth'
) -> nn.Module:
cfg = Config.fromfile('configs/body_2d_keypoint/topdown_heatmap/'
'vitpose/td-hm_ViTPose-base_8xb64-210e_coco-256x192.py')
# Меняем голову под новое количество keypoints
cfg.model.head.num_joints = num_keypoints
cfg.model.test_cfg.num_joints = num_keypoints
model = build_posenet(cfg.model)
# Загружаем pretrained веса, исключая голову
state_dict = torch.load(pretrained_checkpoint)['state_dict']
state_dict_filtered = {
k: v for k, v in state_dict.items()
if 'keypoint_head' not in k # голову инициализируем заново
}
model.load_state_dict(state_dict_filtered, strict=False)
return model
YOLOv8-pose — быстрая альтернатива
Для real-time приложений (видеонаблюдение, спорт):
from ultralytics import YOLO
# Fine-tuning YOLOv8-pose на кастомных данных
model = YOLO('yolov8m-pose.pt')
results = model.train(
data='pose_dataset.yaml', # включает keypoint_shape: [17, 3]
imgsz=640,
batch=16,
epochs=100,
device='0',
kobj=1.0, # вес лосса keypoint объектности
kpt_shape=[17, 3] # [num_keypoints, visibility_flag]
)
Анализ позы: определение эргономических нарушений
Кейс из практики: система мониторинга охраны труда на складе. YOLOv8l-pose + классификатор поз, 40 камер, 12 часов в сутки.
import numpy as np
from typing import Optional
class ErgoRiskAnalyzer:
"""
Оценка эргономических рисков по позе рабочего.
Метрика: RULA (Rapid Upper Limb Assessment) — стандарт ISO 11228.
"""
def calculate_trunk_angle(
self,
left_shoulder: np.ndarray, # [x, y]
right_shoulder: np.ndarray,
left_hip: np.ndarray,
right_hip: np.ndarray
) -> float:
"""Угол наклона торса от вертикали в градусах"""
shoulder_mid = (left_shoulder + right_shoulder) / 2
hip_mid = (left_hip + right_hip) / 2
trunk_vec = shoulder_mid - hip_mid
vertical_vec = np.array([0, -1]) # вверх в системе координат изображения
cos_angle = np.dot(trunk_vec, vertical_vec) / (
np.linalg.norm(trunk_vec) * np.linalg.norm(vertical_vec) + 1e-6
)
return float(np.degrees(np.arccos(np.clip(cos_angle, -1, 1))))
def assess_lifting_risk(
self,
keypoints: dict, # {'left_shoulder': [x,y], 'right_shoulder': [x,y], ...}
confidence_threshold: float = 0.5
) -> dict:
"""
RULA-подобная оценка риска подъёма груза.
Риски: прямая спина OK, наклон 20-60° — предупреждение, >60° — критично.
"""
required_kpts = ['left_shoulder', 'right_shoulder', 'left_hip', 'right_hip']
if not all(
keypoints.get(k) is not None and keypoints[k][2] > confidence_threshold
for k in required_kpts
):
return {'risk': 'unknown', 'reason': 'low_confidence_keypoints'}
trunk_angle = self.calculate_trunk_angle(
keypoints['left_shoulder'][:2],
keypoints['right_shoulder'][:2],
keypoints['left_hip'][:2],
keypoints['right_hip'][:2]
)
if trunk_angle > 60:
risk_level = 'critical'
elif trunk_angle > 20:
risk_level = 'warning'
else:
risk_level = 'ok'
return {
'risk': risk_level,
'trunk_angle_deg': round(trunk_angle, 1),
'rula_trunk_score': 4 if trunk_angle > 60 else (3 if trunk_angle > 20 else 1)
}
На пилотном проекте (склад, 120 рабочих): система выявила 34 случая систематического подъёма с нарушением в течение смены. После коррекции рабочих мест — снижение жалоб на боли в спине на 41% за 3 месяца.
Сравнение методов
| Модель | AP COCO | Latency | Устройство | Применение |
|---|---|---|---|---|
| MediaPipe Pose | 67.4 | <5ms | CPU/телефон | Мобайл, IoT |
| YOLOv8n-pose | 49.0 | 3ms | GPU | Real-time видео |
| YOLOv8l-pose | 65.5 | 9ms | GPU | Точность+скорость |
| ViTPose-B | 75.8 | 18ms | GPU | Высокая точность |
| ViTPose-H | 79.1 | 48ms | GPU | Максимум |
Сроки
| Задача | Срок |
|---|---|
| Fine-tuning стандартного скелета | 2–4 недели |
| Кастомная онтология + обучение | 4–8 недель |
| Полная система с аналитикой поз | 8–14 недель |







