YOLOv11による2次元姿勢推定

【概要】YOLO11-poseを使用して、カメラから人体17箇所のキーポイント(鼻、目、耳、肩、肘、手首、腰、膝、足首)をリアルタイム検出。Nano・Small・Medium・Large・Extra-Largeの5種類のモデルサイズで精度と処理速度のトレードオフを比較実験可能。Windows環境での実行手順、プログラムコード、実験アイデアを含む。

目次

概要

技術名: YOLOv11-pose(You Only Look Once version 11 - Pose Estimation)

発表: Ultralytics YOLO Vision 2024 (YV24) にて発表、2024年リリース

新規性・特徴: YOLOv11-poseは、改良されたバックボーンとネック設計により、YOLOv8と比較して22%少ないパラメータで高い精度を実現する姿勢推定技術である。人体の17箇所のキーポイント(関節位置)をリアルタイムで検出し、トップダウン方式とボトムアップ方式の両方の利点を組み合わせた単一ステップ処理を採用している。エッジデバイスからクラウドプラットフォームまで幅広い環境で動作可能である。

技術革新:

アプリケーション例: スポーツ動作解析、リハビリテーション支援、フィットネスアプリ、モーションキャプチャ、姿勢矯正システム、建設現場安全監視、動物行動分析

体験価値: カメラの前で様々なポーズを取ることで、最新のAIが人体の関節位置を瞬時に認識する過程を視覚的に体験できる。異なるモデルサイズによる精度と速度のトレードオフを実験的に比較し、最新AI技術の性能特性を理解することが可能である。

事前準備

Python, Windsurfをインストールしていない場合の手順(インストール済みの場合は実行不要)。

  1. 管理者権限でコマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)。
  2. 以下のコマンドをそれぞれ実行する(winget コマンドは1つずつ実行)。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Windsurf をシステム領域にインストール
winget install --scope machine --id Codeium.Windsurf -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
REM Windsurf のパス設定
set "WINDSURF_PATH=C:\Program Files\Windsurf"
if exist "%WINDSURF_PATH%" (
    echo "%PATH%" | find /i "%WINDSURF_PATH%" >nul
    if errorlevel 1 setx PATH "%PATH%;%WINDSURF_PATH%" /M >nul
)

必要なパッケージのインストール

管理者権限でコマンドプロンプトを起動し、以下のコマンドを実行する:


pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install ultralytics opencv-python

プログラムコード


# プログラム名: YOLOv11姿勢推定プログラム
# 特徴技術名: YOLOv11 (You Only Look Once version 11)
# 出典: Ultralytics. (2024). YOLOv11: Enhanced Feature Extraction and Optimized Efficiency. https://github.com/ultralytics/ultralytics
# 特徴機能: Enhanced Feature Extraction with C3k2 blocks - YOLOv8のC2fブロックを改良したC3k2ブロックにより、計算効率を維持しながら特徴抽出能力を向上。22%少ないパラメータで高精度を実現
# 学習済みモデル: yolo11n-pose.pt(COCOデータセット17キーポイント学習済み、2.9Mパラメータ、最速処理)URL: Ultralyticsライブラリが自動ダウンロード
# 方式設計:
#   - 関連利用技術: OpenCV(画像処理・表示)、NumPy(数値計算)
#   - 入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://github.com/opencv/opencv/blob/master/samples/data/vtest.aviを使用)、出力: OpenCV画面でリアルタイム表示(検出された姿勢とキーポイント座標)、1秒間隔でprint()による処理結果表示、終了時にresult.txtファイルへ保存
#   - 処理手順: 1.YOLOv11モデルロード、2.動画フレーム取得、3.C3k2ブロックによる特徴抽出、4.姿勢推定ヘッドで17キーポイント検出、5.信頼度閾値によるフィルタリング、6.結果の可視化
#   - 前処理、後処理: 前処理:DirectShowバックエンドによる低遅延フレーム取得、バッファサイズ1設定。後処理:信頼度0.5以上のキーポイントのみ表示、キーポイント名と座標のテキスト描画
#   - 追加処理: grab()とretrieve()の分離呼び出しによる最新フレーム取得(遅延防止効果)
#   - 調整を必要とする設定値: CONF_THRESHOLD(キーポイント検出の信頼度閾値、デフォルト0.5、低くすると検出数増加but誤検出も増加)
# 将来方策: 検出されたキーポイント数の時系列変化を監視し、安定して検出されるキーポイントの割合からCONF_THRESHOLDを動的に調整する機能の実装
# その他の重要事項: Windows環境専用(cv2.CAP_DSHOW使用)、COCO形式17キーポイント(鼻、目、耳、肩、肘、手首、腰、膝、足首)
# 前準備: pip install ultralytics opencv-python

import cv2
import numpy as np
from ultralytics import YOLO
import tkinter as tk
from tkinter import filedialog
import os
import time
import urllib.request

# 定数定義
MODEL_NAME = 'yolo11n-pose.pt'
CONF_THRESHOLD = 0.5  # キーポイント検出の信頼度閾値(0.0-1.0)

# 人体17箇所キーポイント名称(COCO形式)
KEYPOINT_NAMES = [
    'nose', 'left_eye', 'right_eye', 'left_ear', 'right_ear',
    'left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow',
    'left_wrist', 'right_wrist', 'left_hip', 'right_hip',
    'left_knee', 'right_knee', 'left_ankle', 'right_ankle'
]

# プログラム開始時の説明
print('=== YOLOv11姿勢推定プログラム ===')
print('概要: YOLOv11を使用してリアルタイムで人体の17箇所のキーポイントを検出します')
print('検出部位: 鼻、目(左右)、耳(左右)、肩(左右)、肘(左右)、手首(左右)、腰(左右)、膝(左右)、足首(左右)')
print("操作方法: 'q'キーで終了")
print('')

# 乱数シード設定(再現性確保)
np.random.seed(42)

# YOLOv11モデルのロード
print('YOLOv11モデルをロード中...')
model = YOLO(MODEL_NAME)
print(f"モデル '{MODEL_NAME}' のロードが完了しました")
print('')

# 結果記録用リスト
results_log = []
last_print_time = time.time()
frame_count = 0
fps_start_time = time.time()


def video_processing(frame):
    global last_print_time, results_log, frame_count, fps_start_time

    # FPS計算
    frame_count += 1
    current_time = time.time()

    # YOLOv11による姿勢推定実行
    results = model(frame)
    annotated_frame = results[0].plot()

    # FPS表示
    fps = frame_count / (current_time - fps_start_time)
    cv2.putText(annotated_frame, f'FPS: {fps:.1f}', (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)

    # キーポイント処理(複数人物対応)
    detected_keypoints = []
    person_count = 0

    for result in results:
        if hasattr(result, 'keypoints') and result.keypoints is not None:
            keypoints = result.keypoints

            if hasattr(keypoints.data, 'cpu'):
                keypoints_data = keypoints.data.cpu().numpy()
            else:
                keypoints_data = keypoints.data

            if len(keypoints_data.shape) == 3 and keypoints_data.shape[1] == 17:
                # 全人物を処理
                for person_idx in range(keypoints_data.shape[0]):
                    person_count += 1
                    person_keypoints = []

                    for i in range(17):
                        keypoint = keypoints_data[person_idx, i]
                        if len(keypoint) >= 3:
                            x, y, conf = keypoint[:3]
                            if conf > CONF_THRESHOLD:
                                # 人物番号を含めて表示
                                text = f'P{person_idx}:{KEYPOINT_NAMES[i]}({x:.0f},{y:.0f})'
                                cv2.putText(annotated_frame, text, (int(x), int(y)),
                                           cv2.FONT_HERSHEY_SIMPLEX, 0.4,
                                           (255, 255, 255), 1)
                                person_keypoints.append(f'Person{person_idx}-{KEYPOINT_NAMES[i]}:({x:.0f},{y:.0f},conf:{conf:.2f})')

                    if person_keypoints:
                        detected_keypoints.extend(person_keypoints)

    # 1秒間隔での出力
    if current_time - last_print_time >= 1.0:
        if detected_keypoints:
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
            log_entry = f'[{timestamp}] 検出人数: {person_count}, 検出キーポイント数: {len(detected_keypoints)}, FPS: {fps:.1f}'
            print(log_entry)
            for kp in detected_keypoints[:5]:  # 最初の5個のみ表示
                print(f'  {kp}')
            if len(detected_keypoints) > 5:
                print(f'  ... 他 {len(detected_keypoints) - 5} 個のキーポイント')
            results_log.append(log_entry)
            results_log.extend([f'  {kp}' for kp in detected_keypoints])
        else:
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
            log_entry = f'[{timestamp}] キーポイントが検出されませんでした, FPS: {fps:.1f}'
            print(log_entry)
            results_log.append(log_entry)
        last_print_time = current_time

    return annotated_frame


print('0: 動画ファイル')
print('1: カメラ')
print('2: サンプル動画')

choice = input('選択: ')
temp_file = None

if choice == '0':
    root = tk.Tk()
    root.withdraw()
    path = filedialog.askopenfilename()
    if not path:
        exit()
    cap = cv2.VideoCapture(path)
elif choice == '1':
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
elif choice == '2':
    # サンプル動画ダウンロード・処理
    url = 'https://github.com/opencv/opencv/raw/master/samples/data/vtest.avi'
    filename = 'vtest.avi'
    try:
        print(f'サンプル動画をダウンロード中: {url}')
        urllib.request.urlretrieve(url, filename)
        temp_file = filename
        cap = cv2.VideoCapture(filename)
        print('サンプル動画のダウンロードが完了しました')
    except Exception as e:
        print(f'動画のダウンロードに失敗しました: {url}')
        print(f'エラー: {e}')
        exit()
else:
    print('無効な選択です')
    exit()

# メイン処理
print('\n処理を開始します...')
try:
    while True:
        cap.grab()
        ret, frame = cap.retrieve()
        if not ret:
            break

        processed_frame = video_processing(frame)
        cv2.imshow('YOLOv11 Pose Estimation', processed_frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
finally:
    cap.release()
    cv2.destroyAllWindows()
    if temp_file:
        try:
            os.remove(temp_file)
        except:
            pass

    # 結果をファイルに保存
    if results_log:
        with open('result.txt', 'w', encoding='utf-8') as f:
            f.write('\n'.join(results_log))
        print('\n処理結果をresult.txtに保存しました')

    print('プログラムを終了しました')

使用方法

  1. プログラムを実行すると、カメラが起動し、リアルタイムで姿勢推定が開始される
  2. カメラに向かって様々なポーズを取ると、17箇所のキーポイントが検出・表示される
  3. 各キーポイントには番号、名称、座標(ピクセル単位)が表示される
  4. YOLOv11の改良された特徴抽出により、より正確なキーポイント検出が実現される
  5. 'q'キーを押すとプログラムが終了する

実験・探求のアイデア

YOLOv11モデル選択実験

プログラム冒頭のMODEL_NAMEを変更することで、異なるYOLOv11モデルを比較できる:

Enhanced Feature Extraction検証実験

YOLOv11の改良された特徴抽出能力を検証:

検出閾値調整実験

CONF_THRESHOLDの値(0.0-1.0)を変更することで、検出感度を調整できる:

体験・実験・探求のアイデア

精度と効率性の体験実験: 異なるYOLOv11モデル(n, s, m, l, x)で同じ動作を行い、Enhanced Feature ExtractionとOptimized Efficiencyの効果を実感する

環境適応性実験:

高度なポーズ検出実験:

リアルタイム応用実験: YOLOv11の高速性を活用した応用アイデアの検証

複数人同時検出: 改良されたアーキテクチャによる複数人検出性能の向上を確認

建設現場安全監視シミュレーション: 危険姿勢(しゃがみ、前屈等)の自動検出実験