pythonとmediapipeを用いて座った姿勢での腰の角度を取得できるプログラムを作成しました。
現在、開発中なので、詳細な説明は省いておりますのでご了承ください。
今後は腰の角度をデータベースに格納し、機械学習を用いて(おそらくscikit-learn)
座った姿勢が良い姿勢か悪い姿勢かを判定していきます。
以下がソースコードになります
qiita.py
import cv2
import os
import numpy as np
import mediapipe as mp
import math
import matplotlib.pyplot as plt
#体の部位の座標(x,y,z)と部位の可視性(v)を取得
def get_coordinate(landmark_num):
parts_array = {"x":0, "y":0, "z":0,"v":0}
parts_array["x"] = results.pose_landmarks.landmark[landmark_num].x
parts_array["y"] = results.pose_landmarks.landmark[landmark_num].y
parts_array["z"] = results.pose_landmarks.landmark[landmark_num].z
parts_array["v"] = results.pose_landmarks.landmark[landmark_num].visibility
return parts_array
#腰を中心とした、肩-腰-膝の角度を取得
def get_angle(array_hip,array_shoulder,array_knee):
x0,y0,z0=array_hip["x"],array_hip["y"],array_hip["z"]
x1,y1,z1=array_shoulder["x"],array_shoulder["y"],array_shoulder["z"]
x2,y2,z2=array_knee["x"],array_knee["y"],array_knee["z"]
##腰の角度の算出
vec1=[x1-x0,y1-y0,z1-z0]
vec2=[x2-x0,y2-y0,z2-z0]
absvec1=np.linalg.norm(vec1)
absvec2=np.linalg.norm(vec2)
inner=np.inner(vec1,vec2)
cos_theta=inner/(absvec1*absvec2)
theta=math.degrees(math.acos(cos_theta))
return theta
#部位の座標を表示
def show_coordinate(L_shoulder,L_hip,L_knee,R_shoulder,R_hip,R_knee):
x = [L_shoulder["x"],L_hip["x"],L_knee["x"],R_shoulder["x"],R_hip["x"],R_knee["x"]]
y = [L_shoulder["y"],L_hip["y"],L_knee["y"],R_shoulder["y"],R_hip["y"],R_knee["y"]]
z = [L_shoulder["z"],L_hip["z"],L_knee["z"],R_shoulder["z"],R_hip["z"],R_knee["z"]]
fig = plt.figure(figsize = (6, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x[0], y[0], z[0], color='blue', label='L_shoulder')
ax.scatter(x[1], y[1], z[1], color='red', label='L_hip')
ax.scatter(x[2], y[2], z[2], color='green', label='L_knee')
ax.scatter(x[3], y[3], z[3], color='navy', label='R_shoulder')
ax.scatter(x[4], y[4], z[4], color='firebrick', label='R_hip')
ax.scatter(x[5], y[5], z[5], color='darkgreen', label='R_knee')
ax.set_title('show')
ax.set_xlabel('X-label')
ax.set_ylabel('Y-label')
ax.set_zlabel('Z-label')
ax.legend()
plt.show()
return 1
##main##
#ファイルの存在チェック
print(os.path.exists('画像パス'))
# 初期設定
mp_holistic = mp.solutions.holistic
holistic = mp_holistic.Holistic(
static_image_mode=True,
min_detection_confidence=0.5)
#ファイルの読み込み
image = cv2.imread('画像パス')
results = holistic.process(
cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
#左側の部位ごとの座標の取得
L_shoulder = get_coordinate(11)
L_hip = get_coordinate(23)
L_knee = get_coordinate(25)
print(f"L_shoulder angle:{L_shoulder}")
print(f"L_hip angle:{L_hip}")
print(f"L_knee angle:{L_knee}")
#右側の部位ごとの座標の取得
R_shoulder = get_coordinate(12)
R_hip = get_coordinate(24)
R_knee = get_coordinate(26)
print(f"R_shoulder angle:{R_shoulder}")
print(f"R_hip angle:{R_hip}")
print(f"R_knee angle:{R_knee}")
#それぞれの部位の可視性を閾値(0.8)と比較
if (L_shoulder["v"] > 0.8) and (L_hip["v"] > 0.8) and (L_knee["v"] > 0.8):
print("2)success >> get left side parts angle")
if (R_shoulder["v"] > 0.8) and (R_hip["v"] > 0.8) and (R_knee["v"] > 0.8):
print("3)success >> get right side parts angle")
R_angle = get_angle(R_hip,R_shoulder,R_knee)
print(f"R_shoulder_hip_knee angle:{R_angle}")
L_angle = get_angle(L_hip,L_shoulder,L_knee)
print(f"L_shoulder_hip_knee angle:{L_angle}")
#腰の角度
hip_angle = (L_angle + R_angle)/2
print(f"body_hip_angle:{hip_angle}")
else:
print("2)error >> can not get right side parts angle")
#腰の角度
hip_angle = get_angle(L_hip,L_shoulder,L_knee)
print(f"L_shoulder_hip_knee angle:{hip_angle}")
else:
print("2)error >> can not get left side parts angle")
if (R_shoulder["v"] > 0.8) and (R_hip["v"] > 0.8) and (R_knee["v"] > 0.8):
print("3)success >> get right side parts angle")
#腰の角度
hip_angle = get_angle(R_hip,R_shoulder,R_knee)
print(f"R_shoulder_hip_knee angle:{hip_angle}")
else:
print("3)error >> can not get both side parts angle")
#座標の表示
show = 1
if show == 1:
show_plot = show_coordinate(L_shoulder,L_hip,L_knee,R_shoulder,R_hip,R_knee)
print(show_plot)
print("終了")
肩、腰、膝のランドマークはMediaPipe Pose の公式ページを参考にしました。
それぞれの座標と腰の角度の出力結果です。
L_shoulder angle:{'x': 0.289949506521225, 'y': 0.3384971618652344, 'z': 0.3545862138271332, 'v': 0.9986357092857361}
L_hip angle:{'x': 0.3544599115848541, 'y': 0.6008540987968445, 'z': 0.2585517466068268, 'v': 0.9985164999961853}
L_knee angle:{'x': 0.5737991333007812, 'y': 0.4497544765472412, 'z': 0.3909236490726471, 'v': 0.1409769505262375}
R_shoulder angle:{'x': 0.28653785586357117, 'y': 0.3461516797542572, 'z': -0.4852321743965149, 'v': 0.9999244213104248}
R_hip angle:{'x': 0.3632543981075287, 'y': 0.6229157447814941, 'z': -0.2586084306240082, 'v': 0.9991724491119385}
R_knee angle:{'x': 0.5856761932373047, 'y': 0.4437987804412842, 'z': -0.24518434703350067, 'v': 0.9349005222320557}
2)error >> can not get left side parts angle
3)success >> get right side parts angle
R_shoulder_hip_knee angle:73.63609479172645
アドバイス、感想等お待ちしております。