Este tutorial explica cómo utilizar el script 1-reconocimento_facial.py, un sistema completo de reconocimiento facial desarrollado en Python que implementa dos algoritmos avanzados:
Antes de ejecutar el script, necesitas instalar las siguientes librerías de Python:
pip install opencv-python opencv-contrib-python scikit-learn numpy joblib
El sistema espera que las imágenes de entrenamiento estén organizadas de la siguiente manera:
rostros_dataset/
├── Persona1/
│ ├── foto1.jpg
│ ├── foto2.jpg
│ └── foto3.jpg
├── Persona2/
│ ├── foto1.jpg
│ └── foto2.jpg
└── PersonaN/
├── foto1.jpg
└── foto2.jpg
python 1-reconocimento_facial.py
Después de ejecutar el script, verás el siguiente menú:
🤖 Sistema de Reconocimiento Facial
========================================
✨ MEJORADO: Parámetros optimizados para reducir falsos positivos
Opciones:
1. Entrenar modelos
2. Reconocer rostros en imagen
3. Reconocimiento en tiempo real (webcam)
4. Configurar umbrales
5. Salir
Esta es la primera etapa. El sistema:
rostros_extraidos_inferidos/Permite revisar los parámetros actuales optimizados para reducir falsos positivos:
Rápido y eficiente para datasets pequeños. Funciona comparando texturas locales de los rostros.
Más preciso para datasets grandes. Combina descriptores HOG (Histogram of Oriented Gradients) con Support Vector Machines.
Durante el uso del sistema, se generan varios archivos:
modelo_lbph.yml - Modelo LBPH entrenadomodelo_svm.pkl - Modelo SVM entrenadolabel_encoder.pkl - Codificador de etiquetaspersonas.pkl - Lista de nombres de personasrostros_extraidos_inferidos/ - Carpeta con rostros extraídos durante el reconocimientoSolución: Ejecuta primero la opción 1 para entrenar el sistema.
Solución: Verifica que las imágenes contengan rostros visibles y bien iluminados.
Solución: Ajusta los umbrales en la opción 4, o agrega más fotos al dataset.
A continuación se muestra el código completo del sistema de reconocimiento facial:
'''
Características principales:
Dos algoritmos de reconocimiento:
LBPH (Local Binary Pattern Histograms): Rápido y eficiente
SVM con características HOG: Más preciso para datasets grandes
Funcionalidades:
Entrenamiento automático desde carpetas organizadas
Detección y reconocimiento en imágenes estáticas
Reconocimiento en tiempo real con webcam
Interfaz de menú fácil de usar
Extracción automática de rostros reconocidos a archivos con nombre del identificado
Redimensionamiento automático de imágenes grandes (máximo 512px ancho) para mejor visualización
Estructura del dataset:
rostros_dataset/
├── persona1/
│ ├── foto1.jpg
│ └── foto2.jpg
├── persona2/
│ ├── foto1.jpg
│ └── foto2.jpg
└── ...
Dependencias necesarias:
pip install opencv-python opencv-contrib-python scikit-learn numpy joblib
Cómo usar:
Preparar dataset: Crea carpetas con nombres de personas y coloca sus fotos dentro
Entrenar: Ejecuta el script y selecciona la opción 1
Reconocer: Usa las opciones 2 o 3 para reconocimiento en imágenes o tiempo real
El sistema guarda automáticamente los modelos entrenados y puede reconocer rostros con métricas de confianza
'''
import cv2
import numpy as np
import os
import pickle
import time
from sklearn.svm import SVC
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import joblib
class FaceRecognitionTrainer:
def __init__(self, dataset_path="rostros_dataset"):
self.dataset_path = dataset_path
self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
self.recognizer = cv2.face.LBPHFaceRecognizer_create()
self.label_encoder = LabelEncoder()
self.svm_model = SVC(kernel='rbf', probability=True, random_state=42)
def create_dataset_structure(self):
"""Crea la estructura de carpetas para el dataset"""
if not os.path.exists(self.dataset_path):
os.makedirs(self.dataset_path)
print(f"Carpeta {self.dataset_path} creada.")
print("Por favor, crea subcarpetas con los nombres de las personas")
print("y coloca las imágenes de cada persona en su respectiva carpeta.")
def extract_faces_from_image(self, image_path, target_size=(100, 100)):
"""Extrae rostros de una imagen"""
image = cv2.imread(image_path)
if image is None:
return []
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = self.face_cascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30)
)
face_images = []
for (x, y, w, h) in faces:
face = gray[y:y+h, x:x+w]
face_resized = cv2.resize(face, target_size)
face_images.append(face_resized)
return face_images
def load_training_data(self):
"""Carga las imágenes de entrenamiento desde el dataset"""
faces = []
labels = []
person_names = []
print("Cargando imágenes de entrenamiento...")
for person_name in os.listdir(self.dataset_path):
person_path = os.path.join(self.dataset_path, person_name)
if not os.path.isdir(person_path):
continue
person_names.append(person_name)
print(f"Procesando imágenes de: {person_name}")
for image_name in os.listdir(person_path):
if image_name.lower().endswith(('.png', '.jpg', '.jpeg')):
image_path = os.path.join(person_path, image_name)
face_images = self.extract_faces_from_image(image_path)
for face in face_images:
faces.append(face)
labels.append(person_name)
if len(faces) == 0:
raise ValueError("No se encontraron rostros en el dataset")
print(f"Se cargaron {len(faces)} rostros de {len(set(labels))} personas")
return faces, labels, person_names
def train_lbph_model(self, faces, labels):
"""Entrena el modelo LBPH (Local Binary Pattern Histograms)"""
print("Entrenando modelo LBPH...")
# Codificar labels
encoded_labels = self.label_encoder.fit_transform(labels)
# Entrenar el reconocedor LBPH
self.recognizer.train(faces, np.array(encoded_labels))
print("Modelo LBPH entrenado exitosamente")
def train_svm_model(self, faces, labels):
"""Entrena un modelo SVM usando características HOG"""
print("Entrenando modelo SVM con características HOG...")
# Extraer características HOG
hog = cv2.HOGDescriptor()
features = []
for face in faces:
# Redimensionar para HOG
face_resized = cv2.resize(face, (64, 128))
hog_features = hog.compute(face_resized)
features.append(hog_features.flatten())
features = np.array(features)
encoded_labels = self.label_encoder.fit_transform(labels)
# Dividir datos en entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(
features, encoded_labels, test_size=0.2, random_state=42
)
# Entrenar SVM
self.svm_model.fit(X_train, y_train)
# Evaluar modelo
y_pred = self.svm_model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Precisión del modelo SVM: {accuracy:.2f}")
def save_models(self):
"""Guarda los modelos entrenados"""
print("Guardando modelos...")
# Guardar modelo LBPH
self.recognizer.save('modelo_lbph.yml')
# Guardar modelo SVM y encoder
joblib.dump(self.svm_model, 'modelo_svm.pkl')
joblib.dump(self.label_encoder, 'label_encoder.pkl')
# Guardar nombres de personas
with open('personas.pkl', 'wb') as f:
pickle.dump(self.label_encoder.classes_, f)
print("Modelos guardados exitosamente")
def train_complete_system(self):
"""Entrena el sistema completo de reconocimiento facial"""
try:
# Crear estructura de dataset
self.create_dataset_structure()
# Verificar que existan imágenes
if not any(os.path.isdir(os.path.join(self.dataset_path, item))
for item in os.listdir(self.dataset_path)):
print("⚠️ No se encontraron carpetas de personas en el dataset.")
print("Crea carpetas con nombres de personas y coloca sus imágenes dentro.")
return False
# Cargar datos de entrenamiento
faces, labels, person_names = self.load_training_data()
# Entrenar ambos modelos
self.train_lbph_model(faces, labels)
self.train_svm_model(faces, labels)
# Guardar modelos
self.save_models()
print("\n✅ Entrenamiento completado exitosamente!")
print(f"📊 Resumen:")
print(f" - Personas entrenadas: {len(person_names)}")
print(f" - Total de rostros: {len(faces)}")
print(f" - Modelos guardados: LBPH y SVM")
return True
except Exception as e:
print(f"❌ Error durante el entrenamiento: {str(e)}")
return False
class FaceRecognizer:
def __init__(self):
self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# Ajustes para reducir falsos positivos
self.MIN_FACE_SIZE = 50 # Tamaño mínimo del rostro en píxeles
self.LBPH_CONFIDENCE_THRESHOLD = 80 # Umbral de confianza para LBPH (menor = más estricto)
self.SVM_PROBABILITY_THRESHOLD = 0.6 # Umbral de probabilidad para SVM (mayor = más estricto)
# Parámetros más estrictos para detección facial
self.DETECTION_SCALE_FACTOR = 1.2 # Mayor escala = menos falsos positivos
self.DETECTION_MIN_NEIGHBORS = 8 # Mayor vecino mínimo = más estricto
self.load_models()
def load_models(self):
"""Carga los modelos entrenados"""
try:
# Cargar modelo LBPH
self.recognizer = cv2.face.LBPHFaceRecognizer_create()
self.recognizer.read('modelo_lbph.yml')
# Cargar modelo SVM y encoder
self.svm_model = joblib.load('modelo_svm.pkl')
self.label_encoder = joblib.load('label_encoder.pkl')
# Cargar nombres de personas
with open('personas.pkl', 'rb') as f:
self.person_names = pickle.load(f)
print("Modelos cargados exitosamente")
except FileNotFoundError:
print("❌ No se encontraron modelos entrenados. Ejecuta primero el entrenamiento.")
def recognize_faces_in_image(self, image_path, method='lbph'):
"""Reconoce rostros en una imagen con parámetros optimizados para reducir falsos positivos"""
image = cv2.imread(image_path)
if image is None:
print("No se pudo cargar la imagen")
return None
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = self.face_cascade.detectMultiScale(
gray,
scaleFactor=self.DETECTION_SCALE_FACTOR,
minNeighbors=self.DETECTION_MIN_NEIGHBORS,
minSize=(self.MIN_FACE_SIZE, self.MIN_FACE_SIZE)
)
# Crear directorio para rostros extraídos si no existe
output_dir = "rostros_extraidos_inferidos"
os.makedirs(output_dir, exist_ok=True)
recognized_faces = 0
face_counter = {} # Contador para cada persona
for (x, y, w, h) in faces:
# Verificar tamaño mínimo del rostro
if w < self.MIN_FACE_SIZE or h < self.MIN_FACE_SIZE:
continue
face = gray[y:y+h, x:x+w]
if method == 'lbph':
# Usar modelo LBPH con control de confianza
face_resized = cv2.resize(face, (100, 100))
label, confidence = self.recognizer.predict(face_resized)
# Rechazar si confianza es baja (valores altos indican baja confianza en LBPH)
if confidence > self.LBPH_CONFIDENCE_THRESHOLD:
continue
person_name = self.label_encoder.inverse_transform([label])[0]
confidence_text = f"{confidence:.1f}"
elif method == 'svm':
# Usar modelo SVM con control de probabilidad
face_resized = cv2.resize(face, (64, 128))
hog = cv2.HOGDescriptor()
features = hog.compute(face_resized).flatten().reshape(1, -1)
prediction = self.svm_model.predict(features)[0]
person_name = self.label_encoder.inverse_transform([prediction])[0]
probability = self.svm_model.predict_proba(features)[0].max()
# Rechazar si probabilidad es baja
if probability < self.SVM_PROBABILITY_THRESHOLD:
continue
confidence_text = f"{probability:.2f}"
# Inicializar contador para nueva persona
if person_name not in face_counter:
face_counter[person_name] = 0
face_counter[person_name] += 1
# Extraer rostro de la imagen original (imagen a color)
face_color = image[y:y+h, x:x+w]
# Crear nombre de archivo con nombre del reconocido, contador y timestamp
timestamp = int(time.time())
filename = f"{person_name}_{face_counter[person_name]}_{timestamp}.jpg"
output_path = os.path.join(output_dir, filename)
# Guardar el rostro extraído
cv2.imwrite(output_path, face_color)
print(f"✓ Rostro guardado: {output_path}")
# Dibujar rectángulo y nombre
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(image, f"{person_name} ({confidence_text}) - Guardado",
(x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
recognized_faces += 1
print(f"Se reconocieron {recognized_faces} rostros válidos en la imagen")
if recognized_faces > 0:
print(f"💾 Rostros extraídos guardados en carpeta '{output_dir}'")
# Redimensionar la imagen para visualización si es muy grande
display_image = image.copy()
height, width = display_image.shape[:2]
# Limitar ancho máximo a 512 píxeles manteniendo relación de aspecto
max_width = 512
if width > max_width:
ratio = max_width / width
new_width = int(width * ratio)
new_height = int(height * ratio)
display_image = cv2.resize(display_image, (new_width, new_height))
print(f"Imagen redimensionada para visualización: {new_width}x{new_height}")
return display_image
def recognize_from_webcam(self, method='lbph'):
"""Reconocimiento en tiempo real desde la webcam con parámetros optimizados para reducir falsos positivos"""
cap = cv2.VideoCapture(0)
print("Presiona 'q' para salir")
print("Algoritmo:", method.upper())
print("Parámetros anti-falsos-positivos activados")
print("Presiona 's' para guardar rostros reconocidos manualmente")
# Crear directorio para rostros extraídos si no existe
output_dir = "rostros_extraidos_inferidos"
os.makedirs(output_dir, exist_ok=True)
recognized_faces = 0
face_counter = {} # Contador para cada persona
last_save_time = 0 # Para controlar frecuencia de guardado automático
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Usar parámetros estrictos para detección
faces = self.face_cascade.detectMultiScale(
gray,
scaleFactor=self.DETECTION_SCALE_FACTOR,
minNeighbors=self.DETECTION_MIN_NEIGHBORS,
minSize=(self.MIN_FACE_SIZE, self.MIN_FACE_SIZE)
)
current_recognized = 0
for (x, y, w, h) in faces:
# Verificar tamaño mínimo adicional
if w < self.MIN_FACE_SIZE or h < self.MIN_FACE_SIZE:
continue
face = gray[y:y+h, x:x+w]
if method == 'lbph':
# Usar modelo LBPH con control de confianza
face_resized = cv2.resize(face, (100, 100))
label, confidence = self.recognizer.predict(face_resized)
# Rechazar si confianza es baja
if confidence > self.LBPH_CONFIDENCE_THRESHOLD:
# Dibujar rectángulo rojo para rostro detectado pero no reconocido
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.putText(frame, f"Desconocido ({confidence:.1f})",
(x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
continue
person_name = self.label_encoder.inverse_transform([label])[0]
confidence_text = f"{confidence:.1f}"
elif method == 'svm':
# Usar modelo SVM con control de probabilidad
face_resized = cv2.resize(face, (64, 128))
hog = cv2.HOGDescriptor()
features = hog.compute(face_resized).flatten().reshape(1, -1)
prediction = self.svm_model.predict(features)[0]
person_name = self.label_encoder.inverse_transform([prediction])[0]
probability = self.svm_model.predict_proba(features)[0].max()
# Rechazar si probabilidad es baja
if probability < self.SVM_PROBABILITY_THRESHOLD:
# Dibujar rectángulo rojo para rostro detectado pero no reconocido
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.putText(frame, f"Desconocido ({probability:.2f})",
(x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
continue
confidence_text = f"{probability:.2f}"
# Inicializar contador para nueva persona si no existe
if person_name not in face_counter:
face_counter[person_name] = 0
# Dibujar rectángulo verde y nombre para reconocimiento exitoso
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame, f"{person_name} ({confidence_text})",
(x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
current_recognized += 1
recognized_faces += 1
# Preparar extracción (solo si se presiona 's' o automáticamente cada 30 segundos)
current_time = time.time()
if current_time - last_save_time > 30: # Guardado automático cada 30 segundos
face_counter[person_name] += 1
# Extraer rostro de la imagen original (imagen a color)
face_color = frame[y:y+h, x:x+w]
# Crear nombre de archivo con nombre del reconocido, contador y timestamp
timestamp = int(current_time)
filename = f"{person_name}_webcam_{face_counter[person_name]}_{timestamp}.jpg"
output_path = os.path.join(output_dir, filename)
# Guardar el rostro extraído
cv2.imwrite(output_path, face_color)
print(f"\n✓ Rostro guardado: {output_path}")
last_save_time = current_time
cv2.imshow('Reconocimiento Facial', frame)
print(f"\rRostros reconocidos en este frame: {current_recognized}", end='', flush=True)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('s'):
# Guardado manual por cada rostro reconocido
for person_name in list(face_counter.keys())[:current_recognized]: # Guardar solo rostros actuales
faces_in_frame = self.face_cascade.detectMultiScale(
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY),
scaleFactor=self.DETECTION_SCALE_FACTOR,
minNeighbors=self.DETECTION_MIN_NEIGHBORS,
minSize=(self.MIN_FACE_SIZE, self.MIN_FACE_SIZE)
)
for i, (x, y, w, h) in enumerate(faces_in_frame):
if i >= current_recognized: # Solo rostros reconocidos en este frame
break
face_color = frame[y:y+h, x:x+w]
face_counter[person_name] += 1
timestamp = int(time.time())
filename = f"{person_name}_manual_{face_counter[person_name]}_{timestamp}.jpg"
output_path = os.path.join(output_dir, filename)
cv2.imwrite(output_path, face_color)
print(f"\n✓ Rostro guardado manualmente: {output_path}")
last_save_time = time.time()
print("\n")
if recognized_faces > 0:
print(f"💾 Rostros extraídos guardados en carpeta '{output_dir}'")
cap.release()
cv2.destroyAllWindows()
def main():
print("🤖 Sistema de Reconocimiento Facial")
print("=" * 40)
print("✨ MEJORADO: Parámetros optimizados para reducir falsos positivos")
print(" - Umbrales de confianza estrictos")
print(" - Detección facial más precisa")
print(" - Rechazo automático de detecciones poco confiables")
while True:
print("\nOpciones:")
print("1. Entrenar modelos")
print("2. Reconocer rostros en imagen")
print("3. Reconocimiento en tiempo real (webcam)")
print("4. Configurar umbrales")
print("5. Salir")
choice = input("\nSelecciona una opción: ")
if choice == '1':
trainer = FaceRecognitionTrainer()
trainer.train_complete_system()
elif choice == '2':
recognizer = FaceRecognizer()
image_path = input("Ingresa la ruta de la imagen: ")
method = input("Método (lbph/svm) [lbph]: ").lower() or 'lbph'
result = recognizer.recognize_faces_in_image(image_path, method)
if result is not None:
cv2.imshow('Resultado', result)
print("Presiona cualquier tecla para continuar...")
cv2.waitKey(0)
cv2.destroyAllWindows()
elif choice == '3':
recognizer = FaceRecognizer()
method = input("Método (lbph/svm) [lbph]: ").lower() or 'lbph'
recognizer.recognize_from_webcam(method)
elif choice == '4':
# Crear un reconocedor temporal para mostrar configuración
temp_recognizer = FaceRecognizer()
print("\n⚙️ Configuración actual de parámetros anti-falsos-positivos:")
print(f" - Tamaño mínimo del rostro: {temp_recognizer.MIN_FACE_SIZE} píxeles")
print(f" - Umbral de confianza LBPH: {temp_recognizer.LBPH_CONFIDENCE_THRESHOLD}")
print(f" - Umbral de probabilidad SVM: {temp_recognizer.SVM_PROBABILITY_THRESHOLD}")
print(f" - Factor de escala de detección: {temp_recognizer.DETECTION_SCALE_FACTOR}")
print(f" - Vecinos mínimos de detección: {temp_recognizer.DETECTION_MIN_NEIGHBORS}")
print("\n💡 Recomendaciones:")
print(" - Para más precisión: Aumentar los umbrales")
print(" - Para más detecciones: Reducir los umbrales")
print(" - Valores bajos de LBPH indican mejor confianza")
print(" - Valores altos de SVM indican mejor confianza")
print("\n📝 Nota: Estos parámetros están optimizados para reducir falsos positivos")
print(" Considera ajustar si tienes muchos falsos negativos")
elif choice == '5':
print("¡Hasta luego!")
break
else:
print("Opción no válida")
if __name__ == "__main__":
main()
Este sistema de reconocimiento facial ofrece una solución completa y avanzada para identificar personas mediante algoritmos de machine learning. Con su interfaz fácil de usar y capacidades tanto para análisis de imágenes estáticas como en tiempo real, resulta ideal para una variedad de aplicaciones práctica.