借助机器学习套件姿势检测 API,您可以得出有意义的解释 检查身体各个部位的相对位置。当前页面 演示了几个示例。
使用 k-NN 算法进行姿态分类和重复计数
姿势检测最常见的应用之一就是健身跟踪。 构建可识别特定健身姿势和计数的姿势分类器 对开发者来说,重复是一件很有挑战性的任务。
在本部分中,我们将介绍如何构建自定义姿势 使用 MediaPipe Colab 训练分类器,以及 在我们的机器学习套件示例应用中演示了一个可正常运行的分类器。
如果您不熟悉 Google Colaboratory,请查看 入门指南。
为了识别姿势,我们使用 k-最近邻算法 (k-NN), 而且很容易上手该算法会根据 距离最近的样本。
如需构建和训练识别器,请按以下步骤操作:
1. 收集图片样本
我们从各种来源收集了目标运动的图片样本。周三 为每次锻炼选择几百张图片,例如“向上”和“down”位置 做俯卧撑。请务必收集涵盖不同摄像头的样本 角度、环境条件、体型和锻炼变化。
![](https://developers.google.cn/static/ml-kit/images/vision/pose-detection/terminal_pose.png?authuser=9&hl=da)
2. 对示例图片运行姿势检测
这会生成一组用于训练的姿势特征点。我们不是 对姿势检测本身很感兴趣,因为我们将训练我们的 构建自己的模型。
我们为自定义姿势分类选择的 k-NN 算法需要一个 每个样本的特征向量表示法以及要计算的指标 两个向量之间的距离,以查找最接近姿势样本的目标。 这意味着我们必须转换刚刚获取的姿势特征点。
为了将姿势特征点转换为特征向量,我们使用两对距离 在预定义的姿势关节列表之间,如手腕和 肩膀、脚踝、臀部以及左右手腕。由于图片的缩放比例 可以变化,我们对姿势进行了标准化处理,使其具有相同的躯干大小和垂直躯干 然后再转换地标。
3. 训练模型并计算重复次数
我们使用 MediaPipe Colab 访问分类器的代码, 训练模型。
为了统计重复次数,我们使用了另一种 Colab 算法来监控 目标姿势位置的阈值。例如:
- 当“下降”概率姿态类达到指定阈值后, 算法第一次将“down”姿势类。
- 当概率降至阈值以下时,算法会将 “down”姿态类已退出并增加计数器。
![](https://developers.google.cn/static/ml-kit/images/vision/pose-detection/pose_classification.gif?authuser=9&hl=da)
4. 与机器学习套件快速入门应用集成
上面的 Colab 会生成一个 CSV 文件,您可以在其中填充自己的所有姿势 示例。在本部分中,您将了解如何将 CSV 文件与 Android 机器学习套件快速入门应用,用于实时查看自定义姿势分类。
尝试使用快速入门应用中捆绑的样本进行姿势分类
- 获取机器学习套件 Android 快速入门应用项目 并确保它能顺利构建并运行
- 前往
LivePreviewActivity
并启用姿势检测Run classification
在“设置”中页面。现在,您应该能够对俯卧撑和下蹲进行分类了。
添加您自己的 CSV 文件
- 将您的 CSV 文件添加到应用的素材资源文件夹中。
- 在 PoseClassifierProcessor 中,
更新
POSE_SAMPLES_FILE
和POSE_CLASSES
变量,以匹配 CSV 文件和姿势示例。 - 构建并运行应用。
请注意,如果样本数量不足,分类效果可能不会很理想。 通常,每个姿势类别需要大约 100 个样本。
如需了解详情并亲自尝试,请访问 MediaPipe Colab 和 MediaPipe 分类指南。
通过计算地标距离识别简单手势
当两个或多个位置相距较近时,它们可用于 手势识别。例如,当一根或多根手指上的 就可以推断出用户最接近 都有可能碰过自己的脸
![](https://developers.google.cn/static/ml-kit/images/vision/pose-detection/touching_face.gif?authuser=9&hl=da)
通过角度启发法识别瑜伽姿势
您可以通过计算各个关节的角度来识别瑜伽姿势。对于 例如,下面的图 2 显示了 Warrior II 瑜伽姿势。大致角度 分别用以下语言编写:
![](https://developers.google.cn/static/ml-kit/images/vision/pose-detection/warrier2_sketch.png?authuser=9&hl=da)
此姿势可以描述为以下近似身体组合 零件角度:
- 两侧肩膀都 90 度角
- 双肘 180 度
- 前腿和腰部呈 90 度角
- 后膝关节 180 度角
- 腰部 135 度角
您可以使用姿势特征点来计算这些角度。例如,角度 右前腿和右腰之间的夹角 以及从右髋至右膝的线条。
计算出识别姿势所需的全部角度后, 看看是否有匹配,如果是这样,您就已经识别出了姿势。
以下代码段演示了如何使用 X 和 Y 坐标 计算两个身体部位之间的角度。这种分类方法 存在一些限制只检查 X 和 Y,计算出的角度会发生变化 确定拍摄对象与相机之间的角度。您将获得 使用水平、正向的正面图片实现最佳效果。您还可以 请尝试利用 Z 坐标 看看它是否更适合您的用例。
在 Android 上计算地标角度
以下方法计算任意三点之间的夹角, 地标。它确保返回的角度介于 0 度到 180 度。
Kotlin
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 }
Java
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; }
计算右腰的角度的方法如下:
Kotlin
val rightHipAngle = getAngle( pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE))
Java
double rightHipAngle = getAngle( pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE));
在 iOS 上计算地标角度
以下方法计算任意三点之间的夹角, 地标。它确保返回的角度介于 0 度到 180 度。
Swift
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 }
Objective-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; }
计算右腰的角度的方法如下:
Swift
let rightHipAngle = angle( firstLandmark: pose.landmark(ofType: .rightShoulder), midLandmark: pose.landmark(ofType: .rightHip), lastLandmark: pose.landmark(ofType: .rightKnee))
Objective-C
CGFloat rightHipAngle = [self angleFromFirstLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightShoulder] midLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightHip] lastLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightKnee]];