import cv2 import mediapipe as mp from mediapipe.tasks import python from mediapipe.tasks.python import vision import numpy as np import time import math def overlay_image_alpha(img, img_overlay, x, y, alpha_mask): """Наложение изображения img_overlay на img с учетом альфа-канала.""" y1, y2 = max(0, y), min(img.shape[0], y + img_overlay.shape[0]) x1, x2 = max(0, x), min(img.shape[1], x + img_overlay.shape[1]) y1o, y2o = max(0, -y), min(img_overlay.shape[0], img.shape[0] - y) x1o, x2o = max(0, -x), min(img_overlay.shape[1], img.shape[1] - x) if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o: return alpha = alpha_mask[y1o:y2o, x1o:x2o] alpha_inv = 1.0 - alpha for c in range(3): img[y1:y2, x1:x2, c] = (alpha * img_overlay[y1o:y2o, x1o:x2o, c] + alpha_inv * img[y1:y2, x1:x2, c]) def check_gestures(pose_landmarks, gesture_result): """Проверяет позы пользователя и возвращает 'timeout', 'pointing', 'shocked', 'cinema', 'fist', 'bye' или None.""" is_fist = False is_open_palm = False # 1. Жесты кисти if gesture_result and gesture_result.gestures: for hand_gestures in gesture_result.gestures: for gesture in hand_gestures: if gesture.category_name == 'Pointing_Up' and gesture.score > 0.5: return 'pointing' if gesture.category_name == 'Closed_Fist' and gesture.score > 0.5: is_fist = True if gesture.category_name == 'Open_Palm' and gesture.score > 0.5: is_open_palm = True # 2. Поза тела if pose_landmarks: landmarks = pose_landmarks[0] nose = landmarks[0] left_ear = landmarks[7] right_ear = landmarks[8] mouth_l = landmarks[9] mouth_r = landmarks[10] left_wrist = landmarks[15] right_wrist = landmarks[16] mouth_x = (mouth_l.x + mouth_r.x) / 2.0 mouth_y = (mouth_l.y + mouth_r.y) / 2.0 # Мем "Кулак ко рту" if is_fist: dist_l_mouth = math.hypot(left_wrist.x - mouth_x, left_wrist.y - mouth_y) if left_wrist.visibility > 0.15 else 999 dist_r_mouth = math.hypot(right_wrist.x - mouth_x, right_wrist.y - mouth_y) if right_wrist.visibility > 0.15 else 999 if dist_l_mouth < 0.25 or dist_r_mouth < 0.25: return 'fist' if left_wrist.visibility > 0.15 and right_wrist.visibility > 0.15: # Мем "Absolute Cinema" (обе руки высоко подняты) if left_wrist.y < nose.y and right_wrist.y < nose.y: return 'cinema' # Мем с Шаком dist_wrists = math.hypot(left_wrist.x - right_wrist.x, left_wrist.y - right_wrist.y) if dist_wrists < 0.15 and left_wrist.y > nose.y and right_wrist.y > nose.y: return 'timeout' # Мем "Шок" dist_l = math.hypot(left_wrist.x - left_ear.x, left_wrist.y - left_ear.y) dist_r = math.hypot(right_wrist.x - right_ear.x, right_wrist.y - right_ear.y) if dist_l < 0.25 and dist_r < 0.25: return 'shocked' # Мем "Пока" (одна открытая ладонь на уровне лица) # Проверяем после Cinema, чтобы не перебивало двумя руками if is_open_palm: # Если хотя бы одно запястье находится примерно на уровне носа (по вертикали) if (left_wrist.visibility > 0.15 and abs(left_wrist.y - nose.y) < 0.25) or \ (right_wrist.visibility > 0.15 and abs(right_wrist.y - nose.y) < 0.25): return 'bye' return None def load_mask(path): img = cv2.imread(path, cv2.IMREAD_UNCHANGED) if img is None: return None if img.shape[2] == 3: b, g, r = cv2.split(img) alpha = np.ones(b.shape, dtype=b.dtype) * 255 img = cv2.merge((b, g, r, alpha)) return img def main(): # Загружаем маски mask_timeout = load_mask('timeout.png') mask_pointing = load_mask('pointing.png') mask_cinema = load_mask('scorsese.png') mask_shocked = load_mask('shocked.png') mask_fist = load_mask('fist.png') mask_jarvis = load_mask('jarvis.png') mask_bye = load_mask('bye.png') if mask_timeout is None or mask_pointing is None or mask_cinema is None or mask_shocked is None or mask_fist is None or mask_jarvis is None or mask_bye is None: print("Ошибка: Не удалось загрузить одну из картинок.") return # Инициализация моделей Tasks API base_options_face = python.BaseOptions(model_asset_path='face_detector.tflite') options_face = vision.FaceDetectorOptions(base_options=base_options_face, running_mode=vision.RunningMode.VIDEO) face_detector = vision.FaceDetector.create_from_options(options_face) base_options_pose = python.BaseOptions(model_asset_path='pose_landmarker.task') options_pose = vision.PoseLandmarkerOptions(base_options=base_options_pose, running_mode=vision.RunningMode.VIDEO, output_segmentation_masks=False) pose_landmarker = vision.PoseLandmarker.create_from_options(options_pose) base_options_gesture = python.BaseOptions(model_asset_path='gesture_recognizer.task') options_gesture = vision.GestureRecognizerOptions(base_options=base_options_gesture, running_mode=vision.RunningMode.VIDEO, num_hands=2) gesture_recognizer = vision.GestureRecognizer.create_from_options(options_gesture) cap = cv2.VideoCapture(0) if not cap.isOpened(): print("Ошибка: Не удалось открыть камеру") return print("Камера запущена!") print("Управление размером: клавиша '=' (или '+') - увеличить, '-' - уменьшить.") scale_factor = 2.5 # Базовый размер while cap.isOpened(): success, image = cap.read() if not success: continue image = cv2.flip(image, 1) image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=image_rgb) timestamp_ms = int(time.time() * 1000) # Обрабатываем кадр face_result = face_detector.detect_for_video(mp_image, timestamp_ms) pose_result = pose_landmarker.detect_for_video(mp_image, timestamp_ms) gesture_result = gesture_recognizer.recognize_for_video(mp_image, timestamp_ms) pose_lms = pose_result.pose_landmarks if (pose_result and hasattr(pose_result, 'pose_landmarks')) else None detected_gesture = check_gestures(pose_lms, gesture_result) if face_result and hasattr(face_result, 'detections') and face_result.detections: detection = face_result.detections[0] bbox = detection.bounding_box ih, iw, _ = image.shape x = int(bbox.origin_x) y = int(bbox.origin_y) w = int(bbox.width) h = int(bbox.height) if w > iw * 0.25: detected_gesture = 'jarvis' if detected_gesture: if detected_gesture == 'timeout': current_mask = mask_timeout elif detected_gesture == 'pointing': current_mask = mask_pointing elif detected_gesture == 'shocked': current_mask = mask_shocked elif detected_gesture == 'fist': current_mask = mask_fist elif detected_gesture == 'jarvis': current_mask = mask_jarvis elif detected_gesture == 'bye': current_mask = mask_bye else: current_mask = mask_cinema mask_w = int(w * scale_factor) mask_h = int((mask_w / current_mask.shape[1]) * current_mask.shape[0]) mask_x = x - int((mask_w - w) / 2) mask_y = y - int((mask_h - h) / 1.5) if mask_w > 0 and mask_h > 0: resized_mask = cv2.resize(current_mask, (mask_w, mask_h)) alpha_mask = resized_mask[:, :, 3] / 255.0 overlay_image_alpha(image, resized_mask[:, :, :3], mask_x, mask_y, alpha_mask) cv2.putText(image, f"Size: {scale_factor:.1f}x (+/- to change)", (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 200), 1, cv2.LINE_AA) # Вывод на экран cv2.imshow('Meme Detector', image) key = cv2.waitKey(5) & 0xFF if key == ord('q'): break elif key == ord('=') or key == ord('+'): scale_factor += 0.1 elif key == ord('-'): scale_factor -= 0.1 scale_factor = max(0.5, min(scale_factor, 10.0)) cap.release() cv2.destroyAllWindows() face_detector.close() pose_landmarker.close() gesture_recognizer.close() if __name__ == "__main__": main()