Реализация распознавания поз (Pose Estimation) в мобильном приложении
Pose estimation — это детекция 17–33 ключевых точек скелета человека в реальном времени. Задача используется в фитнес-приложениях (подсчёт повторений, оценка техники), медицинских (реабилитация, анализ походки) и AR-продуктах. Технически она сложнее детекции объектов: нужна не только точность локализации точек, но и стабильность между кадрами.
Выбор модели: MoveNet vs BlazePose vs ML Kit Pose Detection
MoveNet Lightning/Thunder (Google) — лучший баланс точность/скорость для мобиля. Lightning: 17 точек, 30+ FPS на iPhone 12, TFLite-оптимизирован. Thunder: точнее, ~15 FPS. Доступен через TFLite Task Library как PoseLandmarker.
MediaPipe BlazePose / Pose Landmarker — 33 точки (включая точки лица, ног, рук). Нужен когда важны детали: угол запястья, положение пальцев. Латентность: ~25 ms на Pixel 7 GPU, ~55 ms на CPU.
ML Kit Pose Detection — 33 точки, простая интеграция через Firebase. In-process, no network. Чуть медленнее MoveNet, но проще в интеграции для Android/iOS кроссплатформы.
Для фитнес-трекинга повторений — MoveNet Lightning достаточно. Для медицинского анализа походки — BlazePose с 33 точками и z-координатами.
Подсчёт повторений: не так просто, как кажется
Типичная задача — «посчитать приседания». Наивный подход: следить за Y-координатой бедра, считать пересечения порога. Работает на идеальных условиях, ломается в реальности.
Правильный подход: вычислять угол в коленном суставе через скалярное произведение векторов [HIP → KNEE] и [KNEE → ANKLE]. Приседание = угол опустился ниже 120°. Вставание = угол вернулся выше 160°. Состояние-машина: STANDING → DOWN → STANDING = 1 повторение.
Угол в 3D через z-координаты работает стабильнее 2D, если камера не строго сбоку. BlazePose/MoveNet возвращают z в нормализованных единицах — это оценка глубины, не абсолютные метры. Достаточно для угла, недостаточно для абсолютных расстояний.
Сглаживание landmarks — обязательно. Сырые данные прыгают на 3–5 пикселей между кадрами. Простейший фильтр — экспоненциальное скользящее среднее EMA(α=0.6) по каждой координате. MediaPipe предоставляет VelocityFilter — лучше для нелинейных движений.
Интеграция: iOS и Android
iOS: MediaPipe Tasks Vision через Swift Package Manager. PoseLandmarker с runningMode = .liveStream. Callback возвращает PoseLandmarkerResult с landmarks (нормализованные) и worldLandmarks (метровые, для физических расчётов). Рисовать скелет поверх AVCaptureVideoPreviewLayer через CAShapeLayer — не через UIKit UIView (тяжелее).
Android: com.google.mediapipe:tasks-vision. PoseLandmarker.create() с BaseOptions.GPU_DELEGATE на поддерживаемых устройствах. Отрисовка через Canvas.drawLine() на SurfaceView.
Кейс: приложение для реабилитации после травмы колена. Нужна оценка угла сгибания в колене при выполнении упражнений. Использовали MoveNet Thunder (точнее Lightning при небольших движениях). Угол через скалярное произведение 2D-векторов (z не нужен, камера строго сбоку — условие задачи). Ошибка измерения угла относительно гониометра врача: ±4.2°. Достаточно для контроля прогресса, не для медицинской диагностики — это важно коммуницировать в UI.
Скелет рисуем через CAShapeLayer с анимацией CABasicAnimation по path — это плавнее, чем перерисовывать в drawRect каждый кадр.
Типичные ошибки
Отрисовывать скелет в координатах модели без трансформации в координаты превью. Точки MoveNet — нормализованные (0..1), координаты превью зависят от gravity AVCaptureVideoPreviewLayer. Нужна явная трансформация с учётом aspect ratio и crop.
Запускать инференс на main thread — верный путь к 0 FPS UI. Инференс на DispatchQueue.global(qos: .userInteractive) (iOS) или Executors.newSingleThreadExecutor() (Android).
Сроки
Базовый скелет на видеопотоке + подсчёт одного упражнения — 1–2 недели. Полноценный фитнес-модуль с несколькими упражнениями, голосовой обратной связью и историей — 3–4 недели. Стоимость рассчитывается индивидуально.







