Python、OpenCV、コンピュータビジョンで跑步フォーム錯誤を検出
Python、OpenCV、MediaPipeを使用して自動跑步フォーム分析を構築する方法を学びます。このチュートリアルでは、ビデオ映像からオーバーストライディングや垂直振動などの一般的な問題を検出する方法を説明します。
このページの内容
経験豊富なマラソンマナーやランニングを始めたばかりの方も、適切なフォームはパフォーマンスと怪我の予防の両方に不可欠です。最も一般的で有害なランニングフォームのミスとして、**オーバーストライド(過剰な歩幅)と過剰な垂直振動(バウンス)**の2つが挙げられます。これらの問題はエネルギー消費的增加え、脛骨疲労骨折やランナーズニーなどの怪我のリスクを高める可能性があります。
伝統的に、ランニングフォームのフィードバックを得るには、コーチとの高額なセッションや専門的な歩行分析ラボでの検査が必要でした。しかし、自分のランニング姿をビデオで撮るだけで、わずかなPythonコードで価値のある洞察を得られたらどうでしょうか?
このチュートリアルでは、ビデオファイルからオーバーストライドと過剰な垂直振動を自動的に検出できるランニングフォーム解析ツールを構築します。ビデオ処理にOpenCVの力と、GoogleのMediaPipeライブラリを使った正確な姿勢推定を使用します。このプロジェクトは、コンピュータビジョンをフィットネスや健康技術に応用した素晴らしい例です。
前提条件:
- Pythonの基本的な理解
- 数値演算のためのNumPyへの精通
- システムにインストールされたPythonとpip
- 横から撮影したトレッドミルでのランニング動画
問題の理解
ランニングミスのバイオメカニクス
検出器を構築するために、まず何を検出する必要があるかを理解する必要があります。
1. オーバーストライド: これは、脚が体の重心よりもずっと前に着地し、膝が伸ばされた状態になる発生します。これにより、一歩ごとに 制動力が生まれ、速度が落ちて関節への衝撃が増します。オーバーストライドは、足が地面に着地した瞬間の脛骨角度を測定することで検出できます。脛骨が垂直に近い状態が理想的で、前方に大きな角度がある場合はオーバーストライドを示唆しています。
2. 過剰な垂直振動: これは一部のランナーが持つ「バウンス」動作です。ある程度の垂直方向の動きは必然ですが、多すぎる場合は前進ではなく上下方向へのエネルギー消費無駄になります。効率的なランナーの典型的な垂直振動は5〜10cmです。歩行周期全体でランナーの腰の垂直方向の動きを追跡することでこれを測定できます。
前提条件
Python環境を設定しましょう。プロジェクトの依存関係を管理するために、仮想環境を使用するのが良い練習です。
# 仮想環境の作成
python -m venv running_analyzer_env
# 仮想環境の有効化
# Windowsの場合:
running_analyzer_env\Scripts\activate
# macOS/Linuxの場合:
source running_analyzer_env/bin/activate
# 必要なライブラリのインストール
pip install opencv-python mediapipe numpy
これらのパッケージごとに正常なインストールメッセージが表示されます。
ステップ1:姿勢推定パイプラインの設定
何をやるか
まず、ビデオをフレームごとに処理し、MediaPipeを使用してランナーの体のランドマークを検出する必要があります。MediaPipeのPoseモデルは全身に33のキーポイントを提供しており、私たちの分析には十分な数です。
実装
新しいPythonファイル(例:running_analyzer.py)を作成し、必要なライブラリをインポートしてMediaPipe Poseモデルを初期化しましょう。
# running_analyzer.py
import cv2
import mediapipe as mp
import numpy as np
# MediaPipe Poseの初期化
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
def process_video(video_path):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print("エラー:ビデオを開けませんでした。")
return
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# BGR画像をRGBに変換
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image.flags.writeable = False
# 検出の実行
results = pose.process(image)
# BGRに戻してカラーリング
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
# 検出結果のレンダリング
if results.pose_landmarks:
mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
cv2.imshow('ランニングフォーム解析', image)
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
# 'running_video.mp4'をお客様のビデオファイルのパスに置き換えてください
process_video('running_video.mp4')
動作の仕組み
- ビデオ処理に
cv2、姿勢推定にmediapipe、計算にnumpyをインポートします。 - 体のランドマークを検出するための機械学習モデルを含む
mp.solutions.pose.Poseクラスを初期化します。 process_video関数はビデオファイルを開き、フレームごとに読み取ります。- 各フレームで、OpenCVのデフォルトであるBGRからMediaPipeが期待するRGBに変換します。
pose.process(image)でフレームに対して姿勢推定モデルを実行します。- 結果には検出されたランドマークが含まれ、
mp_drawing.draw_landmarksを使用して可視化のためにフレームに描画します。 - 最後に、処理されたフレームを表示します。
一般的な落とし穴
- ビデオパス: ビデオファイルへのパスが正しいことを確認してください。
- カメラアングル: この分析を正確に行うには、ビデオはランナーの明確な横からの視点で撮影されている必要があります。
ステップ2:関節角度の計算とオーバーストライドの検出
何をやるか
オーバーストライドを検出するためのコアロジックを作成します。以下のことを行う必要があります:
- 「足着地」の瞬間を特定する — ランナーの足が最初に地面に触れたとき。
- その瞬間に、脛骨(膝から足首までの線)の垂直線に対する角度を計算する。
実装
角度を計算するヘルパー関数を作成し、メインのビデオ処理ループに統合しましょう。
# この関数をrunning_analyzer.pyに追加する
def calculate_angle(a, b, c):
"""3点間の角度を計算する"""
a = np.array(a) # 最初の点
b = np.array(b) # 中間の点
c = np.array(c) # 終点の点
radians = np.arctan2(c[1] - b[1], c[0] - b[0]) - np.arctan2(a[1] - b[1], a[0] - b[0])
angle = np.abs(radians * 180.0 / np.pi)
if angle > 180.0:
angle = 360 - angle
return angle
# ... (残りのコード)
# process_video関数の'while'ループ内で、'results'を取得した後の処理:
# try:
# landmarks = results.pose_landmarks.landmark
#
# # 左側の体の座標を取得
# left_hip = [landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].x, landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].y]
# left_knee = [landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].x, landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value].y]
# left_ankle = [landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].x, landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value].y]
#
# # シンプルな足着地検出(足首が最も低い点に達した時)
# # 多様な地形にはより堅牢な方法が必要
# if 'prev_left_ankle_y' not in locals():
# prev_left_ankle_y = left_ankle[1]
#
# if prev_left_ankle_y < left_ankle[1]: # 足首が下降している場合
# # 足着地が発生しているか、間もなく発生しようとしている
# vertical_point = [left_knee[0], left_knee[1] + 0.1] # 膝の真下の点
# shin_angle = calculate_angle(left_ankle, left_knee, vertical_point)
#
# # 脛骨角度を表示し、オーバーストライドを確認
# cv2.putText(image, f"脛骨角度: {int(shin_angle)}",
# (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
#
# if shin_angle > 15: # オーバーストライドの閾値
# cv2.putText(image, "オーバーストライド検出",
# (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
#
# prev_left_ankle_y = left_ankle[1]
#
# except:
# pass
注記: 簡略化のため、上記のコードスニペットはロジックを示しています。このコードをprocess_video関数のwhileループ内に統合し、どの足が前か.handleする必要があります。
動作の仕組み
calculate_angle関数は3つの点を取り座標の逆正接を使用して中間の点での角度を見つけます。- MediaPipeのランドマークから腰、膝、足首のx、y座標を抽出します。
- トレッドミルでの足着地検出には、歩行周期で足首が垂直方向に最も低い点に達した時を見つけるというシンプルな経験則を使用します。
- 垂直に対する脛骨角度を計算するために、膝の真下に点を作成し、足首、膝、この垂直点間の角度を計算します。
- この角度を画面に表示します。特定の閾値(例:ヒールストライカーでは15度)を超えている場合は、オーバーストライドとしてフラグを立てます。
一般的な落とし穴
- 座標系: OpenCVではy軸が下向きに増加することを忘れないでください。
- 足着地検出: このシンプルな方法はトレッドミル走りに最適です。屋外でのランニングでは、地面に対して脚が止まった時を検出するためにより高度なアルゴリズムが必要な場合があります。
ステップ3:垂直振動の測定
何をやるか
垂直振動を測定するために、腰の垂直位置を経時的に追跡します。バウンス量を決定するために、歩行周期中の腰の最高点と最低点を見つける必要があります。
実装
フレーム全体で腰の垂直位置を追跡する必要があります。
# process_videoの'while'ループの前にこれらの変数を追加
hip_y_positions = []
vertical_oscillation = 0
# 'while'ループ内の'try'ブロック内で:
# # ... (ランドマーク座標の取得後)
#
# left_hip_y = landmarks[mp_pose.PoseLandmark.LEFT_HIP.value].y
# hip_y_positions.append(left_hip_y)
#
# # フレームのウィンドウを分析(例:30フレーム)
# if len(hip_y_positions) > 30:
# max_hip_y = max(hip_y_positions)
# min_hip_y = min(hip_y_positions)
#
# # 正規化された座標をピクセルに変換して意味のある測定値を取得
# frame_height, frame_width, _ = image.shape
# vertical_oscillation_pixels = (max_hip_y - min_hip_y) * frame_height
#
# # ピクセルをcmに変換するには、フレームに известных размеров のオブジェクトが必要です
# # 現時点では、ピクセル値を表示するだけです
# cv2.putText(image, f"垂直振動(ピクセル): {int(vertical_oscillation_pixels)}",
# (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
#
# hip_y_positions.pop(0) # リストサイズを一定に保つ
動作の仕組み
- 各フレームで腰の垂直(
y)座標をリストに保存します。 - スライディングウィンドウアプローチを使用して、過去30フレームでの最大(画面上で最も低い点)と最小(画面上で最も高い点)のy値を見つけます。
- 正規化された座標での垂直振動は、これらの値の差になります。
- 実世界での測定値(cm単位)を得るには、ビデオフレームに 既知のサイズのオブジェクトを 配置してピクセルからcmへの比率を確立することで、システム較正する必要があります。
すべてをまとめる
より完全なコード構造を以下に示します:
import cv2
import mediapipe as mp
import numpy as np
# ... (calculate_angle関数ここに) ...
def analyze_running_form(video_path):
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
cap = cv2.VideoCapture(video_path)
hip_y_positions = []
prev_ankle_y = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
image.flags.writeable = False
results = pose.process(image)
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
try:
landmarks = results.pose_landmarks.landmark
# カメラに対してよりよく見える側を使用
hip = landmarks[mp_pose.PoseLandmark.LEFT_HIP.value]
knee = landmarks[mp_pose.PoseLandmark.LEFT_KNEE.value]
ankle = landmarks[mp_pose.PoseLandmark.LEFT_ANKLE.value]
# --- オーバーストライド検出 ---
if prev_ankle_y > 0 and ankle.y > prev_ankle_y: # シンプルな足着地検出
vertical_point = [knee.x, knee.y + 0.1]
shin_angle = calculate_angle([ankle.x, ankle.y], [knee.x, knee.y], vertical_point)
if shin_angle > 15:
cv2.putText(image, "オーバーストライド", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
prev_ankle_y = ankle.y
# --- 垂直振動 ---
hip_y_positions.append(hip.y)
if len(hip_y_positions) > 30: # 30フレームのスライディングウィンドウ
min_y = min(hip_y_positions)
max_y = max(hip_y_positions)
oscillation_normalized = max_y - min_y
# cmに変換するには較正オブジェクトが必要
# oscillation_cm = oscillation_normalized * frame_height_in_pixels * (real_object_height_cm / object_height_in_pixels)
if oscillation_normalized * 1000 > 60: # 閾値の例
cv2.putText(image, "過剰なバウンス", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
hip_y_positions.pop(0)
except Exception as e:
# print(e) # デバッグ用
pass
mp_drawing.draw_landmarks(image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
cv2.imshow('ランニングフォーム解析ツール', image)
if cv2.waitKey(5) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
analyze_running_form('running_video.mp4')
本番環境でのデプロイのヒント
- 較正: 正確な実世界測定値(ピクセルではなくcm)を得るには、カメラ較正を行う必要があります。これには、トレッドミルの横に既知の高さ(例:锥体)のオブジェクトを 配置することが含まれる場合があります。
- パフォーマンス: MediaPipeは非常に効率的ですが、低電力デバイスでのリアルタイム分析では、より軽量なモデルを使用したり、ビデオ解像度を下げたりすることを検討してください。
- ユーザーインターフェース: ユーザーフレンドリーなアプリケーションにするには、FlaskやStreamlitを使用して簡単なWebインターフェースを構築し、ユーザーがビデオをアップロードして結果を確認できるようにすることを検討してください。
結論
ランナーに価値のある生体力学的フィードバックを提供できるツールを正常に構築しました。OpenCVとMediaPipeを使用することで、シンプルなビデオ再生を超えて定量的分析の領域に入ることができました。このプロジェクトは、健康とフィットネスの分野におけるコンピュータビジョンの力を実証しており、より高度なスポーツ分析プロジェクトの出発点として最適です。
次のステップ
- より堅牢な足着地検出アルゴリズムを実装する。
- ケイデンス(每分步数)などの他のランニング指標の分析を追加する。
- 進捗を監視するためにフォームの変化を経時的に追跡するシステムを作成する。
参考資料
- OpenCVドキュメント: https://docs.opencv.org/
クイックリファレンス
| 側面 | 重要な情報 |
|---|---|
| トピック | 開発 |
| 関連性 | 患者教育と気づき |
| アクション | 医療提供者との相談 |
| 頻度 | ケアチームのおすすめに従う |
| エビデンス | 現在の臨床ガイドラインに基づく |
よくある質問
Q: これが自分に該当するどうかはどうやってわかりますか? A: 個人的な病歴と家族歴を医療提供者と相談し、個人のリスク因子を評価し、適切な次のステップを決定してください。
Q: 医師に何と言えばいいですか? A: 良い質問例:「私の結果はどういう意味ですか?」「私のオプションは何ですか?」「どんなライフスタイルの変更が役に立ちますか?」「いつフォローアップすべきですか?」
Q: помощ有所帮助するライフスタイルの変更はありますか? A: ほとんどの健康状態は、基本的な対策から恩恵を受けます:バランスの取れた栄養、定期的な運動(週150分以上)、質の良い睡眠(7〜9時間)、ストレス管理禁煙です。
医療免責事項
この記事は教育目的のみを目的としており、医療アドバイスを提供ものではありません。あなたの健康、治療、またはケアに関する決定を行う前は、常に資格のある医療提供者と相談してください。個人の状況により、一人にとって適切なことが別の人には適切でない場合があります。
緊急医療情况が発生した場合は、直ちに119番(またはお住まいの地域の緊急電話番号)に連絡してください。