Google MediaPipe를 활용한 인체관절 각도측정

개요

안녕하세요. 평소 육체적, 정신적 건강을 위해 자전거를 즐겨탑니다.

훈련양이 적어 실력이 부족함에도 늘 더 잘타고 싶은 욕망에 좀 더 시간대비 효과적인 훈련방법에 대한 고민이 많습니다. 😓

특히 페달링을 하는 각도와 안장의 높이에 대해 여러 공부를 하고 있습니다.

그러던 와중에 구글의 MediaPipe라는 기술을 알게 되었고 취미인 싸이클에 적용하기 위해 앱을 만들어 보았습니다. 

꼭 싸이클분야가 아니어도 아래의 내용을 읽은 후 관절 번호를 변경하면 신체의 어느 부위라도 관절 인식 및 각도 측정이 가능합니다.

YouTube 운동영상 녹화후 분석

골격인식 후 팔꿈치, 무릎각도 측정

주요 기능으로

1. 실시간, 또는 저장된 동영상에서 인체 골격 추출

2. 골격의 위치좌표를 역탄젠트 함수로 각도측정

3. 어깨, 팔꿈치, 팔목 3 좌표로 팔꿈치(ELBOW) 각도 측정

4. 엉덩관절, 무릎, 발목 3 좌표로 무릎(KNEE) 각도 측정

 


Google MediaPipe

MediaPipe 솔루션은 애플리케이션에 인체특징인식 관련  인공지능 (AI) 및 머신러닝 (ML) 기법을 빠르게 적용할 수 있는 라이브러리와 도구 모음을 제공합니다.

Google MediaPipe 소개
 

아래의 솔루션에서 인체 특징을 감지할 수 있으며 저는 "Pose Landmark" 기술을 활용해 앱을 제작하였습니다. 

보통 구글 GCP(Google Cloud Platform) 기술들은 앱에서 사용시 유료 Subscription key 키를 받아야지만 쓸 수 있었는데 MediaPipe는 비용 이야기는 일단 없는 것 같습니다.

앱으로 제작할 때도 API Key를 요구하는 절차는 없었습니다.

MediaPipe 사용가능 솔루션

워낙 방대하므로 세부 내용은 구글의 MediaPipe 링크를 참조하기 바랍니다.

이 글에서는 MediaPipe와 OpenCV를 이용해 만들어진 앱의 소스코드에 대해 자세히 설명하겠습니다.

 

개발환경

  • MS Windows 11 Pro, Visual Studio 2022

  • Python 3.11.9 64bit

  • Google MediaPipe 0.10.14

  • OpenCV 4.10.0.84

     

소스코드

Git Link : https://github.com/justdoit76/Google_MediaPipe

파이썬 외부 모듈인 Google MediaPipe, OpenCV 설치가 필요합니다.

import cv2
import mediapipe as mp
import math

print(mp.__file__)

L_ELBOW = (11, 13, 15)
L_KNEE = (23, 25, 27)
R_ELBOW = (12, 14, 16)
R_KNEE = (24, 26, 28)

bodys = [L_ELBOW, R_ELBOW, L_KNEE, R_KNEE]

# MediaPipe의 Pose 모델 초기화
mp_pose = mp.solutions.pose
pose = mp_pose.Pose()
mp_drawing = mp.solutions.drawing_utils

def calculate_angle(point1, point2, point3):
    # 좌표 추출
    x1, y1 = point1 
    x2, y2 = point2
    x3, y3 = point3
    
    # 벡터 사이의 각도 계산
    angle = math.degrees(math.atan2(y3 - y2, x3 - x2) - math.atan2(y1 - y2, x1 - x2))
    angle = abs(angle) # 각도를 절댓값으로 변환
    if angle > 180:
        angle = 360 - angle
    return angle

# 비디오 파일 열기
try:
    #cap = cv2.VideoCapture('sample3.mp4')
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
except Exception as e:
    print('Cam Error : ', e)
else:
    width = 1024
    height = 768

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        # 프레임 리사이즈
        frame = cv2.resize(frame, (width, height))

        # 이미지를 RGB로 변환 (MediaPipe는 RGB 이미지를 사용)
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    
        # Pose 모델을 통해 골격 추출
        result = pose.process(rgb_frame)

        # 원본 프레임에 골격 표시
        if result.pose_landmarks:
            mp_drawing.draw_landmarks(frame, result.pose_landmarks, mp_pose.POSE_CONNECTIONS)
            
            # 좌표 추출
            landmarks = result.pose_landmarks.landmark
            
            for body in bodys:
                pts = []
                for i in range(len(body)):
                    pt = (landmarks[body[i]].x * frame.shape[1], landmarks[body[i]].y * frame.shape[0])                     
                    pts.append(pt)
            
                # 각도 계산 (예: 어깨-엉덩이-무릎 각도)
                angle = calculate_angle(pts[0], pts[1], pts[2])
            
                # 각도 화면에 표시
                cv2.putText(frame, str(int(angle)), 
                            (int(pts[1][0]), int(pts[1][1])), 
                            cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 200, 0), 2, cv2.LINE_AA)

        # 결과 프레임을 화면에 출력
        cv2.imshow('Cyclist Pose Detection v 1.0 (Ocean Coding School), Exit : q', frame)

        if cv2.waitKey(1) & 0xFF == ord('q') :
            break

cap.release()
cv2.destroyAllWindows()

 

[1~3 Line] 

필요한 모듈 불러오기

cv2: 컴퓨터 비전 작업을 위한 OpenCV 라이브러리.

mediapipe: 얼굴 감지, 손 추적, 포즈 감지 등 컴퓨터 비전 작업을 위한 머신러닝 파이프라인 라이브러리.

math: 수학적 연산을 위한 표준 Python 라이브러리.

 

[7~10 Line] 

포즈 랜드마크 정의

이 튜플들은 MediaPipe Pose가 감지하는 특정 신체 관절의 랜드마크 인덱스를 나타냅니다. 인덱스는 pose_landmarks 리스트의 랜드마크에 해당하며,

L_ELBOW: 왼쪽 팔꿈치 랜드마크 (어깨, 팔꿈치, 손목).

L_KNEE: 왼쪽 무릎 랜드마크 (엉덩이, 무릎, 발목).

R_ELBOW: 오른쪽 팔꿈치 랜드마크 (어깨, 팔꿈치, 손목).

R_KNEE: 오른쪽 무릎 랜드마크 (엉덩이, 무릎, 발목).

bodys: 각 신체 부위의 각도를 계산하기 위해 반복적으로 사용될 튜플 리스트.

아래의 사진의 관절 번호로 원하는 관절을 변경해 사용가능합니다. 

출처:Google pose_landmarks_index


[14~17 Line] 

MediaPipe Pose 모델 초기화

mp_pose: MediaPipe Pose 모듈에 대한 참조.

pose: 포즈 감지를 위한 Pose 클래스의 인스턴스를 생성.

mp_drawing: 포즈를 화면에 그리기 위한 유틸리티.

 

[19~30 Line] 

관절각도 계산 함수

이 함수는 세 개의 점(각 관절의 좌표)이 주어졌을 때 두 벡터 사이의 각도를 계산합니다.

math.atan2() 함수를 사용하여 각도를 구하고, math.degrees()로 라디안을 도 단위로 변환합니다.

각도는 절댓값으로 변환되며, 180도를 초과하는 경우, 360에서 뺀 값을 사용하여 최종 각도를 반환합니다. 


[32~84 Line] 

비디오 캡쳐 및 포즈 감지

웹캠 열기: cv2.VideoCapture(0, cv2.CAP_DSHOW)는 웹캠을 열어 비디오 스트림을 캡처합니다.예외 발생 시 오류 메시지를 출력합니다.

프레임 크기 조정: 프레임의 너비와 높이를 설정하고, 읽은 각 프레임을 해당 크기로 조정합니다.

RGB로 변환: MediaPipe는 RGB 이미지를 사용하므로 프레임을 BGR에서 RGB로 변환합니다.

포즈 감지: pose.process(rgb_frame)를 통해 포즈 랜드마크를 추출합니다.

랜드마크 그리기: draw_landmarks() 함수를 사용하여 프레임에 랜드마크를 그립니다.

각도 계산 및 표시: 각 관절의 각도를 계산하고, 이를 프레임에 표시합니다.

화면 출력: 결과를 화면에 표시하며, 'q' 키를 누르면 종료됩니다.


이상으로 모든 설명을 마칩니다. 감사합니다.

댓글

이 블로그의 인기 게시물

Qt Designer 설치하기

PyQt5 기반 동영상 플레이어앱 만들기