ML Kit Pose Detection API로 의미 있는 해석을 도출할 수 있습니다. 다양한 신체 부위의 상대적 위치를 확인하여 포즈의 동작을 확인할 수 있습니다. 이 페이지 몇 가지 예를 보여줍니다.
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 빠른 시작 앱 프로젝트 가져오기 빌드 및 실행을 확인합니다
LivePreviewActivity
페이지로 이동하여 자세 감지를 사용 설정하세요.Run classification
'설정'에서 있습니다. 이제 푸시업과 스쿼트를 분류할 수 있습니다.
자체 CSV 추가
- 앱의 애셋 폴더에 CSV 파일을 추가합니다.
- PoseClassifierProcessor에서 CSV 파일 및 포즈 샘플과 일치하도록
POSE_SAMPLES_FILE
및POSE_CLASSES
변수를 업데이트합니다. - 앱을 빌드하고 실행합니다.
샘플이 충분하지 않으면 분류가 제대로 작동하지 않을 수 있습니다. 일반적으로 포즈 클래스당 약 100개의 샘플이 필요합니다.
자세한 내용을 알아보고 직접 사용해 보려면 MediaPipe Colab 및 MediaPipe 분류 가이드를 확인하세요.
랜드마크 거리를 계산하여 간단한 동작 인식
두 개 이상의 랜드마크가 서로 가까이 있으면 동작을 인식하는 데 사용할 수 있습니다. 예를 들어 손가락 한 개에 대한 랜드마크가 코의 이 부위에 손이 가까우면 사용자가 가장 잘 보였다고 추론할 수 있습니다. 얼굴에 갖다 대고 있을 가능성이 높기 때문입니다.

각도 휴리스틱으로 요가 자세 알아보기
다양한 관절의 각도를 계산하여 요가 자세를 식별할 수 있습니다. 예를 들어 아래 그림 2는 전사 II 요가 자세를 보여줍니다. 대략적인 각도 다음과 같이 쓰여 있습니다.

이 자세는 다음과 같이 신체를 대략적으로 조합한 것으로 설명할 수 있습니다. 파트 각도:
- 양 어깨가 90도 각도
- 양쪽 팔꿈치 180도
- 90도 각도로 앞 다리와 허리
- 뒤쪽 무릎을 180도 각도로
- 허리 각도 135도
포즈 랜드마크를 사용하여 이러한 각도를 계산할 수 있습니다. 예를 들어 오른쪽 앞 다리와 허리의 각도는 오른쪽과 선 사이의 각도입니다. 어깨에서 오른쪽 엉덩이까지, 오른쪽 엉덩이에서 오른쪽 무릎까지의 라인입니다.
포즈를 식별하는 데 필요한 모든 각도를 계산한 후 일치하는지 확인할 수 있습니다. 일치하는 경우 포즈를 인식한 것입니다.
아래의 코드 스니펫은 X 및 Y 좌표를 사용하여 두 신체 부위 사이의 각도를 계산합니다. 이 분류 방식에는 몇 가지 제한사항이 있습니다. X와 Y만 확인하면 계산된 각도가 달라집니다. 피사체와 카메라 사이의 각도에 따라 촬영됩니다. 여러분은 정면을 보여주는 평평하고 직선적인 이미지로 최상의 결과를 얻는 것이 중요합니다. Z 좌표를 사용하여 이 알고리즘을 확장해 보고 사용 사례에 더 적합한지 확인해 볼 수도 있습니다.
Android에서 랜드마크 각도 계산
다음 메서드는 3과 1 사이의 각도를 계산합니다. 있습니다. 반환되는 각도가 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 }
자바
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))
자바
double rightHipAngle = getAngle( pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP), pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE));
iOS에서 랜드마크 각도 계산
다음 메서드는 3과 1 사이의 각도를 계산합니다. 있습니다. 반환되는 각도가 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]];