Este repositorio contiene la Práctica 5 donde se utilizan técnicas de procesamiento de imágenes y aprendizaje automático para la detección y caracterización de caras humanas. Se implementan diferentes detectores faciales y se realizan análisis faciales utilizando conjuntos de datos y modelos preentrenados.
Índice
- Práctica 5. Detección y caracterización de caras
- Índice
- Librerías utilizadas
- Autores
- Minijuego: Come Manzanas
- Como se Juega
- Resultado
- Explicación Del Código
- Importación de librerías y módulos necesarios
- Inicialización de Mediapipe para detección de manos y rostro
- Carga de imágenes de recursos (manzana, piedra y vida)
- Carga de la imagen de la boca gigante
- Parámetros del juego
- Configuración de la captura de video
- Función para superponer imágenes con transparencia
- Clase GameObject
- Métodos de GameObject
- Variables para el estado de la boca y la caja del rostro
- Bucle principal del juego
- Procesamiento del frame
- Procesamiento de detección de manos y rostro
- Obtener dimensiones del frame
- Reiniciar variables
- Detección y procesamiento de manos
- Detección y procesamiento del rostro y la boca
- Estado inicial del juego: esperando el gesto de inicio
- Lógica principal del juego cuando está en marcha
- Generación de nuevas manzanas y piedras
- Actualización y dibujo de manzanas
- Manzanas dirigiéndose hacia la boca
- Lógica similar para las piedras
- Dibujo de las vidas (corazones)
- Manejo del fin del juego
- Mostrar el frame y manejar la entrada de teclado
- Cierre de la aplicación
- Filtro: Detección de Caras y Análisis de Emociones
- Referencias y bibliografía
Este proyecto fue desarrollado por:
- Inicia el juego y asegúrate de estar en un entorno con buena iluminación.
- Junta tus manos frente a la cámara para empezar (palma con palma).
- Comenzarán a caer Manzanas y Piedras del cielo.
- Tu objetivo es:
- Comer las Manzanas y evitar que caigan al suelo.
- Evitar comer las Piedras.
- Puedes tocar las manzanas y las piedras con las manos. Esto genera un efecto que las dirige hacia tu boca.
- Reglas adicionales de interacción:
- Manzanas:
- Si una manzana se dirige a tu boca y NO la comes, pierdes una vida.
- Piedras:
- Si una piedra se dirige a tu boca y la comes, pierdes una vida.
- Manzanas:
- Tienes un total de 3 vidas. Si las pierdes todas, el juego termina.
![]() |
⚠️ Importante: Además de las librerías que se utilizan en la prática 5 debes instalar mediapipe (pip install mediapipe)
import cv2
import time
import mediapipe as mp
import numpy as np
import random
- cv2: Librería OpenCV para procesamiento de imágenes y video.
- time: Para manejar tiempos y pausas en el juego.
- mediapipe as mp: Librería de Google para detección y seguimiento de manos y rostro.
- numpy as np: Para operaciones numéricas y matrices.
- random: Para generar números aleatorios (posición de manzanas y piedras).
mp_hands = mp.solutions.hands
mp_face_mesh = mp.solutions.face_mesh
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=2,
min_detection_confidence=0.5, min_tracking_confidence=0.5)
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False, max_num_faces=1,
min_detection_confidence=0.5, min_tracking_confidence=0.5)
- mp_hands y mp_face_mesh: Inicializan los módulos de Mediapipe para manos y rostro.
- hands: Configura el detector de manos.
- static_image_mode=False: Optimiza para video en tiempo real.
- max_num_hands=2: Detecta hasta 2 manos.
- min_detection_confidence y min_tracking_confidence: Umbrales de confianza para detección y seguimiento.
- face_mesh: Configura el detector de malla facial.
- max_num_faces=1: Detecta solo un rostro.
# Cargar la imagen de la manzana
apple_img = cv2.imread('apple.png', cv2.IMREAD_UNCHANGED)
apple_img = cv2.resize(apple_img, (50, 50)) # Ajustar el tamaño de la manzana
- cv2.imread: Carga la imagen con transparencia (canal alfa) si está disponible.
- cv2.resize: Ajusta el tamaño de la imagen para que tenga dimensiones manejables en el juego.
Si no se puede cargar la imagen (por ejemplo, si el archivo no existe), se crea una representación por defecto:
if apple_img is None or apple_img.shape[2] != 4:
apple_img = np.zeros((50, 50, 4), dtype=np.uint8)
cv2.circle(apple_img, (25, 25), 25, (0, 0, 255, 255), -1)
- Crea una imagen vacía con 4 canales (RGBA).
- Dibuja un círculo rojo (como una manzana) en el centro.
Lo mismo se hace para la piedra y el corazón (vida), adaptando las formas y colores correspondientes.
big_mouth_img = cv2.imread('comemanzanas.png', cv2.IMREAD_UNCHANGED)
big_mouth_original_size = (150, 150) # Tamaño por defecto si no se puede cargar
if big_mouth_img is None or big_mouth_img.shape[2] != 4:
big_mouth_img = np.zeros((big_mouth_original_size[1], big_mouth_original_size[0], 4), dtype=np.uint8)
cv2.circle(big_mouth_img, (big_mouth_original_size[0]//2, big_mouth_original_size[1]//2),
min(big_mouth_original_size)//2, (0, 0, 255, 255), -1) # Círculo rojo por defecto
- Carga la imagen que se mostrará cuando la boca esté abierta.
- Si no se encuentra, crea una imagen por defecto (un círculo rojo).
lives = 3 # Vidas del jugador
apples = [] # Lista de manzanas en pantalla
stones = [] # Lista de piedras en pantalla
# Intervalos de aparición
apple_spawn_interval = 5 # Segundos entre apariciones de manzanas
stone_spawn_interval = 8 # Segundos entre apariciones de piedras
# Tiempos de última aparición
last_apple_spawn_time = 0
last_stone_spawn_time = 0
game_started = False # Indicador de inicio del juego
game_over = False # Indicador de fin del juego
start_time = time.time() # Tiempo de inicio del juego
# Dificultad
difficulty_increase_interval = 15 # Cada 15 segundos aumenta la dificultad
apple_fall_speed = 2 # Velocidad inicial de caída de las manzanas
stone_fall_speed = 2 # Velocidad inicial de caída de las piedras
# Límites en pantalla
max_apples_on_screen = 1 # Número máximo inicial de manzanas en pantalla
max_stones_on_screen = 0 # Las piedras comienzan a aparecer después
- Se definen las variables que controlarán el estado del juego, la dificultad y los objetos en pantalla.
cap = cv2.VideoCapture(0)
if not cap.isOpened():
cap = cv2.VideoCapture(1)
if not cap.isOpened():
print('Error al abrir la cámara')
exit(0)
- cv2.VideoCapture(0): Intenta abrir la cámara principal.
- Si no funciona, intenta con cv2.VideoCapture(1) (otra cámara).
- Si no se puede abrir ninguna, muestra un mensaje de error y sale.
def overlay_image_alpha(img, img_overlay, pos):
# ...
- Esta función permite superponer una imagen con canal alfa (transparencia) sobre otra.
- img: Imagen de fondo (frame actual).
- img_overlay: Imagen a superponer (manzana, piedra, boca gigante).
- pos: Posición (x, y) donde se superpondrá la imagen.
La función maneja casos donde la imagen se sale de los límites y mezcla los canales de color teniendo en cuenta la transparencia.
class GameObject:
def __init__(self, x, y, fall_speed, obj_type='apple'):
# ...
- Representa objetos en el juego: manzanas y piedras.
- Atributos:
- x, y: Posición actual del objeto.
- state: Estado del objeto ('falling' o 'to_mouth').
- fall_speed: Velocidad de caída.
- path: Trayectoria cuando se dirige hacia la boca.
- current_path_index: Índice actual en la trayectoria.
- type: Tipo de objeto ('apple' o 'stone').
-
update_position(self):
- Actualiza la posición del objeto.
- Si está cayendo, incrementa y según la velocidad de caída.
- Si está moviéndose hacia la boca, sigue la trayectoria predefinida.
- Devuelve 'continue' si debe seguir actualizándose, o 'reached_mouth' si ha llegado a la boca.
-
generate_parabola(self, mouth_x, mouth_y):
- Genera una trayectoria parabólica desde la posición actual hasta la boca.
- Calcula puntos intermedios para simular el movimiento hacia la boca.
mouth_open = False # Indica si la boca está abierta
current_face_bbox = None # Almacena la caja delimitadora del rostro
while True:
ret, frame = cap.read()
if not ret:
break
# ...
- Se inicia un bucle infinito que captura frames de la cámara.
- Si no se puede leer un frame, se rompe el bucle.
# Flip horizontal para efecto espejo
frame = cv2.flip(frame, 1)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
- cv2.flip(frame, 1): Invierte horizontalmente el frame para que actúe como un espejo.
- cv2.cvtColor: Convierte el frame de BGR a RGB, formato que requiere Mediapipe.
# Procesar detección de manos
hand_results = hands.process(rgb_frame)
# Procesar detección de rostro
face_results = face_mesh.process(rgb_frame)
- Se procesan las detecciones de manos y rostro utilizando Mediapipe.
h, w, _ = frame.shape
- Se extraen las dimensiones del frame para escalado y posicionamiento.
current_face_bbox = None
hand_positions = []
mouth_coords = {}
- current_face_bbox: Se reinicia la caja delimitadora del rostro.
- hand_positions: Lista para almacenar las posiciones de las manos detectadas.
- mouth_coords: Diccionario para almacenar las coordenadas de la boca.
if hand_results.multi_hand_landmarks:
for hand_landmarks in hand_results.multi_hand_landmarks:
# Obtener coordenadas de los puntos de la mano
for lm in hand_landmarks.landmark:
x = int(lm.x * w)
y = int(lm.y * h)
hand_positions.append((x, y))
# Dibujar puntos de la mano
mp.solutions.drawing_utils.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
- Si se detectan manos, se recorren los puntos de referencia (landmarks) de cada mano.
- Se convierten las coordenadas normalizadas (0 a 1) a píxeles multiplicando por el ancho y alto del frame.
- Se almacenan las posiciones en hand_positions.
- Se dibujan las conexiones de la mano en el frame para visualización.
if face_results.multi_face_landmarks:
for face_landmarks in face_results.multi_face_landmarks:
# Obtener coordenadas de los labios
lip_top = face_landmarks.landmark[13]
lip_bottom = face_landmarks.landmark[14]
lip_left = face_landmarks.landmark[78]
lip_right = face_landmarks.landmark[308]
# ...
- Se extraen puntos específicos de los labios superiores e inferiores y las comisuras.
- Se convierten las coordenadas a píxeles.
# Calcular apertura de la boca
mouth_opening = abs(top_lip_y - bottom_lip_y)
# Umbral para considerar boca abierta
if mouth_opening > 7:
mouth_open = True
else:
mouth_open = False
- Se calcula la distancia vertical entre el labio superior e inferior.
- Si esta distancia supera un umbral (7 píxeles), se considera que la boca está abierta.
# Coordenadas de la boca
mouth_coords = {'x': int((left_lip_x + right_lip_x) / 2),
'y': int((top_lip_y + bottom_lip_y) / 2)}
- Se calcula el centro de la boca promediando las coordenadas horizontales y verticales.
cv2.circle(frame, (mouth_coords['x'], mouth_coords['y']), 2, (0, 255, 0), -1)
- Se dibuja un pequeño círculo verde en el centro de la boca para visualización.
x_coords = [int(lm.x * w) for lm in face_landmarks.landmark]
y_coords = [int(lm.y * h) for lm in face_landmarks.landmark]
min_x, max_x = min(x_coords), max(x_coords)
min_y, max_y = min(y_coords), max(y_coords)
- Se obtienen todas las coordenadas x e y de los puntos del rostro.
- Se calcula el mínimo y máximo para definir la caja delimitadora.
current_face_bbox = (min_x, min_y, max_x, max_y)
- Se guarda la caja delimitadora para uso posterior (por ejemplo, cuando los objetos se dirigen hacia la boca).
# Ajustar el tamaño de la boca gigante según el rostro
big_mouth_width = int(face_width * 1.6)
big_mouth_height = int(face_height * 1.6)
resized_big_mouth = cv2.resize(big_mouth_img, (big_mouth_width, big_mouth_height))
# Calcular posición para centrar la boca gigante
mouth_center_x = int((min_x + max_x) / 2)
mouth_center_y = int((min_y + max_y) / 2)
top_left_x = mouth_center_x - big_mouth_width // 2
top_left_y = mouth_center_y - big_mouth_height // 2
# Superponer la boca gigante si la boca está abierta
if mouth_open:
frame = overlay_image_alpha(frame, resized_big_mouth, (top_left_x, top_left_y))
- Se ajusta el tamaño de la imagen de la boca gigante para que se adapte al tamaño del rostro detectado.
- Se calcula la posición superior izquierda para centrar la boca gigante en el rostro.
- Si la boca está abierta, se superpone la imagen de la boca gigante sobre el frame.
if not game_started and not game_over:
if hand_results.multi_hand_landmarks and len(hand_results.multi_hand_landmarks) == 2:
# Calcular distancia entre las dos primeras posiciones detectadas
x1, y1 = hand_positions[0]
x2, y2 = hand_positions[1]
palm_distance = np.hypot(x2 - x1, y2 - y1)
# Si las palmas están cerca (umbral ajustable)
if palm_distance < 27:
game_started = True
start_time = time.time()
else:
cv2.putText(frame, "Junta tus manos para comenzar", (50, 50), cv2.FONT_HERSHEY_SIMPLEX,
1, (255, 255, 255), 2)
- Si el juego no ha comenzado, se espera a que el jugador junte sus manos.
- Se detecta si hay dos manos y se calcula la distancia entre las dos primeras posiciones detectadas.
- Si la distancia es menor que un umbral (27 píxeles), se considera que las manos están juntas y el juego comienza.
- Si no, se muestra el mensaje "Junta tus manos para comenzar".
if game_started and not game_over:
current_time = time.time()
elapsed_time = int(current_time - start_time)
# Mostrar temporizador
cv2.putText(frame, f"Tiempo: {elapsed_time}s", (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1, (255, 255, 255), 2)
# Aumentar dificultad cada cierto tiempo
# ...
- Se calcula el tiempo transcurrido desde que inició el juego.
- Se muestra el temporizador en pantalla.
- Se incrementa la dificultad cada difficulty_increase_interval segundos:
- Aumenta la velocidad de caída de manzanas y piedras.
- Disminuye el intervalo de aparición de manzanas.
- Aumenta el número máximo de manzanas y piedras en pantalla.
# Generar nuevas manzanas si es necesario
if len(apples) < max_apples_on_screen and current_time - last_apple_spawn_time > apple_spawn_interval:
apple_x = random.randint(25, w - 25)
apples.append(GameObject(apple_x, -25, apple_fall_speed, obj_type='apple'))
last_apple_spawn_time = current_time
- Si hay menos manzanas en pantalla que el máximo permitido y ha pasado suficiente tiempo desde la última aparición, se genera una nueva manzana en una posición horizontal aleatoria.
Lo mismo se hace para las piedras, pero comienzan a aparecer después de 20 segundos de juego.
for apple in apples[:]:
status = apple.update_position()
# Dibujar manzana
frame = overlay_image_alpha(frame, apple_img, (apple.x - 25, apple.y - 25))
# Lógica de colisiones y estado de la manzana
# ...
- Se itera sobre la lista de manzanas y se actualiza su posición.
- Se dibuja la manzana en su posición actual.
- Se verifica si la manzana ha tocado el suelo, en cuyo caso se resta una vida y se elimina la manzana.
- Se verifica si la manzana colisiona con una mano:
- Si es así y la boca está abierta, se genera una trayectoria parabólica hacia la boca.
- El estado de la manzana cambia a 'to_mouth'.
- Si la manzana está en estado 'to_mouth', se actualiza su posición siguiendo la trayectoria generada.
- Si la manzana ha llegado a la boca:
- Si la boca está abierta, la manzana se consume.
- Si la boca está cerrada, se resta una vida.
-
Las piedras se manejan de manera similar a las manzanas, pero:
- No restan vidas si caen al suelo.
- Si una piedra llega a la boca, resta una vida.
- Las piedras comienzan a aparecer después de cierto tiempo.
for i in range(lives):
frame = overlay_image_alpha(frame, life_img, (w - (i + 1) * 40, 10))
- Se dibujan tantos corazones como vidas restantes en la esquina superior derecha.
if game_over:
cv2.putText(frame, "Juego Terminado", (w // 2 - 200, h // 2 - 30), cv2.FONT_HERSHEY_SIMPLEX,
1.5, (0, 0, 255), 3)
cv2.putText(frame, f"Tiempo total: {total_time}s", (w // 2 - 150, h // 2 + 10), cv2.FONT_HERSHEY_SIMPLEX,
1, (255, 255, 255), 2)
cv2.putText(frame, "Presiona 'R' para reiniciar", (w // 2 - 200, h // 2 + 50), cv2.FONT_HERSHEY_SIMPLEX,
1, (255, 255, 255), 2)
- Si el juego ha terminado (vidas agotadas), se muestra un mensaje de fin de juego, el tiempo total jugado y la opción de reiniciar presionando 'R'.
cv2.imshow('Minijuego: Come la Manzana', frame)
# Esperar por tecla de salida
tec = cv2.waitKey(1)
if tec & 0xFF == 27: # Esc para salir
break
elif tec & 0xFF == ord('r') and game_over:
# Reiniciar juego
# ...
- Se muestra el frame actualizado en una ventana llamada 'Minijuego: Come la Manzana'.
- Se espera por una entrada de teclado:
- Si se presiona 'Esc' (código ASCII 27), se sale del juego.
- Si se presiona 'R' y el juego ha terminado, se reinicia el juego reseteando todas las variables a sus valores iniciales.
# Cerrar cámara y ventanas
cap.release()
cv2.destroyAllWindows()
- Al salir del bucle principal, se libera la cámara y se cierran todas las ventanas de OpenCV.
Esta sección describe una aplicación que detecta caras en tiempo real usando RetinaFace
y analiza emociones con DeepFace
. Según los puntos faciales detectados, la app superpone filtros (gafas, bigote y sombrero) y muestra la emoción dominante en pantalla.
- Detección en Tiempo Real con
RetinaFace
. - Análisis de Emociones con
DeepFace
. - Filtros Personalizables: Gafas, bigote y sombrero basados en puntos faciales.
- Interactividad: Permite activar y desactivar filtros en tiempo real.
Para usar esta sección, instala las dependencias con:
pip install opencv-python numpy retinaface deepface
Coloca en el directorio las imágenes gafas.png
, bigote.png
y sombrero.png
.
Ejecuta la aplicación con:
python fun_filter.py
Esc
: Salir de la aplicación.d
: Activar/desactivar modo de depuración.g
,m
,h
: Alternar gafas, bigote y sombrero.
-
Carga de Librerías
import cv2 import numpy as np import time from retinaface import RetinaFace from deepface import DeepFace import threading
-
Detección de Caras y Emociones
-
Detección de Caras:
def detect_faces(frame): return RetinaFace.detect_faces(frame)
-
Análisis de Emociones:
def analyze_emotions(face_roi, emotion_queue): # Implementación de análisis de emociones
-
-
Superposición de Filtros
def overlay_image(frame, overlay, position): # Implementación de superposición
-
Bucle Principal
while True: # Procesa el frame, aplica filtros y detecta emociones # Muestra FPS, número de caras y emoción dominante
![]() |
- OpenCV Documentation: docs.opencv.org
- Matplotlib Documentation: matplotlib.org
- Imutils Documentation: github.com/jrosebr1/imutils
- MTCNN Documentation: github.com/ipazc/mtcnn
- TensorFlow Documentation: tensorflow.org
- DeepFace Documentation: github.com/serengil/deepface
- Dlib Documentation: dlib.net
- Scikit-learn Documentation: scikit-learn.org
- Scikit-image Documentation: scikit-image.org
- MediaPipe Documentation: mediapipe.dev