вариант 6 эмоций

This commit is contained in:
2026-09-11 16:19:42 +05:00
commit 43978f9423
14 changed files with 248 additions and 0 deletions
+228
View File
@@ -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()