Files
testCursor/handle_faces.py
T

328 lines
13 KiB
Python

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("Завершено.")