Team Ai
Apppublic

jarondon82/ComputerVisionProject

sourceHugging Faceupdated 2y agoView on Hugging Face
1likes
streamlit_app.py2647 linesDownload Raw Back to root
1import streamlit as st2 3# Set page config with custom title and layout - DEBE SER EL PRIMER COMANDO STREAMLIT4st.set_page_config(5    page_title="Advanced Face & Feature Detection",6    page_icon="馃懁",7    layout="wide",8    initial_sidebar_state="expanded"9)10 11# Importaciones despu茅s de set_page_config12import cv213import numpy as np14from PIL import Image15from io import BytesIO16import base6417import tempfile18import os19import time20import urllib.request21import pandas as pd22import json23import matplotlib.pyplot as plt24import pickle25from sklearn.metrics.pairwise import cosine_similarity # type: ignore26 27# Importar m贸dulos opcionales que pueden no estar disponibles en todos los entornos28try:29    import av30    from streamlit_webrtc import webrtc_streamer, VideoProcessorBase, RTCConfiguration, WebRtcMode31    WEBRTC_AVAILABLE = True32except ImportError:33    WEBRTC_AVAILABLE = False34    st.warning("WebRTC components are not available. Real-time camera features will be disabled.")35 36# Importar las utilidades para la base de datos de rostros37try:38    from face_database_utils import save_face_database, load_face_database, export_database_json, import_database_json, print_database_info39    DATABASE_UTILS_AVAILABLE = True40except ImportError:41    DATABASE_UTILS_AVAILABLE = False42    st.warning("Database utilities are not available. Face recognition data will not be persistent between sessions.")43 44# Importar DeepFace para reconocimiento facial avanzado45try:46    from deepface import DeepFace47    DEEPFACE_AVAILABLE = True48except ImportError:49    DEEPFACE_AVAILABLE = False50 51# Import functions for face comparison52try:53    from face_comparison import compare_faces, compare_faces_embeddings, generate_comparison_report_english, draw_face_matches, extract_face_embeddings, extract_face_embeddings_all_models54    FACE_COMPARISON_AVAILABLE = True55except ImportError:56    FACE_COMPARISON_AVAILABLE = False57    st.warning("Face comparison functions are not available. Please check your installation.")58 59# Funci贸n principal que encapsula toda la aplicaci贸n60def main():61    # La configuraci贸n de la p谩gina ya se ha hecho al inicio del script, eliminar de aqu铆62    63    # Sidebar for navigation and controls64    st.sidebar.title("Controls & Settings")65 66    # Initialize session_state to store original image and camera state67    if 'original_image' not in st.session_state:68        st.session_state.original_image = None69    if 'camera_running' not in st.session_state:70        st.session_state.camera_running = False71    if 'feature_camera_running' not in st.session_state:72        st.session_state.feature_camera_running = False73 74    # Navigation menu75    app_mode = st.sidebar.selectbox(76        "Choose the app mode",77        ["About", "Face Detection", "Feature Detection", "Comparison Mode", "Face Recognition", "Diagn贸stico"]78    )79    80    # A帽adir mensaje destacado para guiar al usuario a la detecci贸n en tiempo real81    if app_mode != "Face Recognition":82        st.sidebar.warning("鈿狅笍 Para usar la detecci贸n facial en tiempo real, selecciona 'Face Recognition' y luego la pesta帽a 'Real-time Recognition'")83    84    # Function to load DNN models with caching and auto-download85    @st.cache_resource86    def load_face_model():87        # No need to create directory as we're using the root directory88        #89            #90        91        # Correct model file names92        modelFile = "res10_300x300_ssd_iter_140000.caffemodel"93        configFile = "deploy.prototxt.txt"94        95        # Check if files exist96        missing_files = []97        if not os.path.exists(modelFile):98            missing_files.append(modelFile)99        if not os.path.exists(configFile):100            missing_files.append(configFile)101        102        if missing_files:103            st.error("Missing model files: " + ", ".join(missing_files))104            st.error("Please manually download the following files:")105            st.code("""106            1. Download the model file:107               URL: https://raw.githubusercontent.com/sr6033/face-detection-with-OpenCV-and-DNN/master/res10_300x300_ssd_iter_140000.caffemodel108               Save as: res10_300x300_ssd_iter_140000.caffemodel109               110            2. Download the configuration file:111               URL: https://raw.githubusercontent.com/sr6033/face-detection-with-OpenCV-and-DNN/master/deploy.prototxt.txt112               Save as: deploy.prototxt.txt113            """)114            st.stop()115        116        # Load model117        try:118            net = cv2.dnn.readNetFromCaffe(configFile, modelFile)119            return net120        except Exception as e:121            st.error(f"Error loading model: {e}")122            st.stop()123 124    @st.cache_resource125    def load_feature_models():126        # Load pre-trained models for eye and smile detection127        eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')128        smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_smile.xml')129        return eye_cascade, smile_cascade130 131    # Function for detecting faces in an image132    def detect_face_dnn(net, frame, conf_threshold=0.3):133        """134        Detecta rostros en una imagen utilizando un modelo DNN pre-entrenado.135        Si no se detectan rostros, usa autom谩ticamente Haar Cascades como respaldo.136        137        Args:138            net: Modelo DNN cargado139            frame: Imagen en formato BGR140            conf_threshold: Umbral de confianza para la detecci贸n (0.0-1.0)141            142        Returns:143            Lista de bounding boxes con formato [x1, y1, x2, y2, confidence]144            o None si no se detectan rostros145        """146        # Crear un diagn贸stico m谩s detallado147        log_info = f"===== DIAGN脫STICO DE DETECCI脫N FACIAL =====\n"148        log_info += f"Timestamp: {time.strftime('%Y-%m-%d %H:%M:%S')}\n"149        log_info += f"Tipo de modelo: {type(net)}\n"150        log_info += f"Forma de la imagen: {frame.shape}\n"151        152        # Forzar un umbral muy bajo para aumentar la sensibilidad153        internal_threshold = 0.05  # Usar este umbral internamente para mayor sensibilidad154        155        # A帽adir impresi贸n de depuraci贸n para el umbral usado156        print(f"Detecting faces with original threshold: {conf_threshold}, using internal threshold: {internal_threshold}")157        log_info += f"Umbral original: {conf_threshold}, umbral interno: {internal_threshold}\n"158        159        # Obtener dimensiones de la imagen160        h, w = frame.shape[:2]161        log_info += f"Dimensiones de imagen: {w}x{h}\n"162        163        # Crear un blob de la imagen (redimensionada a 300x300 y normalizada)164        # IMPORTANTE: Los valores de media (104.0, 177.0, 123.0) son espec铆ficos 165        # para el modelo res10_300x300_ssd_iter_140000.caffemodel entrenado en Caffe166        try:167            blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0,168                                        (300, 300), (104.0, 177.0, 123.0))169            log_info += f"Blob creado correctamente. Forma: {blob.shape}\n"170        except Exception as e:171            log_info += f"ERROR al crear blob: {str(e)}\n"172            with open("diagnostico_deteccion.txt", "a") as f:173                f.write(log_info)174            print(log_info)175            return detect_face_haar(frame, conf_threshold)176        177        # Pasar el blob a trav茅s de la red178        try:179            net.setInput(blob)180            log_info += "Input establecido correctamente en la red\n"181        except Exception as e:182            log_info += f"ERROR al establecer input: {str(e)}\n"183            with open("diagnostico_deteccion.txt", "a") as f:184                f.write(log_info)185            print(log_info)186            return detect_face_haar(frame, conf_threshold)187        188        # Realizar la detecci贸n (forward pass)189        try:190            detections = net.forward()191            log_info += f"Forward pass exitoso. Forma de las detecciones: {detections.shape}\n"192        except Exception as e:193            log_info += f"ERROR en forward pass: {str(e)}\n"194            with open("diagnostico_deteccion.txt", "a") as f:195                f.write(log_info)196            print(log_info)197            # Intentar con Haar cascade como respaldo198            print("Intentando detecci贸n con Haar cascade como respaldo...")199            return detect_face_haar(frame, conf_threshold)200            201        # Variable para almacenar las cajas delimitadoras202        bboxes = []203        204        # Procesar cada detecci贸n205        detection_count = 0206        detection_info = []207        208        for i in range(detections.shape[2]):209            # Extraer la confianza (probabilidad) de la detecci贸n210            confidence = detections[0, 0, i, 2]211            detection_info.append(f"  {i}: confianza={confidence:.3f}")212            213            # Filtrar detecciones d茅biles por confianza (usando el umbral interno m谩s bajo)214            if confidence > internal_threshold:215                detection_count += 1216                # La red da las coordenadas de la caja normalizadas entre 0 y 1217                # Multiplicamos por ancho y alto para obtener coordenadas en p铆xeles218                box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])219                220                # Convertir a enteros221                x1, y1, x2, y2 = box.astype("int")222                223                # Garantizar que las coordenadas est茅n dentro de los l铆mites de la imagen224                x1, y1 = max(0, x1), max(0, y1)225                x2, y2 = min(w, x2), min(h, y2)226                227                # Imprimir informaci贸n de depuraci贸n228                print(f"Detecci贸n #{detection_count}: confianza={confidence:.3f}, bbox=[{x1},{y1},{x2},{y2}]")229                detection_info[i] += f", bbox=[{x1},{y1},{x2},{y2}]"230                231                # Saltar cajas inv谩lidas (por ejemplo, con ancho o alto negativo)232                width, height = x2 - x1, y2 - y1233                if width <= 0 or height <= 0:234                    print(f"Saltando caja inv谩lida con dimensiones: {width}x{height}")235                    detection_info[i] += f" - INV脕LIDA: dimensiones {width}x{height}"236                    continue237                238                # A帽adir la caja y la confianza a la lista de resultados239                bboxes.append([x1, y1, x2, y2, confidence])240                detection_info[i] += " - A脩ADIDA"241        242        # A帽adir informaci贸n de detecciones al log243        log_info += f"Detecciones totales analizadas: {detections.shape[2]}\n"244        log_info += "Detalles de detecciones:\n"245        for info in detection_info:246            log_info += f"{info}\n"247        248        # Dar feedback sobre el n煤mero de detecciones249        log_info += f"Total de detecciones con confianza > {internal_threshold}: {detection_count}\n"250        log_info += f"Total de cajas v谩lidas: {len(bboxes)}\n"251        252        # Si no se encontraron rostros, intentar con Haar cascade253        if not bboxes:254            log_info += "NO SE DETECTARON ROSTROS CON DNN\n"255            log_info += "Intentando detecci贸n con Haar cascade como respaldo...\n"256            257            # Verificar si hay detecciones con umbral m谩s bajo para depuraci贸n258            for i in range(detections.shape[2]):259                confidence = detections[0, 0, i, 2]260                if confidence > 0.01:  # Umbral extremadamente bajo para depuraci贸n261                    box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])262                    x1, y1, x2, y2 = box.astype("int")263                    log_info += f"Detecci贸n de baja confianza: {confidence:.3f} en [{x1},{y1},{x2},{y2}]\n"264            265            # Intentar detecci贸n Haar266            haar_bboxes = detect_face_haar(frame, conf_threshold)267            if haar_bboxes and len(haar_bboxes) > 0:268                log_info += f"Haar cascade encontr贸 {len(haar_bboxes)} rostro(s)\n"269                log_info += f"Haar bboxes: {haar_bboxes}\n"270                271                # Guardar diagn贸stico en archivo272                with open("diagnostico_deteccion.txt", "a") as f:273                    f.write(log_info)274                print(log_info)275                return haar_bboxes276                277            log_info += "Haar cascade NO detect贸 rostros\n"278            279            # Guardar diagn贸stico en archivo cuando no hay detecciones280            with open("diagnostico_deteccion.txt", "a") as f:281                f.write(log_info)282            print(log_info)283            return []284        285        # Si llegamos aqu铆, hay detecciones exitosas286        log_info += f"Detecci贸n exitosa. Retornando {len(bboxes)} bounding boxes.\n"287        log_info += f"Bounding boxes: {bboxes}\n"288        289        # Guardar diagn贸stico en archivo290        with open("diagnostico_deteccion.txt", "a") as f:291            f.write(log_info)292        print(log_info)293        294        # Devolver las cajas detectadas295        return bboxes296 297    # Funci贸n alternativa para detectar rostros usando Haar Cascades298    def detect_face_haar(frame, conf_threshold=0.3):299        """Detecta rostros usando Haar Cascades como m茅todo de respaldo"""300        try:301            # Carga el clasificador Haar Cascade para rostros (deber铆a estar cargado globalmente,302            # pero lo hacemos aqu铆 para asegurar que est茅 disponible)303            if 'haar_face_cascade' not in st.session_state:304                cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'305                st.session_state.haar_face_cascade = cv2.CascadeClassifier(cascade_path)306                print(f"Haar cascade loaded from {cascade_path}")307            308            # Convertir a escala de grises309            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)310            311            # Ecualizar el histograma para mejorar contraste312            gray = cv2.equalizeHist(gray)313            314            # Par谩metros m谩s sensibles para la detecci贸n con Haar315            scale_factor = 1.05  # M谩s lento pero m谩s preciso (era 1.1)316            min_neighbors = 3    # Valor m谩s bajo, m谩s detecciones pero m谩s falsos positivos (era 5)317            min_size = (20, 20)  # Tama帽o m铆nimo m谩s peque帽o (era 30, 30)318            319            # Detectar rostros con clasificador Haar320            faces = st.session_state.haar_face_cascade.detectMultiScale(321                gray,322                scaleFactor=scale_factor,323                minNeighbors=min_neighbors,324                minSize=min_size,325                flags=cv2.CASCADE_SCALE_IMAGE326            )327            328            # Convertir a formato bounding box [x1, y1, x2, y2, confianza]329            bboxes = []330            for (x, y, w, h) in faces:331                # Usar un valor de confianza fijo para las detecciones Haar332                confidence = 0.8  # Valor arbitrario alto333                bboxes.append([x, y, x + w, y + h, confidence])334                335            return bboxes336            337        except Exception as e:338            print(f"Error en detecci贸n Haar: {e}")339            return []340 341    # Function for processing face detections342    def process_face_detections(frame, detections, conf_threshold=0.5, bbox_color=(0, 255, 0)):343        # Create a copy for drawing on344        result_frame = frame.copy()345        346        # Asegurar que bbox_color sea una tupla de 3 elementos para BGR347        if isinstance(bbox_color, tuple) and len(bbox_color) == 3:348            bbox_color_bgr = bbox_color349        else:350            # Usar verde como color predeterminado351            bbox_color_bgr = (0, 255, 0)352        353        # Definir grosor para los rect谩ngulos (m谩s grueso para mejor visibilidad)354        thickness = 3355        356        # Procesar detecciones si son del formato original357        if isinstance(detections, np.ndarray) and len(detections.shape) == 4:358            bboxes = []359            frame_h = frame.shape[0]360            frame_w = frame.shape[1]361            362            for i in range(detections.shape[2]):363                confidence = detections[0, 0, i, 2]364                print(f"Confidence: {confidence}, Threshold: {conf_threshold}")365                366                # Usar un umbral muy bajo para mejorar la detecci贸n367                effective_threshold = max(0.05, conf_threshold)368                369                if confidence > effective_threshold:370                    x1 = int(detections[0, 0, i, 3] * frame_w)371                    y1 = int(detections[0, 0, i, 4] * frame_h)372                    x2 = int(detections[0, 0, i, 5] * frame_w)373                    y2 = int(detections[0, 0, i, 6] * frame_h)374                    375                    # Asegurarse de que las coordenadas est茅n dentro de los l铆mites376                    x1 = max(0, min(x1, frame_w - 1))377                    y1 = max(0, min(y1, frame_h - 1))378                    x2 = max(0, min(x2, frame_w - 1))379                    y2 = max(0, min(y2, frame_h - 1))380                    381                    # Verificar que el rect谩ngulo es v谩lido382                    if x2 <= x1 or y2 <= y1:383                        continue384                    385                    # Dibujar el bounding box con l铆nea m谩s gruesa386                    cv2.rectangle(result_frame, (x1, y1), (x2, y2), bbox_color_bgr, thickness)387                    388                    # A帽adir texto con la confianza389                    label = f"{confidence:.2f}"390                    cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, bbox_color_bgr, 2)391                    392                    # A帽adir a la lista de bounding boxes393                    bboxes.append([x1, y1, x2, y2, confidence])394        else:395            # Si ya es una lista de bounding boxes (formato nuevo)396            bboxes = detections if detections is not None else []397            398            # Dibujar bounding boxes399            for bbox in bboxes:400                if len(bbox) == 5:  # Asegurarse de que el bounding box tiene el formato correcto401                    x1, y1, x2, y2, confidence = bbox402                    403                    # Usar un umbral bajo para la visualizaci贸n404                    effective_threshold = max(0.05, conf_threshold)405                    406                    if confidence >= effective_threshold:407                        # Verificar que las coordenadas son v谩lidas408                        if x1 >= 0 and y1 >= 0 and x2 > x1 and y2 > y1:409                            # Dibujar el bounding box con l铆nea m谩s gruesa410                            cv2.rectangle(result_frame, (x1, y1), (x2, y2), bbox_color_bgr, thickness)411                            412                            # A帽adir texto con la confianza413                            label = f"{confidence:.2f}"414                            cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, bbox_color_bgr, 2)415        416        return result_frame, bboxes417 418    # Function to detect facial features (eyes, smile) with improved profile face handling419    def detect_facial_features(frame, bboxes, eye_cascade, smile_cascade, detect_eyes=True, detect_smile=True, smile_sensitivity=15, eye_sensitivity=5):420        result_frame = frame.copy()421        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)422        423        # Counters for detection summary424        eye_count = 0425        smile_count = 0426        427        for bbox in bboxes:428            x1, y1, x2, y2, _ = bbox429            roi_gray = gray[y1:y2, x1:x2]430            roi_color = result_frame[y1:y2, x1:x2]431            face_width = x2 - x1432            face_height = y2 - y1433            434            # Detect eyes if enabled435            if detect_eyes:436                # Adjust region of interest to focus on the upper part of the face437                upper_face_y1 = y1438                upper_face_y2 = y1 + int(face_height * 0.55)  # Slightly reduced to focus more on the eye area439                440                # For profile faces, we need to search the entire upper region441                # as well as the left and right sides separately442                443                # Full upper region for profile faces444                upper_face_roi_gray = gray[upper_face_y1:upper_face_y2, x1:x2]445                upper_face_roi_color = result_frame[upper_face_y1:upper_face_y2, x1:x2]446                447                # Split the upper region into two halves (left and right) to search for eyes individually448                mid_x = x1 + face_width // 2449                left_eye_roi_gray = gray[upper_face_y1:upper_face_y2, x1:mid_x]450                right_eye_roi_gray = gray[upper_face_y1:upper_face_y2, mid_x:x2]451                452                left_eye_roi_color = result_frame[upper_face_y1:upper_face_y2, x1:mid_x]453                right_eye_roi_color = result_frame[upper_face_y1:upper_face_y2, mid_x:x2]454                455                # Apply histogram equalization and contrast enhancement for all regions456                if upper_face_roi_gray.size > 0:457                    upper_face_roi_gray = cv2.equalizeHist(upper_face_roi_gray)458                    459                    # Enhance contrast460                    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))461                    upper_face_roi_gray = clahe.apply(upper_face_roi_gray)462                    463                    # First try to detect eyes in the full upper region (for profile faces)464                    full_eyes = eye_cascade.detectMultiScale(465                        upper_face_roi_gray, 466                        scaleFactor=1.02,  # More sensitive for profile faces467                        minNeighbors=max(1, eye_sensitivity-3),  # Even more sensitive468                        minSize=(int(face_width * 0.07), int(face_width * 0.07)),469                        maxSize=(int(face_width * 0.3), int(face_width * 0.3))470                    )471                    472                    # If we found eyes in the full region, use those473                    if len(full_eyes) > 0:474                        # Sort by size (area) and take up to 2 largest475                        full_eyes = sorted(full_eyes, key=lambda e: e[2] * e[3], reverse=True)476                        full_eyes = full_eyes[:2]  # Take at most 2 eyes477                        478                        for ex, ey, ew, eh in full_eyes:479                            eye_count += 1480                            cv2.rectangle(upper_face_roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)481                            cv2.putText(upper_face_roi_color, "Eye", (ex, ey-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)482                    else:483                        # If no eyes found in full region, try left and right separately484                        if left_eye_roi_gray.size > 0:485                            left_eye_roi_gray = cv2.equalizeHist(left_eye_roi_gray)486                            left_eye_roi_gray = clahe.apply(left_eye_roi_gray)487                            488                            left_eyes = eye_cascade.detectMultiScale(489                                left_eye_roi_gray, 490                                scaleFactor=1.03,491                                minNeighbors=max(1, eye_sensitivity-2),492                                minSize=(int(face_width * 0.08), int(face_width * 0.08)),493                                maxSize=(int(face_width * 0.25), int(face_width * 0.25))494                            )495                            496                            if len(left_eyes) > 0:497                                # Sort by size and take the largest498                                left_eyes = sorted(left_eyes, key=lambda e: e[2] * e[3], reverse=True)499                                left_eye = left_eyes[0]500                                eye_count += 1501                                502                                # Draw rectangle for the left eye503                                ex, ey, ew, eh = left_eye504                                cv2.rectangle(left_eye_roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)505                                cv2.putText(left_eye_roi_color, "Eye", (ex, ey-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)506                        507                        if right_eye_roi_gray.size > 0:508                            right_eye_roi_gray = cv2.equalizeHist(right_eye_roi_gray)509                            right_eye_roi_gray = clahe.apply(right_eye_roi_gray)510                            511                            right_eyes = eye_cascade.detectMultiScale(512                                right_eye_roi_gray, 513                                scaleFactor=1.03,514                                minNeighbors=max(1, eye_sensitivity-2),515                                minSize=(int(face_width * 0.08), int(face_width * 0.08)),516                                maxSize=(int(face_width * 0.25), int(face_width * 0.25))517                            )518                            519                            if len(right_eyes) > 0:520                                # Sort by size and take the largest521                                right_eyes = sorted(right_eyes, key=lambda e: e[2] * e[3], reverse=True)522                                right_eye = right_eyes[0]523                                eye_count += 1524                                525                                # Draw rectangle for the right eye526                                ex, ey, ew, eh = right_eye527                                cv2.rectangle(right_eye_roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)528                                cv2.putText(right_eye_roi_color, "Eye", (ex, ey-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)529            530            # Detect smile if enabled531            if detect_smile:532                # For profile faces, we need to adjust the region of interest533                # Try multiple regions to improve detection534                535                # Standard region (middle to bottom)536                lower_face_y1 = y1 + int(face_height * 0.5)537                lower_face_roi_gray = gray[lower_face_y1:y2, x1:x2]538                lower_face_roi_color = result_frame[lower_face_y1:y2, x1:x2]539                540                # Alternative region (lower third)541                alt_lower_face_y1 = y1 + int(face_height * 0.65)542                alt_lower_face_roi_gray = gray[alt_lower_face_y1:y2, x1:x2]543                544                # Apply histogram equalization and enhance contrast545                smile_detected = False546                547                if lower_face_roi_gray.size > 0:548                    lower_face_roi_gray = cv2.equalizeHist(lower_face_roi_gray)549                    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))550                    lower_face_roi_gray = clahe.apply(lower_face_roi_gray)551                    552                    # Try with standard parameters553                    smiles = smile_cascade.detectMultiScale(554                        lower_face_roi_gray, 555                        scaleFactor=1.2,556                        minNeighbors=smile_sensitivity,557                        minSize=(int(face_width * 0.25), int(face_width * 0.15)),558                        maxSize=(int(face_width * 0.7), int(face_width * 0.4))559                    )560                    561                    if len(smiles) > 0:562                        # Sort by size and take the largest563                        smiles = sorted(smiles, key=lambda s: s[2] * s[3], reverse=True)564                        sx, sy, sw, sh = smiles[0]565                        566                        # Increment smile counter567                        smile_count += 1568                        smile_detected = True569                        570                        # Draw rectangle for the smile571                        cv2.rectangle(lower_face_roi_color, (sx, sy), (sx+sw, sy+sh), (0, 0, 255), 2)572                        cv2.putText(lower_face_roi_color, "Smile", (sx, sy-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)573                574                # If no smile detected in standard region, try alternative region575                if not smile_detected and alt_lower_face_roi_gray.size > 0:576                    alt_lower_face_roi_gray = cv2.equalizeHist(alt_lower_face_roi_gray)577                    alt_lower_face_roi_gray = clahe.apply(alt_lower_face_roi_gray)578                    579                    # Try with more sensitive parameters580                    alt_smiles = smile_cascade.detectMultiScale(581                        alt_lower_face_roi_gray, 582                        scaleFactor=1.1,583                        minNeighbors=max(1, smile_sensitivity-5),  # More sensitive584                        minSize=(int(face_width * 0.2), int(face_width * 0.1)),585                        maxSize=(int(face_width * 0.6), int(face_width * 0.3))586                    )587                    588                    if len(alt_smiles) > 0:589                        # Sort by size and take the largest590                        alt_smiles = sorted(alt_smiles, key=lambda s: s[2] * s[3], reverse=True)591                        sx, sy, sw, sh = alt_smiles[0]592                        593                        # Adjust coordinates for the alternative region594                        adjusted_sy = sy + (alt_lower_face_y1 - lower_face_y1)595                        596                        # Increment smile counter597                        smile_count += 1598                        599                        # Draw rectangle for the smile (in the original lower face ROI)600                        cv2.rectangle(lower_face_roi_color, (sx, adjusted_sy), (sx+sw, adjusted_sy+sh), (0, 0, 255), 2)601                        cv2.putText(lower_face_roi_color, "Smile", (sx, adjusted_sy-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)602        603        return result_frame, eye_count, smile_count604 605    # Funci贸n para detectar atributos faciales (edad, g茅nero, emoci贸n)606    def detect_face_attributes(image, bbox):607        """608        Detecta atributos faciales como edad, g茅nero y emoci贸n usando DeepFace.609        610        Args:611            image: Imagen en formato OpenCV (BGR)612            bbox: Bounding box de la cara [x1, y1, x2, y2, conf]613            614        Returns:615            Diccionario con los atributos detectados616        """617        if not DEEPFACE_AVAILABLE:618            return None619        620        try:621            x1, y1, x2, y2, _ = bbox622            face_img = image[y1:y2, x1:x2]623            624            # Convertir de BGR a RGB para DeepFace625            face_img_rgb = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)626            627            # Analyze atributos faciales628            attributes = DeepFace.analyze(629                img_path=face_img_rgb,630                actions=['age', 'gender', 'emotion'],631                enforce_detection=False,632                detector_backend="opencv"633            )634            635            return attributes[0]636        637        except Exception as e:638            st.error(f"Error detecting facial attributes: {str(e)}")639            return None640 641    # Function to apply age and gender detection (placeholder - would need additional models)642    def detect_age_gender(frame, bboxes):643        # Versi贸n mejorada que usa DeepFace si est谩 disponible644        result_frame = frame.copy()645        646        for i, bbox in enumerate(bboxes):647            x1, y1, x2, y2, _ = bbox648            649            if DEEPFACE_AVAILABLE:650                # Intentar usar DeepFace para an谩lisis facial651                attributes = detect_face_attributes(frame, bbox)652                653                if attributes:654                    # Extraer informaci贸n de atributos655                    age = attributes.get('age', 'Unknown')656                    gender = attributes.get('gender', 'Unknown')657                    emotion = attributes.get('dominant_emotion', 'Unknown').capitalize()658                    gender_prob = attributes.get('gender', {}).get('Woman', 0)659                    660                    # Determinar color basado en confianza661                    if gender == 'Woman':662                        gender_color = (255, 0, 255)  # Magenta para mujer663                    else:664                        gender_color = (255, 0, 0)    # Azul para hombre665                    666                    # A帽adir texto con informaci贸n667                    cv2.putText(result_frame, f"Age: {age}", (x1, y2+20), 668                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)669                    cv2.putText(result_frame, f"Gender: {gender}", (x1, y2+40), 670                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, gender_color, 2)671                    cv2.putText(result_frame, f"Emotion: {emotion}", (x1, y2+60), 672                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)673                else:674                    # Fallback si DeepFace falla675                    cv2.putText(result_frame, "Age: Unknown", (x1, y2+20), 676                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)677                    cv2.putText(result_frame, "Gender: Unknown", (x1, y2+40), 678                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)679            else:680                # Usar texto placeholder si DeepFace no est谩 disponible681                cv2.putText(result_frame, "Age: 25-35", (x1, y2+20), 682                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)683                cv2.putText(result_frame, "Gender: Unknown", (x1, y2+40), 684                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)685        686        return result_frame687 688    # Function to generate download link for processed image689    def get_image_download_link(img, filename, text):690        buffered = BytesIO()691        img.save(buffered, format="JPEG")692        img_str = base64.b64encode(buffered.getvalue()).decode()693        href = f'<a href="data:file/txt;base64,{img_str}" download="{filename}">{text}</a>'694        return href695 696    # Function to process video frames697    def process_video(video_path, face_net, eye_cascade, smile_cascade, conf_threshold=0.5, detect_eyes=True, detect_smile=True, bbox_color=(0, 255, 0), smile_sensitivity=15, eye_sensitivity=5):698        cap = cv2.VideoCapture(video_path)699        700        # Get video properties701        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))702        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))703        fps = int(cap.get(cv2.CAP_PROP_FPS))704        705        # Create temporary output file706        temp_dir = tempfile.mkdtemp()707        temp_output_path = os.path.join(temp_dir, "processed_video.mp4")708        709        # Initialize video writer710        fourcc = cv2.VideoWriter_fourcc(*'mp4v')711        out = cv2.VideoWriter(temp_output_path, fourcc, fps, (frame_width, frame_height))712        713        # Create a progress bar714        frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))715        progress_bar = st.progress(0)716        status_text = st.empty()717        718        # Process video frames719        current_frame = 0720        processing_times = []721        722        # Total counters for statistics723        total_faces = 0724        total_eyes = 0725        total_smiles = 0726        727        while cap.isOpened():728            ret, frame = cap.read()729            if not ret:730                break731            732            # Start timing for performance metrics733            start_time = time.time()734            735            # Detect faces736            detections = detect_face_dnn(face_net, frame, conf_threshold)737            processed_frame, bboxes = process_face_detections(frame, detections, conf_threshold, bbox_color)738            739            # Update face counter740            total_faces += len(bboxes)741            742            # Detect facial features if enabled743            if detect_eyes or detect_smile:744                processed_frame, eye_count, smile_count = detect_facial_features(745                    processed_frame, 746                    bboxes, 747                    eye_cascade, 748                    smile_cascade,749                    detect_eyes,750                    detect_smile,751                    smile_sensitivity,752                    eye_sensitivity753                )754                # Update counters755                total_eyes += eye_count756                total_smiles += smile_count757            758            # End timing759            processing_times.append(time.time() - start_time)760            761            # Write the processed frame762            out.write(processed_frame)763            764            # Update progress765            current_frame += 1766            progress_bar.progress(current_frame / frame_count)767            status_text.text(f"Processing frame {current_frame}/{frame_count}")768        769        # Release resources770        cap.release()771        out.release()772        773        # Calculate and display performance metrics774        if processing_times:775            avg_time = sum(processing_times) / len(processing_times)776            status_text.text(f"Processing complete! Average processing time: {avg_time:.4f}s per frame")777        778        # Return detection statistics779        detection_stats = {780            "faces": total_faces // max(1, current_frame),  # Average per frame781            "eyes": total_eyes // max(1, current_frame),    # Average per frame782            "smiles": total_smiles // max(1, current_frame) # Average per frame783        }784        785        return temp_output_path, temp_dir, detection_stats786 787    # Camera control functions788    def start_camera():789        st.session_state.camera_running = True790 791    def stop_camera():792        st.session_state.camera_running = False793        st.session_state.camera_stopped = True794 795    def start_feature_camera():796        st.session_state.feature_camera_running = True797 798    def stop_feature_camera():799        st.session_state.feature_camera_running = False800        st.session_state.feature_camera_stopped = True801 802    # Funci贸n auxiliar para verificar si una imagen es v谩lida antes de redimensionar803    def is_valid_image(img):804        if img is None:805            return False806        try:807            # Verificar que la imagen tenga dimensiones v谩lidas y datos808            return img.size > 0 and len(img.shape) >= 2 and img.shape[0] > 0 and img.shape[1] > 0809        except Exception:810            return False811    812    # Funci贸n auxiliar para redimensionar de forma segura813    def safe_resize(img, target_size):814        if is_valid_image(img):815            try:816                return cv2.resize(img, target_size)817            except Exception as e:818                print(f"Error al redimensionar: {str(e)}")819                return None820        return None821 822    if app_mode == "About":823        st.markdown("""824        ## About This App825        826        This application uses OpenCV's Deep Neural Network (DNN) module and Haar Cascade classifiers to detect faces and facial features in images and videos.827        828        ### Features:829        - Face detection using OpenCV DNN830        - Eye and smile detection using Haar Cascades831        - Support for both image and video processing832        - Adjustable confidence threshold833        - Download options for processed media834        - Performance metrics835        836        ### How to use:837        1. Select a mode from the sidebar838        2. Upload an image or video839        3. Adjust settings as needed840        4. View and download the results841        842        ### Technologies Used:843        - Streamlit for the web interface844        - OpenCV for computer vision operations845        - Python for backend processing846        847        ### Models:848        - SSD MobileNet for face detection849        - Haar Cascades for facial features850        """)851        852        # Display a sample image or GIF853        st.image("https://opencv.org/wp-content/uploads/2019/07/detection.gif", caption="Sample face detection", use_container_width=True)854 855    elif app_mode == "Face Detection":856        # Load the face detection model857        face_net = load_face_model()858        859        # Input type selection (Image or Video)860        input_type = st.sidebar.radio("Select Input Type", ["Image", "Video"])861        862        # Confidence threshold slider863        conf_threshold = st.sidebar.slider(864            "Confidence Threshold", 865            min_value=0.0, 866            max_value=1.0, 867            value=0.5, 868            step=0.05,869            help="Adjust the threshold for face detection confidence (higher = fewer detections but more accurate)"870        )871        872        # Style options873        bbox_color = st.sidebar.color_picker("Bounding Box Color", "#00FF00")874        # Convert hex color to BGR for OpenCV875        bbox_color_rgb = tuple(int(bbox_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))876        bbox_color_bgr = (bbox_color_rgb[2], bbox_color_rgb[1], bbox_color_rgb[0])  # Convert RGB to BGR877        878        # Display processing metrics879        show_metrics = st.sidebar.checkbox("Show Processing Metrics", True)880        881        if input_type == "Image":882            # File uploader for images883            file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])884            885            # Umbral de confianza ajustable886            conf_threshold = st.slider(887                "Umbral de confianza",888                min_value=0.05,889                max_value=0.95,890                value=0.2,  # Valor por defecto m谩s bajo (era 0.5)891                step=0.05,892                help="Ajusta este valor para controlar la sensibilidad de la detecci贸n facial. Un valor m谩s bajo detecta m谩s rostros pero puede tener falsos positivos."893            )894            895            # Color del bounding box896            bbox_color_bgr = (0, 255, 0)  # Verde brillante para mejor visibilidad897            898            if file_buffer is not None:899                # Read the file and convert it to OpenCV format900                raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)901                image = cv2.imdecode(raw_bytes, cv2.IMREAD_COLOR)902                903                # Save la imagen original en session_state para reprocesarla cuando cambie el umbral904                # Usar un identificador 煤nico para cada archivo para detectar cambios905                file_id = file_buffer.name + str(file_buffer.size)906                907                if 'file_id' not in st.session_state or st.session_state.file_id != file_id:908                    st.session_state.file_id = file_id909                    st.session_state.original_image = image.copy()910                911                # Display original image912                col1, col2 = st.columns(2)913                with col1:914                    st.subheader("Original Image")915                    st.image(st.session_state.original_image, channels='BGR', use_container_width=True)916                917                # Start timing for performance metrics918                start_time = time.time()919                920                # Detect faces921                detections = detect_face_dnn(face_net, st.session_state.original_image, conf_threshold)922                processed_image, bboxes = process_face_detections(st.session_state.original_image, detections, conf_threshold, bbox_color_bgr)923                924                # Calculate processing time925                processing_time = time.time() - start_time926                927                # Display the processed image928                with col2:929                    st.subheader("Processed Image")930                    st.image(processed_image, channels='BGR', use_container_width=True)931                    932                    # Mostrar mensaje sobre lo que se est谩 viendo933                    if len(bboxes) > 0:934                        st.success(f"Se detectaron {len(bboxes)} rostros en la imagen.")935                    else:936                        st.warning("No se detectaron rostros. Prueba ajustar el umbral de confianza o usar otra imagen.")937                    938                    # Convert OpenCV image to PIL for download939                    pil_img = Image.fromarray(processed_image[:, :, ::-1])940                    st.markdown(941                        get_image_download_link(pil_img, "face_detection_result.jpg", "馃摜 Download Processed Image"),942                        unsafe_allow_html=True943                    )944        945        else:  # Video mode946            # Video mode options947            video_source = st.radio("Select video source", ["Upload video", "Use webcam"])948            949            if video_source == "Upload video":950                # File uploader for videos951                file_buffer = st.file_uploader("Upload a video", type=['mp4', 'avi', 'mov'])952                953                if file_buffer is not None:954                    # Save uploaded video to temporary file955                    temp_dir = tempfile.mkdtemp()956                    temp_path = os.path.join(temp_dir, "input_video.mp4")957                    958                    with open(temp_path, "wb") as f:959                        f.write(file_buffer.read())960                    961                    # Display original video962                    st.subheader("Original Video")963                    st.video(temp_path)964                    965                    # Load models for feature detection (will be used in the processing)966                    eye_cascade, smile_cascade = load_feature_models()967                    968                    # Process video button969                    if st.button("Process Video"):970                        with st.spinner("Processing video... This may take a while depending on the video length."):971                            # Process the video972                            output_path, output_dir, detection_stats = process_video(973                                temp_path, 974                                face_net, 975                                eye_cascade,976                                smile_cascade,977                                conf_threshold,978                                detect_eyes=True,979                                detect_smile=True,980                                bbox_color=bbox_color_bgr,981                                eye_sensitivity=5982                            )983                            984                            # Display processed video985                            st.subheader("Processed Video")986                            st.video(output_path)987                            988                            # Mostrar estad铆sticas de detecci贸n989                            st.subheader("Detection Summary")990                            summary_col1, summary_col2, summary_col3 = st.columns(3)991                            summary_col1.metric("Avg. Faces per Frame", detection_stats["faces"])992                            993                            if detect_eyes: # type: ignore994                                summary_col2.metric("Avg. Eyes per Frame", detection_stats["eyes"])995                            else:996                                summary_col2.metric("Avg. Eyes Detected", "N/A")997                            998                            if detect_smile: # type: ignore999                                summary_col3.metric("Avg. Smiles per Frame", detection_stats["smiles"])1000                            else:1001                                summary_col3.metric("Avg. Smiles Detected", "N/A")1002                            1003                            # Provide download link1004                            with open(output_path, 'rb') as f:1005                                video_bytes = f.read()1006                            1007                            st.download_button(1008                                label="馃摜 Download Processed Video",1009                                data=video_bytes,1010                                file_name="processed_video.mp4",1011                                mime="video/mp4"1012                            )1013                            1014                            # Clean up temporary files1015                            try:1016                                os.remove(temp_path)1017                                os.remove(output_path)1018                                os.rmdir(temp_dir)1019                                os.rmdir(output_dir)1020                            except:1021                                pass1022            else:  # Use webcam1023                st.subheader("Real-time face detection")1024                st.write("Click 'Start Camera' to begin real-time face detection.")1025                1026                # Verificar si WebRTC est谩 disponible1027                if not WEBRTC_AVAILABLE:1028                    st.error("WebRTC components are not available. Real-time camera features are disabled.")1029                    st.warning("鈿狅笍 Note: If you're using this app on Hugging Face Spaces without WebRTC support, try using the image upload or video upload features instead.")1030                else:1031                    # Placeholder for webcam video1032                    camera_placeholder = st.empty()1033                    1034                    # Buttons to control the camera1035                    col1, col2 = st.columns(2)1036                    start_button = col1.button("Start Camera", on_click=start_camera)1037                    stop_button = col2.button("Stop Camera", on_click=stop_camera)1038                    1039                    # Show message when camera is stopped1040                    if 'camera_stopped' in st.session_state and st.session_state.camera_stopped:1041                        st.info("Camera stopped. Click 'Start Camera' to activate it again.")1042                        st.session_state.camera_stopped = False1043                    1044                    if st.session_state.camera_running:1045                        st.info("Camera activated. Processing real-time video...")1046                        # Initialize webcam1047                        cap = cv2.VideoCapture(0)  # 0 is typically the main webcam1048                        1049                        if not cap.isOpened():1050                            st.error("Could not access webcam. Make sure it's connected and not being used by another application.")1051                            st.warning("鈿狅笍 Note: If you're using this app on Hugging Face Spaces, webcam access is not supported. Try running this app locally for webcam features.")1052                            st.session_state.camera_running = False1053                        else:1054                            # Display real-time video with face detection1055                            try:1056                                while st.session_state.camera_running:1057                                    ret, frame = cap.read()1058                                    if not ret:1059                                        st.error("Error reading frame from camera.")1060                                        break1061                                    1062                                    # Detect faces1063                                    detections = detect_face_dnn(face_net, frame, conf_threshold)1064                                    processed_frame, bboxes = process_face_detections(frame, detections, conf_threshold, bbox_color_bgr)1065                                    1066                                    # Display the processed frame1067                                    camera_placeholder.image(processed_frame, channels="BGR", use_container_width=True)1068                                    1069                                    # Small pause to avoid overloading the CPU1070                                    time.sleep(0.01)1071                            finally:1072                                # Release the camera when stopped1073                                cap.release()1074 1075    elif app_mode == "Feature Detection":1076        # Load all required models1077        face_net = load_face_model()1078        eye_cascade, smile_cascade = load_feature_models()1079        1080        # Feature selection checkboxes1081        st.sidebar.subheader("Feature Detection Options")1082        detect_eyes = st.sidebar.checkbox("Detect Eyes", True)1083        1084        # Add controls for eye detection sensitivity1085        eye_sensitivity = 5  # Default value1086        if detect_eyes:1087            eye_sensitivity = st.sidebar.slider(1088                "Eye Detection Sensitivity", 1089                min_value=1, 1090                max_value=10, 1091                value=5, 1092                step=1,1093                help="Adjust the sensitivity of eye detection (lower value = more detections)"1094            )1095        1096        detect_smile = st.sidebar.checkbox("Detect Smile", True)1097        1098        # Add controls for smile detection sensitivity1099        smile_sensitivity = 15  # Default value1100        if detect_smile:1101            smile_sensitivity = st.sidebar.slider(1102                "Smile Detection Sensitivity", 1103                min_value=5, 1104                max_value=30, 1105                value=15, 1106                step=1,1107                help="Adjust the sensitivity of smile detection (lower value = more detections)"1108            )1109        1110        detect_age_gender_option = st.sidebar.checkbox("Detect Age/Gender (Demo)", False)1111        1112        # Confidence threshold slider1113        conf_threshold = st.sidebar.slider(1114            "Face Detection Confidence", 1115            min_value=0.0, 1116            max_value=1.0, 1117            value=0.5, 1118            step=0.051119        )1120        1121        # Style options1122        bbox_color = st.sidebar.color_picker("Bounding Box Color", "#00FF00")1123        # Convert hex color to BGR for OpenCV1124        bbox_color_rgb = tuple(int(bbox_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))1125        bbox_color_bgr = (bbox_color_rgb[2], bbox_color_rgb[1], bbox_color_rgb[0])  # Convert RGB to BGR1126        1127        # Input type selection1128        input_type = st.sidebar.radio("Select Input Type", ["Image", "Video"])1129        1130        if input_type == "Image":1131            # File uploader for images1132            file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])1133            1134            # Umbral de confianza ajustable1135            conf_threshold = st.slider(1136                "Umbral de confianza",1137                min_value=0.05,1138                max_value=0.95,1139                value=0.2,  # Valor por defecto m谩s bajo (era 0.5)1140                step=0.05,1141                help="Ajusta este valor para controlar la sensibilidad de la detecci贸n facial. Un valor m谩s bajo detecta m谩s rostros pero puede tener falsos positivos."1142            )1143            1144            # Color del bounding box1145            bbox_color_bgr = (0, 255, 0)  # Verde brillante para mejor visibilidad1146            1147            if file_buffer is not None:1148                # Read the file and convert it to OpenCV format1149                raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)1150                image = cv2.imdecode(raw_bytes, cv2.IMREAD_COLOR)1151                1152                # Save la imagen original en session_state para reprocesarla cuando cambie el umbral1153                # Usar un identificador 煤nico para cada archivo para detectar cambios1154                file_id = file_buffer.name + str(file_buffer.size)1155                1156                if 'feature_file_id' not in st.session_state or st.session_state.feature_file_id != file_id:1157                    st.session_state.feature_file_id = file_id1158                    st.session_state.feature_original_image = image.copy()1159                1160                # Display original image1161                col1, col2 = st.columns(2)1162                with col1:1163                    st.subheader("Original Image")1164                    st.image(st.session_state.feature_original_image, channels='BGR', use_container_width=True)1165                1166                # Start processing with face detection1167                detections = detect_face_dnn(face_net, st.session_state.feature_original_image, conf_threshold)1168                processed_image, bboxes = process_face_detections(st.session_state.feature_original_image, detections, conf_threshold, bbox_color_bgr)1169                1170                # Inicializar contadores1171                eye_count = 01172                smile_count = 01173                1174                # Detect facial features if any options are enabled1175                if detect_eyes or detect_smile:1176                    processed_image, eye_count, smile_count = detect_facial_features(1177                        processed_image, 1178                        bboxes,1179                        eye_cascade,1180                        smile_cascade,1181                        detect_eyes,1182                        detect_smile,1183                        smile_sensitivity,1184                        eye_sensitivity1185                    )1186                    1187                # Apply age/gender detection if enabled (demo purpose)1188                if detect_age_gender_option:1189                    processed_image = detect_age_gender(processed_image, bboxes)1190                1191                # Display the processed image1192                with col2:1193                    st.subheader("Processed Image")1194                    st.image(processed_image, channels='BGR', use_container_width=True)1195                    1196                    # Mostrar mensaje sobre lo que se est谩 viendo1197                    if len(bboxes) > 0:1198                        st.success(f"Se detectaron {len(bboxes)} rostros en la imagen.")1199                    else:1200                        st.warning("No se detectaron rostros. Prueba ajustar el umbral de confianza o usar otra imagen.")

Showing the first 1,200 of 2647 lines. Download the file for the rest.