Загрузка данных


Привет! Я делаю проект: управление роботом в Roblox Build a Boat с помощью отслеживания движений рук через веб-камеру.

Что уже есть:

· Python-скрипт с MediaPipe Hands, который определяет, согнут ли каждый палец целиком (по углу между основанием, суставом и кончиком).
· Для каждого пальца рисуется зелёный (разогнут) или красный (согнут) кружок на кончике.
· Мы обсуждали добавление симуляции нажатий клавиш с помощью pyautogui: при сгибании пальца зажимается определённая клавиша, при разгибании — отпускается.
· Я хочу управлять не просто пальцем целиком, а каждой фалангой отдельно (угол в каждом суставе). Нужно переделать код так, чтобы для каждого сустава (MCP, PIP, DIP) вычислялся угол и отправлялась своя клавиша.

Текущая задача:
Переработать код, чтобы для указательного, среднего, безымянного и мизинца было по 3 сустава (3 клавиши на палец), для большого — 2 сустава (2 клавиши). Управление — непрерывное (клавиша зажата, пока сустав согнут).

Технологии: Python 3.10/3.11, OpenCV, MediaPipe, PyAutoGUI.

Предыдущий код (базовый трекинг пальцев):
import cv2
import mediapipe as mp
import numpy as np
import tkinter as tk

# Получение размеров экрана
root = tk.Tk()
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.destroy()

mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=1)

cap = cv2.VideoCapture(0)

# Установка разрешения видео в Full HD
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

# Устанавливаем размер окна на 80% от размера экрана
window_width = int(screen_width * 0.9)
window_height = int(screen_height * 0.9)

tip_ids = [4, 8, 12, 16, 20]
base_ids = [0, 5, 9, 13, 17]
joint_ids = [3, 6, 10, 14, 18]

# Пороговые значения углов для пальцев
thumb_bend_threshold = 40
finger_bend_threshold = 50

def get_angle(v1, v2):
    v1 = np.array(v1)
    v2 = np.array(v2)
    dot_product = np.dot(v1, v2)
    norm_v1 = np.linalg.norm(v1)
    norm_v2 = np.linalg.norm(v2)
    cosine_angle = dot_product / (norm_v1 * norm_v2)
    angle = np.arccos(cosine_angle)
    return np.degrees(angle)

def is_finger_bent(base, joint, tip, is_thumb=False):
    v1 = [joint.x - base.x, joint.y - base.y, joint.z - base.z]
    v2 = [tip.x - joint.x, tip.y - joint.y, tip.z - joint.z]
    angle = get_angle(v1, v2)
    if is_thumb:
        return angle < thumb_bend_threshold
    else:
        return angle < finger_bend_threshold

while True:
    ret, frame = cap.read()
    if not ret:
        continue

    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    results = hands.process(frame)
    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
            landmarks = hand_landmarks.landmark
            
            for id, landmark in enumerate(landmarks):
                h, w, c = frame.shape
                cx, cy = int(landmark.x * w), int(landmark.y * h)
                cv2.putText(frame, str(id), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1)
            
            for finger_index, tip_id in enumerate(tip_ids):
                base_id = base_ids[finger_index]
                joint_id = joint_ids[finger_index]
                is_thumb = (finger_index == 0)
                if is_finger_bent(landmarks[base_id], landmarks[joint_id], landmarks[tip_id], is_thumb):
                    cx, cy = int(landmarks[tip_id].x * frame.shape[1]), int(landmarks[tip_id].y * frame.shape[0])
                    cv2.circle(frame, (cx, cy), 10, (0, 0, 255), cv2.FILLED)
                else:
                    cx, cy = int(landmarks[tip_id].x * frame.shape[1]), int(landmarks[tip_id].y * frame.shape[0])
                    cv2.circle(frame, (cx, cy), 10, (0, 255, 0), cv2.FILLED)

    # Масштабируем изображение до размера окна
    frame_resized = cv2.resize(frame, (window_width, window_height))
    
    cv2.imshow('Fingers', frame_resized)
    cv2.resizeWindow('Fingers', window_width, window_height)

    if cv2.waitKey(10) == 27:
        break

cap.release()
cv2.destroyAllWindows()

Можешь помочь дописать код под фаланги и объяснить, как лучше назначить клавиши?