📝
ハンドトラッキングでPC操作してみる(Python + MediaPipe)
今回は Python + MediaPipe を使って、手のジェスチャーでPCを操作する方法を実装してみました。
カメラで手を認識し、Finderの起動/終了 や マウス操作 を実現します。
✨ 使用ライブラリ
事前に以下を仮想環境にインストールしてください:
- 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つで簡単にハンドトラッキングができた。
- 指の判定を工夫すれば、ジェスチャーにもっといろんな操作を割り当てられそう。
- マウスの実用性はさておき、自作の マウス操作方法 を設定するのはかなり楽しい。
👉 これを応用して、音楽再生やウィンドウ操作、ゲームの操作などに繋げてみるのも面白いと思いました。
おまけ
Discussion