Files

229 lines
9.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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():
# Загружаем маски из папки memes
mask_timeout = load_mask('memes/timeout.png')
mask_pointing = load_mask('memes/pointing.png')
mask_cinema = load_mask('memes/scorsese.png')
mask_shocked = load_mask('memes/shocked.png')
mask_fist = load_mask('memes/fist.png')
mask_jarvis = load_mask('memes/jarvis.png')
mask_bye = load_mask('memes/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()