import cv2 import mediapipe as mp import math print("Запуск MediaPipe детектора рук (новый API)...") print(f"Версия MediaPipe: {mp.__version__}") if not hasattr(mp, 'tasks'): print("ОШИБКА: mediapipe.tasks не найден!") exit() BaseOptions = mp.tasks.BaseOptions HandLandmarker = mp.tasks.vision.HandLandmarker HandLandmarkerOptions = mp.tasks.vision.HandLandmarkerOptions VisionRunningMode = mp.tasks.vision.RunningMode HAND_CONNECTIONS = [ (0, 1), (1, 2), (2, 3), (3, 4), (0, 5), (5, 6), (6, 7), (7, 8), (0, 9), (9, 10), (10, 11), (11, 12), (0, 13), (13, 14), (14, 15), (15, 16), (0, 17), (17, 18), (18, 19), (19, 20), (5, 9), (9, 13), (13, 17) ] options = HandLandmarkerOptions( base_options=BaseOptions(model_asset_path='hand_landmarker.task'), running_mode=VisionRunningMode.IMAGE, num_hands=2, min_hand_detection_confidence=0.7, min_hand_presence_confidence=0.7, min_tracking_confidence=0.5 ) hand_landmarker = HandLandmarker.create_from_options(options) cap = cv2.VideoCapture(0) if not cap.isOpened(): print("Ошибка: не удалось открыть камеру") exit() print("Нажмите 'q' для выхода") hand_colors = [ (0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 0, 255), ] def recognize_gesture(hand_landmarks, handedness, fingers_up): """ Распознаёт жесты на основе положения пальцев. hand_landmarks: список из 21 точки (x, y) handedness: 'Left' или 'Right' fingers_up: список названий поднятых пальцев Возвращает: название жеста или None """ # === Жест "ОК" === # Кончики большого (4) и указательного (8) пальцев соединены thumb_tip = hand_landmarks[4] index_tip = hand_landmarks[8] distance_thumb_index = math.sqrt( (thumb_tip[0] - index_tip[0])**2 + (thumb_tip[1] - index_tip[1])**2 ) # Порог расстояния (в пикселях) - зависит от размера руки в кадре pinch_threshold = 50 if distance_thumb_index < pinch_threshold: # Проверяем, что остальные пальцы не обязательно подняты return "OK" # === Жест "Большой палец вверх" === # Только большой палец поднят, остальные согнуты if fingers_up == ['Большой']: # Дополнительно проверяем, что большой палец направлен вверх thumb_tip = hand_landmarks[4] thumb_ip = hand_landmarks[3] if thumb_tip[1] > thumb_ip[1]: # Кончик выше сустава return "THUMBS_UP" # === Жест "Победа/Мир" (V sign) === # Указательный и средний подняты, остальные согнуты if set(fingers_up) == {'Указательный', 'Средний'}: return "PEACE" # === Жест "Стоп/Пять" === # Все 5 пальцев подняты if len(fingers_up) == 5: return "STOP" # === Жест "Рок/Коза" === # Указательный и мизинец подняты, остальные согнуты if set(fingers_up) == {'Указательный', 'Мизинец'}: return "ROCK" # === Жест "Указание" === # Только указательный палец поднят if fingers_up == ['Указательный']: return "POINT" # === Жест "Кулак" === # Ни один палец не поднят if len(fingers_up) == 0: return "FIST" # === Жест "Три" === # Указательный, средний и безымянный подняты if set(fingers_up) == {'Указательный', 'Средний', 'Безымянный'}: return "THREE" # === Жест "Четыре" === # Четыре пальца подняты (все кроме большого) if set(fingers_up) == {'Указательный', 'Средний', 'Безымянный', 'Мизинец'}: return "FOUR" # Жест не распознан return None def count_fingers(hand_landmarks, handedness): """Подсчитывает количество поднятых пальцев""" fingers_up = [] # Большой палец thumb_tip = hand_landmarks[4] thumb_ip = hand_landmarks[3] if handedness == 'Right': if thumb_ip[0] > thumb_tip[0] : fingers_up.append('Большой') else: if thumb_ip[0] > thumb_tip[0] : fingers_up.append('Большой') # Указательный if hand_landmarks[8][1] < hand_landmarks[6][1]: fingers_up.append('Указательный') # Средний if hand_landmarks[12][1] < hand_landmarks[10][1]: fingers_up.append('Средний') # Безымянный if hand_landmarks[16][1] < hand_landmarks[14][1]: fingers_up.append('Безымянный') # Мизинец if hand_landmarks[20][1] < hand_landmarks[18][1]: fingers_up.append('Мизинец') return len(fingers_up), fingers_up def detect_palm_orientation(hand_landmarks, handedness): """ Определяет ориентацию ладони: к камере или тыльной стороной. Логика метода: - Берём большой палец (точка 4) и мизинец (точка 17) - Сравниваем их x-координаты - Для ПРАВОЙ руки: если большой палец ЛЕВЕЕ мизинца (x меньше) → ладонь к камере - Для ЛЕВОЙ руки: если большой палец ПРАВЕЕ мизинца (x больше) → ладонь к камере Почему так: - Когда правая рука показывает ладонью к камере, большой палец оказывается слева - Когда правая рука показывает тыльной стороной, большой палец оказывается справа - Для левой руки всё зеркально Возвращает: 'palm' (ладонь к камере) или 'back' (тыльная сторона) """ thumb_tip = hand_landmarks[4] # Кончик большого пальца pinky_mcp = hand_landmarks[17] # Основание мизинца thumb_x = thumb_tip[0] pinky_x = pinky_mcp[0] if handedness == 'Right': # Правая рука: большой палец слева от мизинца → ладонь к камере if thumb_x < pinky_x: return 'back' else: return 'palm' else: # Левая рука: большой палец справа от мизинца → ладонь к камере if thumb_x > pinky_x: return 'back' else: return 'palm' frame_counter = 0 while True: ret, frame = cap.read() if not ret: break rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_frame) result = hand_landmarker.detect(mp_image) h, w, _ = frame.shape total_fingers = 0 if result.hand_landmarks: for hand_idx, hand_landmarks in enumerate(result.hand_landmarks): color = hand_colors[hand_idx % len(hand_colors)] handedness = result.handedness[hand_idx][0].category_name points = [] for lm in hand_landmarks: px = int(lm.x * w) py = int(lm.y * h) points.append((px, py)) # Подсчёт пальцев fingers_count, fingers_names = count_fingers(points, handedness) total_fingers += fingers_count # Определение ориентации ладони orientation = detect_palm_orientation(points, handedness) # === РАСПОЗНАВАНИЕ ЖЕСТА === gesture = recognize_gesture(points, handedness, fingers_names) # Рисуем соединения for connection in HAND_CONNECTIONS: start_idx, end_idx = connection if start_idx < len(points) and end_idx < len(points): pt1 = points[start_idx] pt2 = points[end_idx] # Если ладонь к камере - рисуем жирнее и ярче thickness = 3 if orientation == 'palm' else 2 cv2.line(frame, pt1, pt2, color, thickness) # Рисуем точки for i, (px, py) in enumerate(points): if i == 0: cv2.circle(frame, (px, py), 8, color, -1) cv2.circle(frame, (px, py), 8, (255, 255, 255), 2) elif i in [4, 8, 12, 16, 20]: cv2.circle(frame, (px, py), 7, color, -1) cv2.circle(frame, (px, py), 7, (255, 255, 255), 2) else: cv2.circle(frame, (px, py), 5, color, -1) # Подписи if points: wrist_x, wrist_y = points[0] # Название руки cv2.putText(frame, f"Hand {hand_idx+1}: {handedness}", (wrist_x - 50, wrist_y - 40), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2) # Ориентация ладони (с цветовой индикацией) if orientation == 'palm': orient_text = "LADON (palm)" orient_color = (0, 255, 0) # Зелёный else: orient_text = "TYLNAYA (back)" orient_color = (0, 0, 255) # Красный cv2.putText(frame, orient_text, (wrist_x - 50, wrist_y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, orient_color, 2) # === ОТОБРАЖЕНИЕ ЖЕСТА === if gesture: gesture_color = (0, 255, 255) # Жёлтый для распознанного жеста cv2.putText(frame, f"GESTURE: {gesture}", (wrist_x - 50, wrist_y), cv2.FONT_HERSHEY_SIMPLEX, 0.9, gesture_color, 3) else: cv2.putText(frame, "No gesture", (wrist_x - 50, wrist_y), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (128, 128, 128), 2) # Количество пальцев cv2.putText(frame, f"Fingers: {fingers_count}", (wrist_x - 50, wrist_y + 25), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 3) # Список пальцев if fingers_names: fingers_text = ', '.join(fingers_names) cv2.putText(frame, fingers_text, (wrist_x - 50, wrist_y + 55), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2) # Большая панель с общей информацией cv2.rectangle(frame, (10, 10), (350, 120), (0, 0, 0), -1) cv2.putText(frame, f"Total fingers: {total_fingers}", (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 255), 3) hands_count = len(result.hand_landmarks) if result.hand_landmarks else 0 cv2.putText(frame, f"Hands detected: {hands_count}", (20, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2) # Вывод в консоль (раз в 30 кадров) frame_counter += 1 if result.hand_landmarks and frame_counter % 30 == 0: print(f"\n=== Кадр {frame_counter} ===") print(f"Рук обнаружено: {hands_count}, всего пальцев: {total_fingers}") for hand_idx, hand_landmarks in enumerate(result.hand_landmarks): handedness = result.handedness[hand_idx][0].category_name points = [(int(lm.x * w), int(lm.y * h)) for lm in hand_landmarks] fingers_count, fingers_names = count_fingers(points, handedness) orientation = detect_palm_orientation(points, handedness) orient_str = "ЛАДОНЬ" if orientation == 'palm' else "ТЫЛЬНАЯ" fingers_str = ', '.join(fingers_names) if fingers_names else 'нет' print(f" Рука {hand_idx+1} ({handedness}): ориентация={orient_str}, " f"пальцев={fingers_count} [{fingers_str}]") cv2.imshow("Hand Detection - Palm Orientation", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() print("Завершено.")