手でマウスの代わり。人差し指でマウスの動き
親指と指をくっ付けるとクリック。
主にpyautoguiとmediapipeライブラリを使用。

import os
import cv2
import mediapipe as mp
import pyautogui
import numpy as np

# GPU 無効化
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

# Mediapipe 設定
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(max_num_hands=1, min_detection_confidence=0.7, min_tracking_confidence=0.7)

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

# カメラ設定
cap = cv2.VideoCapture(3)  # 0: 内蔵カメラ / 1: 外部カメラ

# カーソル移動用
prev_x, prev_y = screen_width // 2, screen_height // 2

# クリック判定フラグ
is_clicking = False

# 画面中央80%の範囲に制限するための設定
MIN_RATIO = 0.1  # 10% のマージン
MAX_RATIO = 0.9  # 90% の範囲

while cap.isOpened():
    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:
        for hand_landmarks in result.multi_hand_landmarks:
            # ランドマーク描画
            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            # 各指のランドマーク取得
            index_tip = hand_landmarks.landmark[8]  # 人差し指の先端
            thumb_tip = hand_landmarks.landmark[4]  # 親指の先端

            # **座標を中央80%の範囲にマッピング**
            norm_x = (index_tip.x - MIN_RATIO) / (MAX_RATIO - MIN_RATIO)
            norm_y = (index_tip.y - MIN_RATIO) / (MAX_RATIO - MIN_RATIO)

            # 範囲外なら制限
            norm_x = max(0, min(1, norm_x))
            norm_y = max(0, min(1, norm_y))

            # マウス座標変換
            x, y = int(norm_x * screen_width), int(norm_y * screen_height)

            # カーソル移動のスムージング(少し前の位置を影響させる)
            cursor_x = int(prev_x * 0.7 + x * 0.3)
            cursor_y = int(prev_y * 0.7 + y * 0.3)

            # カーソル移動
            pyautogui.moveTo(cursor_x, cursor_y)
            prev_x, prev_y = cursor_x, cursor_y

            # クリック(人差し指と親指が近づいたら)
            distance_click = np.linalg.norm(np.array([index_tip.x, index_tip.y]) - np.array([thumb_tip.x, thumb_tip.y]))

            if distance_click < 0.04 and not is_clicking:
                is_clicking = True
                pyautogui.click()
                print("🖱️ クリック!")
            elif distance_click > 0.05:
                is_clicking = False  # クリック解除

    # 画面表示
    cv2.imshow("Hand Tracking Mouse", frame)

    # ESCキーで終了
    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()