📝

ハンドトラッキングでPC操作してみる(Python + MediaPipe)

に公開

今回は Python + MediaPipe を使って、手のジェスチャーでPCを操作する方法を実装してみました。
カメラで手を認識し、Finderの起動/終了マウス操作 を実現します。

✨ 使用ライブラリ

  • OpenCV(映像処理)
  • MediaPipe(手のランドマーク検出)
  • pyautogui(マウス操作)
  • subprocess / osascript(Macアプリ制御)

事前に以下を仮想環境にインストールしてください:

  • Mac環境で実装
pip install opencv-python mediapipe pyautogui

👋 Part 1: ジェスチャーでアプリ操作

まずは 手の形でFinderを操作 するサンプルです。

  • パー(✋) → Finderを起動
  • 人差し指と中指をクロス → Finderを終了
import cv2
import mediapipe as mp
import subprocess
import time

mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils

FINDER_NAME = "Finder"

def finger_status(hand_landmarks):
    """指ごとに 伸びている(True) / 曲がっている(False) を判定"""
    fingers = []

    # 親指 (横方向で判定)
    fingers.append(hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP].x >
                   hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_IP].x)

    # 人差し指〜小指 (TIPがPIPより上なら伸びている)
    for tip, pip in [(mp_hands.HandLandmark.INDEX_FINGER_TIP, mp_hands.HandLandmark.INDEX_FINGER_PIP),
                     (mp_hands.HandLandmark.MIDDLE_FINGER_TIP, mp_hands.HandLandmark.MIDDLE_FINGER_PIP),
                     (mp_hands.HandLandmark.RING_FINGER_TIP, mp_hands.HandLandmark.RING_FINGER_PIP),
                     (mp_hands.HandLandmark.PINKY_TIP, mp_hands.HandLandmark.PINKY_PIP)]:
        fingers.append(hand_landmarks.landmark[tip].y <
                       hand_landmarks.landmark[pip].y)

    return fingers  # [Thumb, Index, Middle, Ring, Pinky]

def detect_gesture(hand_landmarks, fingers):
    """ジェスチャー判定"""
    index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
    middle_tip = hand_landmarks.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_TIP]

    # パー(5本全部伸ばす)
    if all(fingers):
        return "palm"

    # 人差し指と中指が交差
    if fingers[1] and fingers[2] and index_tip.x > middle_tip.x:
        return "close"

    return "none"

cap = cv2.VideoCapture(0)
with mp_hands.Hands(min_detection_confidence=0.7,
                    min_tracking_confidence=0.7,
                    max_num_hands=1) as hands:
    while True:
        ret, frame = cap.read()
        if not ret:
            break

        frame = cv2.flip(frame, 1)
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        result = hands.process(rgb_frame)

        if result.multi_hand_landmarks:
            hand = result.multi_hand_landmarks[0]
            mp_drawing.draw_landmarks(frame, hand, mp_hands.HAND_CONNECTIONS)

            fingers = finger_status(hand)
            gesture = detect_gesture(hand, fingers)

            cv2.putText(frame, f"Gesture: {gesture}", (10, 40),
                        cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

            if gesture == "palm":
                print("Opening Finder...")
                subprocess.Popen(["open", "-a", FINDER_NAME])

            elif gesture == "close":
                print("Closing Finder...")
                time.sleep(0.2)
                subprocess.Popen([
                    "osascript", "-e", f'tell application "{FINDER_NAME}" to quit'
                ])

        cv2.imshow("Hand Gesture Control", frame)
        if cv2.waitKey(1) & 0xFF == 27:  # ESC
            break

cap.release()
cv2.destroyAllWindows()

🖱️ Part 2: バーチャルマウス操作

次に 人差し指でマウス移動、グー✊で左クリック、ピース✌️で右クリック を実現するコードです。

import cv2
import mediapipe as mp
import pyautogui
import math
import time

# MediaPipe Hands 初期化
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils

# 画面サイズ取得
screen_width, screen_height = pyautogui.size()

# カメラ起動
cap = cv2.VideoCapture(0)

last_click_time = 0
click_cooldown = 0.3  # クリック連打防止

with mp_hands.Hands(
    min_detection_confidence=0.7,
    min_tracking_confidence=0.7,
    max_num_hands=1
) as hands:
    while True:
        ret, frame = cap.read()
        if not ret:
            break

        frame = cv2.flip(frame, 1)  # ミラー表示
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        result = hands.process(rgb_frame)

        if result.multi_hand_landmarks:
            hand = result.multi_hand_landmarks[0]
            mp_drawing.draw_landmarks(frame, hand, mp_hands.HAND_CONNECTIONS)

            index_tip = hand.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
            thumb_tip = hand.landmark[mp_hands.HandLandmark.THUMB_TIP]
            middle_tip = hand.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_TIP]

            # ---- マウス移動 (人差し指) ----
            x = int(index_tip.x * screen_width)
            y = int(index_tip.y * screen_height)

            # スムージング
            smooth_x = (pyautogui.position().x * 0.7) + (x * 0.4)
            smooth_y = (pyautogui.position().y * 0.7) + (y * 0.4)
            pyautogui.moveTo(smooth_x, smooth_y)

            # ---- グー (左クリック) ----
            def is_fist(hand):
                return all(
                    hand.landmark[tip].y > hand.landmark[tip - 2].y
                    for tip in [
                        mp_hands.HandLandmark.THUMB_TIP,
                        mp_hands.HandLandmark.INDEX_FINGER_TIP,
                        mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
                        mp_hands.HandLandmark.RING_FINGER_TIP,
                        mp_hands.HandLandmark.PINKY_TIP
                    ]
                )

            if is_fist(hand) and (time.time() - last_click_time) > click_cooldown:
                pyautogui.click()
                print("Left Click (Fist ✊)")
                last_click_time = time.time()

            # ---- 右クリック(ピース ✌️) ----
            dist_index_middle = math.dist(
                [index_tip.x, index_tip.y],
                [middle_tip.x, middle_tip.y]
            )
            if dist_index_middle > 0.1 and (time.time() - last_click_time) > click_cooldown:
                pyautogui.click(button="right")
                print("Right Click (✌️)")
                last_click_time = time.time()

        cv2.imshow("Virtual Mouse", frame)
        if cv2.waitKey(1) & 0xFF == 27:  # ESCで終了
            break

cap.release()
cv2.destroyAllWindows()

精度は少しイマイチだった...

🎉 実行してみた感想

  • MediaPipeは軽量で精度も高いので、カメラ1つで簡単にハンドトラッキングができた。
  • 指の判定を工夫すれば、ジェスチャーにもっといろんな操作を割り当てられそう。
  • マウスの実用性はさておき、自作の マウス操作方法 を設定するのはかなり楽しい。

👉 これを応用して、音楽再生やウィンドウ操作、ゲームの操作などに繋げてみるのも面白いと思いました。

おまけ

https://youtu.be/PgJ8vQPqISU

Discussion