nerdemma

biometrical

Aug 26th, 2025 (edited)
312
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.11 KB | Source Code | 0 0
  1. #== Biometrial V.0.1.2 Desarrollado Por @nerdemma ==
  2. #== https://github.com/nerdemma ===
  3. #== https://emmanuelbreyaue.com ===
  4.  
  5. import os
  6. import shutil
  7. import cv2
  8. import sys
  9. import multiprocessing
  10. from datetime import datetime
  11. from concurrent.futures import ThreadPoolExecutor, as_completed
  12. from pathlib import Path
  13.  
  14. # == Parámetros y configuración ==
  15. HAAR_CASCADE_PATH = 'haarcascade_frontalface_default.xml'
  16. SUPPORTED_EXT = ('.jpg', '.jpeg', '.png')
  17. TEMP_RESIZE_DIM = 480
  18.  
  19. # Nombres de subcarpetas dentro del directorio de destino
  20. FACES_SUBDIR = 'faces'
  21. OBJECTS_SUBDIR = 'objects'
  22. LOGS_SUBDIR = 'logs'
  23.  
  24. # === Cargar detector de caras Haar Cascade de OpenCV ===
  25. try:
  26.     detector = cv2.CascadeClassifier(HAAR_CASCADE_PATH)
  27.     if detector.empty():
  28.         raise IOError(f"❌ Error: El archivo '{HAAR_CASCADE_PATH}' no se pudo cargar o está vacío.")
  29.     print("✅ Detector de caras Haar Cascade de OpenCV cargado correctamente.")
  30. except IOError as e:
  31.     print(e)
  32.     sys.exit(1)
  33.  
  34. # ---
  35. ## Funciones de ayuda
  36.  
  37. def resize_for_detection(image, max_dim):
  38.     """
  39.    Redimensiona la imagen para que su dimensión más grande sea 'max_dim'.
  40.    Evita que las dimensiones resultantes sean cero o negativas.
  41.    """
  42.     h, w = image.shape[:2]
  43.    
  44.     if max(h, w) > max_dim:
  45.         scale = max_dim / max(h, w)
  46.         new_w, new_h = int(w * scale), int(h * scale)
  47.        
  48.         # Asegurarse de que las dimensiones sean válidas
  49.         if new_w <= 0 or new_h <= 0:
  50.             raise ValueError("Las dimensiones de redimensionamiento calculadas no son válidas.")
  51.        
  52.         return cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)
  53.    
  54.     return image
  55.  
  56. def get_all_images(root_dir):
  57.     """Obtiene todas las imágenes de un directorio de forma recursiva."""
  58.     root_path = Path(root_dir)
  59.     return [str(p) for p in root_path.rglob('*') if p.suffix.lower() in SUPPORTED_EXT]
  60.  
  61. # ---
  62. ## Lógica principal de procesamiento
  63.  
  64. def process_image(file_path, dest_root):
  65.     """
  66.    Procesa una imagen individual, la clasifica y la mueve a su destino.
  67.    Devuelve un mensaje de estado y una entrada de log.
  68.    """
  69.     filename = Path(file_path).name
  70.     dest_path_faces = Path(dest_root) / FACES_SUBDIR / filename
  71.     dest_path_objects = Path(dest_root) / OBJECTS_SUBDIR / filename
  72.  
  73.     # Si la imagen ya está en el destino, no la procesa de nuevo.
  74.     if dest_path_faces.exists() or dest_path_objects.exists():
  75.         return f"[{filename}] ⚠️ Archivo ya en destino, saltando.", None
  76.  
  77.     original_image = cv2.imread(file_path)
  78.     if original_image is None:
  79.         return f"[{filename}] ❌ Error al cargar la imagen.", None
  80.    
  81.     h, w = original_image.shape[:2]
  82.  
  83.     try:
  84.         # Redimensionar temporalmente para el análisis
  85.         resized_image = resize_for_detection(original_image, TEMP_RESIZE_DIM)
  86.        
  87.         # Convertir a escala de grises para la detección (más eficiente)
  88.         gray_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY)
  89.        
  90.         # Detectar caras usando Haar Cascade
  91.         faces = detector.detectMultiScale(gray_image, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
  92.  
  93.         # Determinar destino y mover el archivo
  94.         if len(faces) > 0:
  95.             dest_dir = Path(dest_root) / FACES_SUBDIR
  96.             resultado = f"Cara(s) detectada(s): {len(faces)}"
  97.         else:
  98.             dest_dir = Path(dest_root) / OBJECTS_SUBDIR
  99.             resultado = "Sin rostro"
  100.        
  101.         dest_path = dest_dir / filename
  102.         os.makedirs(dest_dir, exist_ok=True)
  103.         shutil.move(file_path, dest_path)
  104.  
  105.         return f"[{filename}] ✅ Procesado: {resultado} | Resolución original: {w}x{h}", resultado
  106.  
  107.     except (cv2.error, ValueError, Exception) as e:
  108.         return f"[{filename}] ⚠️ Error de procesamiento: {e}", None
  109.  
  110. # ---
  111. ## Bucle principal del script
  112.  
  113. def main():
  114.     print("📁 Ingresa el directorio de origen (se buscará recursivamente):")
  115.     source_root = input("Ruta de origen: ").strip('"').strip()
  116.  
  117.     if not Path(source_root).is_dir():
  118.         print("❌ Error: La ruta de origen no es válida.")
  119.         sys.exit(1)
  120.  
  121.     print("➡️ Ingresa el directorio de destino:")
  122.     dest_root = input("Ruta de destino: ").strip('"').strip()
  123.    
  124.     # Crear las subcarpetas de destino y logs
  125.     for subdir in [FACES_SUBDIR, OBJECTS_SUBDIR, LOGS_SUBDIR]:
  126.         os.makedirs(Path(dest_root) / subdir, exist_ok=True)
  127.  
  128.     # Generar un registro de log único en la carpeta de destino
  129.     timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
  130.     log_file = Path(dest_root) / LOGS_SUBDIR / f'procesamiento_{timestamp}.log'
  131.  
  132.     image_files = get_all_images(source_root)
  133.     total_files = len(image_files)
  134.  
  135.     if not image_files:
  136.         print("⚠️ No se encontraron imágenes en el directorio proporcionado.")
  137.         sys.exit(0)
  138.  
  139.     # Definir el número de trabajadores para la piscina de hilos
  140.     max_workers = max(1, multiprocessing.cpu_count() - 1)
  141.     print(f"\n🔍 Se encontraron {total_files} imágenes. Usando {max_workers} hilos. Iniciando procesamiento...\n")
  142.  
  143.     with open(log_file, 'a', encoding='utf-8') as log:
  144.         log.write(f"\n--- Procesamiento iniciado: {datetime.now()} ---\n")
  145.  
  146.         processed_count = 0
  147.         with ThreadPoolExecutor(max_workers=max_workers) as executor:
  148.             future_to_file = {executor.submit(process_image, file, dest_root): file for file in image_files}
  149.  
  150.             for future in as_completed(future_to_file):
  151.                 result, log_entry = future.result()
  152.                 if log_entry:
  153.                     log.write(f"{result}\n")
  154.                
  155.                 # Actualizar la barra de progreso
  156.                 processed_count += 1
  157.                 progress = (processed_count / total_files) * 100
  158.                 sys.stdout.write(f"\rProgreso: [{processed_count}/{total_files}] {progress:.2f}% completado.  ")
  159.                 sys.stdout.flush()
  160.  
  161.         sys.stdout.write("\n")
  162.         sys.stdout.flush()
  163.  
  164.         log.write(f"--- Procesamiento finalizado: {datetime.now()} ---\n")
  165.  
  166. if __name__ == "__main__":
  167.     main()
Advertisement
Add Comment
Please, Sign In to add comment