commit 43978f942339b8c2e612ba5664c47e6a7a87771e Author: AmanIbrash-brsh Date: Fri Sep 11 16:19:42 2026 +0500 вариант 6 эмоций diff --git a/bye.png b/bye.png new file mode 100644 index 0000000..b5d9cb2 Binary files /dev/null and b/bye.png differ diff --git a/face_detector.tflite b/face_detector.tflite new file mode 100644 index 0000000..1b2b522 Binary files /dev/null and b/face_detector.tflite differ diff --git a/face_landmarker.task b/face_landmarker.task new file mode 100644 index 0000000..c50c845 Binary files /dev/null and b/face_landmarker.task differ diff --git a/fist.png b/fist.png new file mode 100644 index 0000000..82fd6eb Binary files /dev/null and b/fist.png differ diff --git a/gesture_recognizer.task b/gesture_recognizer.task new file mode 100644 index 0000000..130d1d0 Binary files /dev/null and b/gesture_recognizer.task differ diff --git a/jarvis.png b/jarvis.png new file mode 100644 index 0000000..6481f5a Binary files /dev/null and b/jarvis.png differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..1ba4662 --- /dev/null +++ b/main.py @@ -0,0 +1,228 @@ +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() diff --git a/pointing.png b/pointing.png new file mode 100644 index 0000000..cc3f5d0 Binary files /dev/null and b/pointing.png differ diff --git a/pose_landmarker.task b/pose_landmarker.task new file mode 100644 index 0000000..09576a9 Binary files /dev/null and b/pose_landmarker.task differ diff --git a/rock.png b/rock.png new file mode 100644 index 0000000..a964c82 Binary files /dev/null and b/rock.png differ diff --git a/scorsese.png b/scorsese.png new file mode 100644 index 0000000..8a1dd4c Binary files /dev/null and b/scorsese.png differ diff --git a/shocked.png b/shocked.png new file mode 100644 index 0000000..b5c9c77 Binary files /dev/null and b/shocked.png differ diff --git a/test_api.py b/test_api.py new file mode 100644 index 0000000..169dd65 --- /dev/null +++ b/test_api.py @@ -0,0 +1,20 @@ +import cv2 +import mediapipe as mp +from mediapipe.tasks import python +from mediapipe.tasks.python import vision + +print("Imports successful") + +# Face Detector +base_options_face = python.BaseOptions(model_asset_path='face_detector.tflite') +options_face = vision.FaceDetectorOptions(base_options=base_options_face) +detector = vision.FaceDetector.create_from_options(options_face) +print("Face detector created") + +# Pose Landmarker +base_options_pose = python.BaseOptions(model_asset_path='pose_landmarker.task') +options_pose = vision.PoseLandmarkerOptions( + base_options=base_options_pose, + output_segmentation_masks=False) +landmarker = vision.PoseLandmarker.create_from_options(options_pose) +print("Pose landmarker created") diff --git a/timeout.png b/timeout.png new file mode 100644 index 0000000..1a231a2 Binary files /dev/null and b/timeout.png differ