4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MediaPipeを使用して誰でも簡単にハンドトラッキングゲームの作成手順

Last updated at Posted at 2024-07-19

MediaPipeとは

Googleが提供するライブストリーミングのためのオープンソースのMLソリューションです。このMediaPipeを利用すると高性能なAI画像処理アルゴリズムを利用したARアプリケーション等を簡単に作成できます。

詳細はMediaPipeの公式サイトでご確認ください。

環境構築

Android, Web, Python, iOS に対応しているそうですが、今回はPythonを使って仮想環境を作って開発していしていきます。

Pythonのインストール

公式サイトから最新のPythonをダウンロードしてインストールします。
インストール方法は別のサイトで手順を確認してください。
インストール時に「Add Python to PATH」にチェックを入れておいてください。

仮想環境の作成

仮想環境とはパソコンの中に仮想的な環境を作ることで、いろんなバージョンのpythonやライブラリを使えるようにすることです。

以下の手順を追ってください。

コマンドプロンプトなどを開いて、自分が作成したいプロジェクトディレクトリにいき、以下のコマンドで仮想環境を作成します。

python -m venv myenv

これでmyenvという名前の仮想環境が構築されます。

仮想環境の有効化

同じディレクトリ内にて、以下のコマンドで打って仮想環境を有効化します。
Windows:

myenv\Scripts\activate

macOS/Linux:

source myenv/bin/activate

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

仮想環境を有効化した状態で、以下のコマンドでライブラリをインストールします。

pip install mediapipe opencv-python

これで、mediapipeとopencvのライブラリを仮想環境上にインストールされます。

ゲームの実装

・ゲームの説明
目的: 右手の人差し指で赤い丸に触れてスコアを稼ぐ。
制限時間: 30秒。
操作:
'f' キーでフルスクリーンとウィンドウモードを切り替え。
'q' キーでゲームを終了。(30秒たつと自動的に終了はします。)

import cv2
import mediapipe as mp
import random
import math
import time

# MediaPipe Handsのセットアップ
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_drawing = mp.solutions.drawing_utils

# スコアの初期化
score = 0

# ランダムに赤い丸を表示するための初期位置
circle_x = random.randint(100, 500)
circle_y = random.randint(100, 400)
circle_radius = 20

# カメラからの映像をキャプチャ
cap = cv2.VideoCapture(0)

# OpenCVのウィンドウを作成
cv2.namedWindow('Hand Game', cv2.WINDOW_NORMAL)
is_fullscreen = False

# ゲームの制限時間(秒)
game_duration = 30
start_time = time.time()

def is_hand_touching_circle(hand_x, hand_y, circle_x, circle_y, circle_radius):
    distance = math.sqrt((hand_x - circle_x) ** 2 + (hand_y - circle_y) ** 2)
    return distance < circle_radius

def generate_new_circle_position(frame_width, frame_height, circle_radius, exclude_area):
    while True:
        new_x = random.randint(circle_radius, frame_width - circle_radius)
        new_y = random.randint(circle_radius, frame_height - circle_radius)
        if not (exclude_area[0] <= new_x <= exclude_area[2] and exclude_area[1] <= new_y <= exclude_area[3]):
            return new_x, new_y

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

    # 画像を水平方向に反転
    frame = cv2.flip(frame, 1)

    # 画像をRGBに変換
    image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # 手のランドマークの検出
    results = hands.process(image)

    # 画像をBGRに戻す
    frame = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    # スコアとタイマー表示領域の背景を描画
    cv2.rectangle(frame, (0, 0), (200, 100), (0, 0, 0), -1)

    # 検出結果がある場合、ランドマークを描画し、右手の位置を取得
    if results.multi_hand_landmarks:
        for idx, hand_landmarks in enumerate(results.multi_hand_landmarks):
            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            # ランドマークの座標を取得(例として8番目のランドマーク=右手の人差し指の先端を使用)
            hand_x = int(hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].x * frame.shape[1])
            hand_y = int(hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].y * frame.shape[0])

            if is_hand_touching_circle(hand_x, hand_y, circle_x, circle_y, circle_radius):
                score += 1
                # 新しい位置に赤い丸を再配置
                circle_x, circle_y = generate_new_circle_position(frame.shape[1], frame.shape[0], circle_radius, (0, 0, 200, 100))

    # 赤い丸を描画
    cv2.circle(frame, (circle_x, circle_y), circle_radius, (0, 0, 255), -1)

    # スコアを表示
    cv2.putText(frame, f'Score: {score}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)

    # 残り時間を計算
    elapsed_time = time.time() - start_time
    remaining_time = max(0, game_duration - int(elapsed_time))
    cv2.putText(frame, f'Time: {remaining_time}', (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)

    # ゲーム終了条件
    if remaining_time <= 0:
        break

    # 画像を表示
    cv2.imshow('Hand Game', frame)

    # キー入力をチェック
    key = cv2.waitKey(10) & 0xFF
    if key == ord('q'):
        break
    elif key == ord('f'):
        is_fullscreen = not is_fullscreen
        if is_fullscreen:
            cv2.setWindowProperty('Hand Game', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
        else:
            cv2.setWindowProperty('Hand Game', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_NORMAL)

# リソースの解放
cap.release()
cv2.destroyAllWindows()

# ゲーム終了メッセージ
print(f'Game Over! Your score is {score}')

取得できるlandmarkは以下の通りです。参考

image.png

動作確認

上記のファイルを仮想環境が作られたディレクトリ内で作成して、仮想環境を使って実行すれば動きます。VScodeではRun Python File in Dedicated Terminalを押せば仮想環境で実行されます。

image.png

結果(動作例)

image.png

感想

こんなにも簡単に、しかもローカルでハンドトラッキングができることに驚きました。他にも、いろんな機能があるようなので試してみようと思います。

次回の記事でじゃんけんの手の判別をしてみたという記事も書いています。よければ見てみてください。

4
0
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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?