7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】骨格推定の概要、様々な骨格推定パターンの実装

Posted at

はじめに

骨格推定(Pose Estimation)は、画像や動画から人物の関節位置を特定する技術で、スポーツ分析やリハビリテーション、人間の動作認識、AR(拡張現実)など、さまざまな分野で利用されています。リアルタイムで骨格推定を行うためには、高速で効率的な推定手法が求められます。

本記事では、代表的な骨格推定ライブラリであるMediaPipeOpenPoseを比較し、その後、MediaPipeを使った骨格推定の実装方法を紹介します。

骨格推定の実装方法

骨格推定を実現するためのライブラリにはいくつかの選択肢がありますが、MediaPipeOpenPoseが代表的です。両者の特徴を比較してみましょう。

MediaPipe vs OpenPose 比較表

特徴 MediaPipe OpenPose
提供元 Google Carnegie Mellon University (CMU)
使用用途 リアルタイムアプリ向け(モバイル、Web) 高精度な学術・研究用途に向けた大型モデル
処理速度 高速、モバイル端末でも実行可能 処理速度はやや遅くなる(高解像度で重くなる)
精度 中程度(リアルタイム重視) 高精度(研究用途に適した精度)
インストールの簡便さ 非常に簡単(pipでインストール可能) 複雑、CMakeを使って手動でビルドが必要
依存ライブラリ 少ない 多くの依存関係(Caffe、CUDA、OpenCVなど)
プラットフォーム Android、iOS、Windows、Linux、Web Windows、Linux(MacOSは公式サポートなし)
リアルタイム処理の適性 ◎(非常に高速、モバイル端末でも動作) △(精度重視だが遅くなることがある)
モデルのサイズ 小さめ(モデルは軽量) 大きめ(モデルが重い)

MediaPipeは、軽量でリアルタイム処理を重視しており、特にモバイルやWebアプリケーションに適しています。一方で、OpenPoseは高精度な推定が可能ですが、実行速度がやや遅く、特に高解像度のデータで処理が重くなることがあります。一般的に、精度重視のシナリオではOpenPoseが、速度重視のシナリオではMediaPipeが選ばれます。

骨格推定のパターン一覧

mediapipeには、以下のような骨格推定のパターンがあります。これらは主に「姿勢推定」「顔」「手」「足」など、用途に応じたモジュールで提供されます。

パターン名 説明
全身 (Pose) 人体全体の骨格を推定 (33個のランドマーク)
手 (Hands) 手の骨格を推定 (21個のランドマーク)
顔 (Face Mesh) 顔のランドマークを推定 (468個のランドマーク)
ポーズと手の複合 ポーズ推定と手の推定を同時に行う
ポーズと顔の複合 ポーズ推定と顔の推定を同時に行う

環境の準備

まず、骨格推定を行うために必要なライブラリのインストールを行います。mediapipeをインストールするだけで利用できます。非常にお手軽です。

pip3 install mediapipe

骨格推定を実装

◆ 基本的な実装

MediaPipeを使った骨格推定の実装方法を説明します。まずは、全身 (Pose)の骨格推定のみを実装してみます。

import cv2
import mediapipe as mp

# MediaPipeのポーズ推定モジュールをインスタンス化
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5)

# Webカメラを開く
cap = cv2.VideoCapture(0)

# カメラが開けない場合のエラーハンドリング
if not cap.isOpened():
    print("Error: Camera not found.")
    exit()

# 画像処理ループ
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        print("Error: Failed to grab frame.")
        break

    # BGRからRGBに変換(MediaPipeはRGB形式を期待するため)
    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # MediaPipeで骨格推定を実行
    results = pose.process(rgb_frame)

    # 結果を描画
    if results.pose_landmarks:
        # 骨格のランドマークを描画
        mp.solutions.drawing_utils.draw_landmarks(
            frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)

    # 画像を表示
    cv2.imshow("Pose Estimation", frame)

    # 'q'を押すと終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 終了処理
cap.release()
cv2.destroyAllWindows()

コード解説

  1. mediapipeの設定
    mp.solutions.pose.Pose()を使用して、骨格推定モデルを設定します。min_detection_confidencemin_tracking_confidenceは、検出と追跡の精度を調整するパラメータです。

  2. Webカメラキャプチャ
    cv2.VideoCapture(0)を使ってWebカメラから映像を取得します。cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)で、BGRからRGBに変換します。

  3. 骨格推定の実行
    pose.process(rgb_frame)で、MediaPipeによる骨格推定を実行し、得られた結果を画面に表示します。

  4. 結果の描画
    mp.solutions.drawing_utils.draw_landmarks()で、推定された骨格のランドマークを描画します。

◆ 様々な骨格推定パターンの実装

次に、全身 (Pose)だけでなく、様々な骨格推定パターンを実装し、コマンドライン引数で切り替えられる実装方法を示します。

import cv2
import mediapipe as mp
import argparse

# 引数のパース
def parse_arguments():
    parser = argparse.ArgumentParser(description="mediapipeで骨格推定を実行")
    parser.add_argument('--pattern', type=str, default='pose', choices=['pose', 'hands', 'face', 'pose_hands', 'pose_face'],
                        help="使用する骨格推定のパターンを指定します。")
    return parser.parse_args()

def run_pose():
    mp_pose = mp.solutions.pose
    pose = mp_pose.Pose()
    return pose, mp_pose

def run_hands():
    mp_hands = mp.solutions.hands
    hands = mp_hands.Hands()
    return hands, mp_hands

def run_face_mesh():
    mp_face_mesh = mp.solutions.face_mesh
    face_mesh = mp_face_mesh.FaceMesh()
    return face_mesh, mp_face_mesh

def main():
    args = parse_arguments()
    
    # 動画の読み込み (カメラを使用)
    cap = cv2.VideoCapture(0)

    # 使用する推定モデルを選択
    if args.pattern == 'pose':
        pose, mp_pose = run_pose()
    elif args.pattern == 'hands':
        hands, mp_hands = run_hands()
    elif args.pattern == 'face':
        face_mesh, mp_face_mesh = run_face_mesh()
    elif args.pattern == 'pose_hands':
        pose, mp_pose = run_pose()
        hands, mp_hands = run_hands()
    elif args.pattern == 'pose_face':
        pose, mp_pose = run_pose()
        face_mesh, mp_face_mesh = run_face_mesh()

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

        # 処理を行うための画像の前処理
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = None

        if args.pattern == 'pose':
            results = pose.process(frame_rgb)
            mp.solutions.drawing_utils.draw_landmarks(
                image=frame,
                landmark_list=results.pose_landmarks,
                connections=mp_pose.POSE_CONNECTIONS)
            cv2.imshow('MediaPipe Pose', frame)

        elif args.pattern == 'hands':
            results = hands.process(frame_rgb)
            if results.multi_hand_landmarks:
                for hand_landmarks in results.multi_hand_landmarks:
                    mp.solutions.drawing_utils.draw_landmarks(
                        image=frame,
                        landmark_list=hand_landmarks,
                        connections=mp_hands.HAND_CONNECTIONS,
                        landmark_drawing_spec=mp.solutions.drawing_styles.get_default_hand_landmarks_style(),
                        connection_drawing_spec=mp.solutions.drawing_styles.get_default_hand_connections_style()
                    )
            cv2.imshow("Hand Estimation", frame)

        elif args.pattern == 'face':
            results = face_mesh.process(frame_rgb)
            if results.multi_face_landmarks:
                for face_landmarks in results.multi_face_landmarks:
                    mp.solutions.drawing_utils.draw_landmarks(
                        image=frame,
                        landmark_list=face_landmarks,
                        connections=mp_face_mesh.FACEMESH_CONTOURS,
                        landmark_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(thickness=1, circle_radius=1),
                        connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(thickness=1, circle_radius=1),
                    )
                cv2.imshow('Face Estimation', frame)                

        elif args.pattern == 'pose_hands':
            # 両方のモデルを処理
            results_pose = pose.process(frame_rgb)
            mp.solutions.drawing_utils.draw_landmarks(
                image=frame,
                landmark_list=results_pose.pose_landmarks,
                connections=mp_pose.POSE_CONNECTIONS)
            cv2.imshow('MediaPipe Pose', frame)

            results_hands = hands.process(frame_rgb)
            if results_hands.multi_hand_landmarks:
                for hand_landmarks in results_hands.multi_hand_landmarks:
                    mp.solutions.drawing_utils.draw_landmarks(
                        image=frame,
                        landmark_list=hand_landmarks,
                        connections=mp_hands.HAND_CONNECTIONS,
                        landmark_drawing_spec=mp.solutions.drawing_styles.get_default_hand_landmarks_style(),
                        connection_drawing_spec=mp.solutions.drawing_styles.get_default_hand_connections_style()
                    )
            cv2.imshow("Hand Estimation", frame)

        elif args.pattern == 'pose_face':
            # 両方のモデルを処理
            results_pose = pose.process(frame_rgb)
            mp.solutions.drawing_utils.draw_landmarks(
                image=frame,
                landmark_list=results_pose.pose_landmarks,
                connections=mp_pose.POSE_CONNECTIONS)
            cv2.imshow('MediaPipe Pose', frame) 

            results_face = face_mesh.process(frame_rgb)
            if results_face.multi_face_landmarks:
                for face_landmarks in results_face.multi_face_landmarks:
                    mp.solutions.drawing_utils.draw_landmarks(
                        image=frame,
                        landmark_list=face_landmarks,
                        connections=mp_face_mesh.FACEMESH_CONTOURS,
                        landmark_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(thickness=1, circle_radius=1),
                        connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(thickness=1, circle_radius=1),
                    )
                cv2.imshow('Face Estimation', frame)

        # 結果を表示
        cv2.imshow("Skeleton Estimation", frame)

        # 'q'キーで終了
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

実行例

コマンドラインで以下の通り実行することで、pose(全身)推定が実行されます。他のパターンも同様に実行できます。

python3 main.py --pattern pose

コマンドラインの使い方(ヘルプ)は以下のように表示します。

python3 main.py --help

# 出力結果
------------------------------------------------------------------------
usage: main.py [-h] [--pattern {pose,hands,face,pose_hands,pose_face}]

mediapipeで骨格推定を実行

options:
  -h, --help            show this help message and exit
  --pattern {pose,hands,face,pose_hands,pose_face}
                        使用する骨格推定のパターンを指定します。
------------------------------------------------------------------------

おわりに

本記事では、骨格推定を行うための代表的なライブラリであるMediaPipeOpenPoseを比較し、MediaPipeを使った実装方法を紹介しました。骨格推定は、様々な用途に応用できる技術であり、AR/VRの分野などでも活躍できる技術ですので、ぜひ勉強してみて下さい。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?