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' 키를 누르면 종료됩니다.
이상으로 모든 설명을 마칩니다. 감사합니다.
댓글
댓글 쓰기