YOLOv8による2次元姿勢推定
【概要】YOLOv8-poseを使用してリアルタイム姿勢推定を実行。人体17箇所のキーポイント検出技術を学習し、5種類のモデルサイズによる精度と速度の比較実験が可能。Windows環境での実行手順、プログラムコード、実験アイデアを含む。

目次
概要
技術名: YOLOv8-pose(You Only Look Once version 8 - Pose Estimation)
論文: "YOLOv8: A New Era of Object Detection and Image Segmentation" (arXiv:2305.09972, 2023)
新規性・特徴: YOLOv8-poseは、単一のニューラルネットワークで人体の17箇所のキーポイント(関節位置)をリアルタイムで検出する技術である。従来手法と比較して高速性と精度を両立し、CPU環境でもリアルタイム処理が可能である。
アプリケーション例: スポーツ動作解析、リハビリテーション支援、フィットネスアプリ、モーションキャプチャ、姿勢矯正システム
体験価値: カメラの前で様々なポーズを取ることで、AI が人体の関節位置を瞬時に認識する過程を視覚的に体験できる。異なるモデルサイズによる精度と速度のトレードオフを実験的に比較し、AI の性能特性を理解することが可能である。
事前準備
Python, Windsurfをインストールしていない場合の手順(インストール済みの場合は実行不要)。
- 管理者権限でコマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)。
- 以下のコマンドをそれぞれ実行する(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 numpy
プログラムコード
# プログラム名: YOLOv8リアルタイム姿勢推定
# 特徴技術名: YOLOv8(You Only Look Once version 8)
# 出典: Jocher, G., Chaurasia, A., & Qiu, J. (2023). YOLO by Ultralytics. GitHub. https://github.com/ultralytics/ultralytics
# 特徴機能: リアルタイム姿勢推定 - 単一のニューラルネットワークで人体の17個のキーポイント(鼻、目、耳、肩、肘、手首、腰、膝、足首)を同時検出し、30FPS以上の高速処理を実現
# 学習済みモデル: YOLOv8姿勢推定モデル(yolov8n-pose.pt)- COCOデータセットで事前学習済み、人体姿勢の17キーポイントを高精度で検出、初回実行時に自動ダウンロード
# 方式設計:
# - 関連利用技術: OpenCV(動画処理・表示)、NumPy(数値計算)、tkinter(ファイル選択ダイアログ)
# - 入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://github.com/opencv/opencv/blob/master/samples/data/vtest.aviを使用)、出力: 処理結果が画像化できる場合にはOpenCV画面でリアルタイムに表示.OpenCV画面内に処理結果をテキストで表示.さらに,1秒間隔で,print()で処理結果を表示.プログラム終了時にprint()で表示した処理結果をresult.txtファイルに保存し,「result.txtに保存」したことをprint()で表示.プログラム開始時に,プログラムの概要,ユーザが行う必要がある操作(もしあれば)をprint()で表示.
# - 処理手順: 1.動画フレーム取得、2.YOLOv8モデルで推論実行、3.17個のキーポイント座標と信頼度取得、4.キーポイントを可視化、5.結果表示
# - 前処理、後処理: 前処理:フレームバッファクリア(最新フレーム取得)、後処理:信頼度閾値によるフィルタリング(0.5以上のキーポイントのみ表示)
# - 追加処理: キーポイント名称の日本語表示(nose→鼻等)による可読性向上
# - 調整を必要とする設定値: CONF_THRESHOLD(信頼度閾値、デフォルト0.5)- キーポイント検出の信頼度閾値、低くすると検出数増加但しノイズも増加
# 将来方策: 動的な信頼度閾値調整機能 - 検出されたキーポイント数に基づいて閾値を自動調整し、最適な検出精度を実現
# その他の重要事項: Windows環境での動作確認済み、初回実行時にモデルファイルが自動ダウンロードされる
# 前準備: pip install ultralytics opencv-python numpy
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
import torch
# システム設定
CAMERA_INDEX = 0 # 使用するカメラ番号
SAMPLE_VIDEO_URL = 'https://github.com/opencv/opencv/blob/master/samples/data/vtest.avi?raw=true'
SAMPLE_VIDEO_FILENAME = 'vtest.avi'
RESULT_FILENAME = 'result.txt'
PRINT_INTERVAL = 1.0 # 結果表示間隔(秒)
# YOLOv8設定
MODEL_NAME = 'yolov8n-pose.pt' # 使用するモデル(n/s/m/l/xから選択可能)
CONF_THRESHOLD = 0.5 # キーポイント検出の信頼度閾値(0.0-1.0)
INFERENCE_BATCH_SIZE = 1 # 推論時のバッチサイズ
# 表示設定
FONT_SCALE = 0.4
FONT_THICKNESS = 1
TEXT_COLOR = (255, 255, 255)
WINDOW_NAME = 'YOLOv8 姿勢推定'
# その他
RANDOM_SEED = 42 # 再現性のための乱数シード
KEYPOINT_NAMES = [
'鼻', '左目', '右目', '左耳', '右耳',
'左肩', '右肩', '左肘', '右肘',
'左手首', '右手首', '左腰', '右腰',
'左膝', '右膝', '左足首', '右足首'
]
# プログラム開始メッセージ
print('='*60)
print('YOLOv8リアルタイム姿勢推定プログラム')
print('='*60)
print('このプログラムは動画から人体の17個のキーポイントを検出します。')
print('検出されたキーポイントは画面上にリアルタイムで表示されます。')
print()
print('操作方法:')
print('- qキー: プログラムを終了')
print('- 検出結果は1秒ごとにコンソールに表示されます')
print('- 終了時に全ての検出結果がresult.txtに保存されます')
print('='*60)
print()
# 乱数シード設定
np.random.seed(RANDOM_SEED)
torch.manual_seed(RANDOM_SEED)
# 結果保存用リスト
detection_results = []
last_print_time = time.time()
def video_processing(frame, model):
"""動画フレームの処理"""
global last_print_time, detection_results
# 推論実行(最適化パラメータ付き)
results = model(frame, verbose=False, conf=CONF_THRESHOLD, batch=INFERENCE_BATCH_SIZE)
annotated_frame = results[0].plot()
current_time = time.time()
detected_keypoints = []
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[0] > 0 and keypoints_data.shape[1] == 17:
for person_idx in range(keypoints_data.shape[0]):
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'{KEYPOINT_NAMES[i]}({x:.0f},{y:.0f})'
cv2.putText(annotated_frame, text, (int(x), int(y)),
cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, TEXT_COLOR, FONT_THICKNESS)
person_keypoints.append(f'{KEYPOINT_NAMES[i]}: ({x:.0f}, {y:.0f}) [信頼度: {conf:.2f}]')
if person_keypoints:
detected_keypoints.append(person_keypoints)
# 指定間隔で結果を表示
if current_time - last_print_time >= PRINT_INTERVAL and detected_keypoints:
timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
print(f'\n[{timestamp}] 検出結果:')
for person_idx, person_keypoints in enumerate(detected_keypoints):
print(f' 人物{person_idx + 1}:')
for kp in person_keypoints:
print(f' - {kp}')
# 結果を保存
detection_results.append({
'timestamp': timestamp,
'detections': detected_keypoints
})
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(CAMERA_INDEX, cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
elif choice == '2':
# サンプル動画ダウンロード・処理
try:
print('サンプル動画をダウンロード中...')
urllib.request.urlretrieve(SAMPLE_VIDEO_URL, SAMPLE_VIDEO_FILENAME)
temp_file = SAMPLE_VIDEO_FILENAME
cap = cv2.VideoCapture(SAMPLE_VIDEO_FILENAME)
except Exception as e:
print(f'動画のダウンロードに失敗しました: {SAMPLE_VIDEO_URL}')
print(f'エラー: {e}')
exit()
else:
print('無効な選択です')
exit()
# GPU/CPU自動選択(利用可能ならGPUを使用)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f'使用デバイス: {device}')
# モデルロード
print('YOLOv8モデルをロード中...')
model = YOLO(MODEL_NAME)
print(f'モデル {MODEL_NAME} を正常にロードしました')
# メイン処理
try:
while True:
cap.grab()
ret, frame = cap.retrieve()
if not ret:
break
processed_frame = video_processing(frame, model)
cv2.imshow(WINDOW_NAME, processed_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
cv2.destroyAllWindows()
# 結果をファイルに保存
if detection_results:
with open(RESULT_FILENAME, 'w', encoding='utf-8') as f:
f.write('YOLOv8姿勢推定 検出結果\n')
f.write('='*60 + '\n\n')
for result in detection_results:
f.write(f'[{result["timestamp"]}]\n')
for person_idx, person_keypoints in enumerate(result['detections']):
f.write(f' 人物{person_idx + 1}:\n')
for kp in person_keypoints:
f.write(f' - {kp}\n')
f.write('\n')
print(f'\n検出結果を{RESULT_FILENAME}に保存しました')
if temp_file and os.path.exists(temp_file):
os.remove(temp_file)
使用方法
- プログラムを実行すると、カメラが起動し、リアルタイムで姿勢推定が開始される
- カメラに向かって様々なポーズを取ると、17箇所のキーポイントが検出・表示される
- 各キーポイントには番号、名称、座標(ピクセル単位)が表示される
- 'q'キーを押すとプログラムが終了する
実験・探求のアイデア
AIモデル選択実験
プログラム冒頭のMODEL_NAMEを変更することで、異なるモデルを比較できる:
yolov8n-pose.pt
:最小モデル(高速、低精度)yolov8s-pose.pt
:小型モデル(バランス型)yolov8m-pose.pt
:中型モデル(高精度、中速)yolov8l-pose.pt
:大型モデル(高精度、低速)yolov8x-pose.pt
:最大モデル(最高精度、最低速)
検出閾値調整実験
CONF_THRESHOLDの値(0.0-1.0)を変更することで、検出感度を調整できる:
- 0.3:低信頼度のキーポイントも表示(ノイズ増加)
- 0.5:標準設定(バランス型)
- 0.7:高信頼度のキーポイントのみ表示(検出数減少)
体験・実験・探求のアイデア
精度と速度の比較実験: 異なるモデル(n, s, m, l, x)で同じ動作を行い、検出精度と処理速度を比較する
ポーズ難易度実験:
- 正面立ち姿勢:全キーポイント検出可能性の確認
- 横向き姿勢:片側のキーポイントのみ検出される現象の観察
- しゃがみ姿勢:下半身キーポイントの重複や誤検出の確認
- 手を上げる動作:肘・手首の検出精度変化の観察
距離による検出性能: カメラからの距離を変えて、キーポイント検出の限界距離を測定する
複数人同時検出: 複数人がカメラに映った場合の検出性能を確認する
照明条件の影響: 明るさを変えて検出精度への影響を観察する