С помощью API обнаружения позы ML Kit вы можете получить осмысленную интерпретацию позы, проверяя взаимное расположение различных частей тела. На этой странице показано несколько примеров.
Классификация поз и подсчет повторений с помощью алгоритма k-NN
Одним из наиболее распространенных применений определения позы является отслеживание фитнеса. Создание классификатора поз, который распознает определенные фитнес-позы и подсчитывает повторения, может оказаться непростой задачей для разработчиков.
В этом разделе мы описываем, как мы создали собственный классификатор поз с помощью MediaPipe Colab , и демонстрируем рабочий классификатор в нашем примере приложения ML Kit.
Если вы не знакомы с Google Colaboratory, ознакомьтесь с вводным руководством .
Для распознавания поз мы используем алгоритм k-ближайших соседей (k-NN), потому что он прост и с него легко начать. Алгоритм определяет класс объекта на основе ближайших образцов в обучающем наборе.
Выполните следующие шаги, чтобы создать и обучить распознаватель:
1. Соберите образцы изображений
Мы собрали образцы изображений целевых учений из разных источников. Мы выбрали несколько сотен изображений для каждого упражнения, например, позиции «вверх» и «вниз» для отжиманий. Важно собирать образцы, охватывающие разные ракурсы камеры, условия окружающей среды, формы тела и варианты упражнений.
2. Запустите обнаружение позы на образцах изображений.
В результате создается набор ориентиров позы, которые будут использоваться для обучения. Нас не интересует само определение позы, поскольку на следующем этапе мы будем обучать собственную модель.
Алгоритм k-NN, который мы выбрали для пользовательской классификации поз, требует представления вектора признаков для каждого образца и метрики для вычисления расстояния между двумя векторами, чтобы найти цель, ближайшую к образцу позы. Это означает, что мы должны преобразовать только что полученные ориентиры позы.
Чтобы преобразовать ориентиры позы в вектор признаков, мы используем попарные расстояния между предопределенными списками суставов позы, например, расстояние между запястьем и плечом, лодыжкой и бедром, а также левым и правым запястьями. Поскольку масштаб изображений может различаться, перед преобразованием ориентиров мы нормализовали позы, чтобы они имели одинаковый размер туловища и вертикальную ориентацию туловища.
3. Тренируйте модель и считайте повторения.
Мы использовали MediaPipe Colab для доступа к коду классификатора и обучения модели.
Для подсчета повторений мы использовали другой алгоритм Colab для мониторинга порога вероятности целевой позиции позы. Например:
- Когда вероятность класса позы «вниз» впервые превышает заданный порог, алгоритм отмечает, что введен класс позы «вниз».
- Когда вероятность падает ниже порога, алгоритм отмечает, что из класса позы «вниз» произошел выход, и увеличивает счетчик.
4. Интегрируйтесь с приложением быстрого запуска ML Kit.
Вышеупомянутый Colab создает файл CSV, который вы можете заполнить всеми образцами поз. В этом разделе вы узнаете, как интегрировать CSV-файл с приложением быстрого запуска ML Kit для Android, чтобы просматривать пользовательскую классификацию поз в режиме реального времени.
Попробуйте классифицировать позы с помощью образцов, включенных в приложение быстрого запуска.
- Получите проект приложения быстрого запуска ML Kit для Android на Github и убедитесь, что он собирается и работает хорошо.
- Перейдите в
LivePreviewActivity
и включитеRun classification
с обнаружением позы на странице настроек. Теперь вы сможете классифицировать отжимания и приседания.
Добавьте свой CSV-файл
- Добавьте файл CSV в папку ресурсов приложения.
- В PoseClassifierProcessor обновите переменные
POSE_SAMPLES_FILE
иPOSE_CLASSES
, чтобы они соответствовали вашему CSV-файлу и образцам поз. - Создайте и запустите приложение.
Обратите внимание, что классификация может работать неэффективно, если выборок недостаточно. Обычно вам нужно около 100 образцов для каждого класса поз.
Чтобы узнать больше и попробовать это самостоятельно, ознакомьтесь с руководством по классификации MediaPipe Colab и MediaPipe .
Распознавание простых жестов путем расчета расстояния до ориентира
Когда два или более ориентира расположены близко друг к другу, их можно использовать для распознавания жестов. Например, если ориентир одного или нескольких пальцев на руке находится близко к ориентиру носа, можно сделать вывод, что пользователь, скорее всего, касается своего лица.
Распознавание позы йоги с помощью угловой эвристики
Вы можете определить позу йоги, рассчитав углы различных суставов. Например, на рисунке 2 ниже показана поза йоги «Воин II». Примерные углы, определяющие эту позу, записаны в:
Эту позу можно описать как следующую комбинацию примерных углов частей тела:
- Угол 90 градусов на обоих плечах
- 180 градусов в обоих локтях
- Угол 90 градусов на передней ноге и талии
- Угол 180 градусов в заднем колене
- Угол талии 135 градусов
Вы можете использовать ориентиры позы для вычисления этих углов. Например, угол между правой передней ногой и талией — это угол между линией от правого плеча до правого бедра и линией от правого бедра до правого колена.
После того, как вы вычислили все углы, необходимые для идентификации позы, вы можете проверить, есть ли совпадение, и в этом случае вы распознали позу.
В приведенном ниже фрагменте кода показано, как использовать координаты X и Y для расчета угла между двумя частями тела. Этот подход к классификации имеет некоторые ограничения. Если проверять только X и Y, вычисленные углы будут различаться в зависимости от угла между объектом и камерой. Вы получите наилучшие результаты при ровном, прямолинейном изображении в лоб. Вы также можете попробовать расширить этот алгоритм, используя координату Z , и посмотреть, будет ли он работать лучше для вашего варианта использования.
Вычисление углов ориентиров на Android
Следующий метод вычисляет угол между любыми тремя ориентирами. Это гарантирует, что возвращаемый угол находится в диапазоне от 0 до 180 градусов.
Котлин
fun getAngle(firstPoint: PoseLandmark, midPoint: PoseLandmark, lastPoint: PoseLandmark): Double { var result = Math.toDegrees(atan2(lastPoint.getPosition().y - midPoint.getPosition().y, lastPoint.getPosition().x - midPoint.getPosition().x) - atan2(firstPoint.getPosition().y - midPoint.getPosition().y, firstPoint.getPosition().x - midPoint.getPosition().x)) result = Math.abs(result) // Angle should never be negative if (result > 180) { result = 360.0 - result // Always get the acute representation of the angle } return result }
Ява
static double getAngle(PoseLandmark firstPoint, PoseLandmark midPoint, PoseLandmark lastPoint) { double result = Math.toDegrees( atan2(lastPoint.getPosition().y - midPoint.getPosition().y, lastPoint.getPosition().x - midPoint.getPosition().x) - atan2(firstPoint.getPosition().y - midPoint.getPosition().y, firstPoint.getPosition().x - midPoint.getPosition().x)); result = Math.abs(result); // Angle should never be negative if (result > 180) { result = (360.0 - result); // Always get the acute representation of the angle } return result; }
Вот как вычислить угол в правом бедре:
Котлин
val rightHipAngle = getAngle( pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE))
Ява
double rightHipAngle = getAngle( pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE));
Вычисление углов ориентиров на iOS
Следующий метод вычисляет угол между любыми тремя ориентирами. Это гарантирует, что возвращаемый угол находится в диапазоне от 0 до 180 градусов.
Быстрый
func angle( firstLandmark: PoseLandmark, midLandmark: PoseLandmark, lastLandmark: PoseLandmark ) -> CGFloat { let radians: CGFloat = atan2(lastLandmark.position.y - midLandmark.position.y, lastLandmark.position.x - midLandmark.position.x) - atan2(firstLandmark.position.y - midLandmark.position.y, firstLandmark.position.x - midLandmark.position.x) var degrees = radians * 180.0 / .pi degrees = abs(degrees) // Angle should never be negative if degrees > 180.0 { degrees = 360.0 - degrees // Always get the acute representation of the angle } return degrees }
Цель-C
(CGFloat)angleFromFirstLandmark:(MLKPoseLandmark *)firstLandmark midLandmark:(MLKPoseLandmark *)midLandmark lastLandmark:(MLKPoseLandmark *)lastLandmark { CGFloat radians = atan2(lastLandmark.position.y - midLandmark.position.y, lastLandmark.position.x - midLandmark.position.x) - atan2(firstLandmark.position.y - midLandmark.position.y, firstLandmark.position.x - midLandmark.position.x); CGFloat degrees = radians * 180.0 / M_PI; degrees = fabs(degrees); // Angle should never be negative if (degrees > 180.0) { degrees = 360.0 - degrees; // Always get the acute representation of the angle } return degrees; }
Вот как вычислить угол в правом бедре:
Быстрый
let rightHipAngle = angle( firstLandmark: pose.landmark(ofType: .rightShoulder), midLandmark: pose.landmark(ofType: .rightHip), lastLandmark: pose.landmark(ofType: .rightKnee))
Цель-C
CGFloat rightHipAngle = [self angleFromFirstLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightShoulder] midLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightHip] lastLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightKnee]];