jarondon82/ComputerVisionProject
1
1import streamlit as st2import cv23import numpy as np4from PIL import Image5from io import BytesIO6import base647import tempfile8import os9import time10import urllib.request11import matplotlib.pyplot as plt12import pickle13from sklearn.metrics.pairwise import cosine_similarity # type: ignore14import pandas as pd15 16# Importar DeepFace para reconocimiento facial avanzado17try:18 from deepface import DeepFace19 DEEPFACE_AVAILABLE = True20except ImportError:21 DEEPFACE_AVAILABLE = False22 23# Import functions for face comparison24try:25 from face_comparison import compare_faces, compare_faces_embeddings, generate_comparison_report_english, draw_face_matches, extract_face_embeddings, extract_face_embeddings_all_models26 FACE_COMPARISON_AVAILABLE = True27except ImportError:28 FACE_COMPARISON_AVAILABLE = False29 st.warning("Face comparison functions are not available. Please check your installation.")30 31# Funci贸n principal que encapsula toda la aplicaci贸n32def main():33 # Set page config with custom title and layout34 st.set_page_config(35 page_title="Advanced Face & Feature Detection",36 page_icon="馃懁",37 layout="wide",38 initial_sidebar_state="expanded"39 )40 41 # Sidebar for navigation and controls42 st.sidebar.title("Controls & Settings")43 44 # Initialize session_state to store original image and camera state45 if 'original_image' not in st.session_state:46 st.session_state.original_image = None47 if 'camera_running' not in st.session_state:48 st.session_state.camera_running = False49 if 'feature_camera_running' not in st.session_state:50 st.session_state.feature_camera_running = False51 52 # Navigation menu53 app_mode = st.sidebar.selectbox(54 "Choose the app mode",55 ["About", "Face Detection", "Feature Detection", "Comparison Mode", "Face Recognition"]56 )57 58 # Function to load DNN models with caching and auto-download59 @st.cache_resource60 def load_face_model():61 # No need to create directory as we're using the root directory62 #63 #64 65 # Correct model file names66 modelFile = "res10_300x300_ssd_iter_140000.caffemodel"67 configFile = "deploy.prototxt.txt"68 69 # Check if files exist70 missing_files = []71 if not os.path.exists(modelFile):72 missing_files.append(modelFile)73 if not os.path.exists(configFile):74 missing_files.append(configFile)75 76 if missing_files:77 st.error("Missing model files: " + ", ".join(missing_files))78 st.error("Please manually download the following files:")79 st.code("""80 1. Download the model file:81 URL: https://raw.githubusercontent.com/sr6033/face-detection-with-OpenCV-and-DNN/master/res10_300x300_ssd_iter_140000.caffemodel82 Save as: res10_300x300_ssd_iter_140000.caffemodel83 84 2. Download the configuration file:85 URL: https://raw.githubusercontent.com/sr6033/face-detection-with-OpenCV-and-DNN/master/deploy.prototxt.txt86 Save as: deploy.prototxt.txt87 """)88 st.stop()89 90 # Load model91 try:92 net = cv2.dnn.readNetFromCaffe(configFile, modelFile)93 return net94 except Exception as e:95 st.error(f"Error loading model: {e}")96 st.stop()97 98 @st.cache_resource99 def load_feature_models():100 # Load pre-trained models for eye and smile detection101 eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')102 smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_smile.xml')103 return eye_cascade, smile_cascade104 105 # Function for detecting faces in an image106 def detect_face_dnn(net, frame, conf_threshold=0.5):107 blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], False, False)108 net.setInput(blob)109 detections = net.forward()110 111 # Procesar las detecciones para devolver una lista de bounding boxes112 bboxes = []113 frame_h = frame.shape[0]114 frame_w = frame.shape[1]115 116 for i in range(detections.shape[2]):117 confidence = detections[0, 0, i, 2]118 if confidence > conf_threshold:119 x1 = int(detections[0, 0, i, 3] * frame_w)120 y1 = int(detections[0, 0, i, 4] * frame_h)121 x2 = int(detections[0, 0, i, 5] * frame_w)122 y2 = int(detections[0, 0, i, 6] * frame_h)123 124 # Asegurarse de que las coordenadas est茅n dentro de los l铆mites de la imagen125 x1 = max(0, min(x1, frame_w - 1))126 y1 = max(0, min(y1, frame_h - 1))127 x2 = max(0, min(x2, frame_w - 1))128 y2 = max(0, min(y2, frame_h - 1))129 130 # A帽adir el bounding box y la confianza131 bboxes.append([x1, y1, x2, y2, confidence])132 133 return bboxes134 135 # Function for processing face detections136 def process_face_detections(frame, detections, conf_threshold=0.5, bbox_color=(0, 255, 0)):137 # Create a copy for drawing on138 result_frame = frame.copy()139 140 # Filtrar detecciones por umbral de confianza141 bboxes = []142 for detection in detections:143 if len(detection) == 5: # Asegurarse de que la detecci贸n tiene el formato correcto144 x1, y1, x2, y2, confidence = detection145 if confidence >= conf_threshold:146 # Dibujar el bounding box147 cv2.rectangle(result_frame, (x1, y1), (x2, y2), bbox_color, 2)148 149 # A帽adir texto con la confianza150 label = f"{confidence:.2f}"151 cv2.putText(result_frame, label, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, bbox_color, 2)152 153 # A帽adir a la lista de bounding boxes154 bboxes.append([x1, y1, x2, y2, confidence])155 156 return result_frame, bboxes157 158 # Function to detect facial features (eyes, smile) with improved profile face handling159 def detect_facial_features(frame, bboxes, eye_cascade, smile_cascade, detect_eyes=True, detect_smile=True, smile_sensitivity=15, eye_sensitivity=5):160 result_frame = frame.copy()161 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)162 163 # Counters for detection summary164 eye_count = 0165 smile_count = 0166 167 for bbox in bboxes:168 x1, y1, x2, y2, _ = bbox169 roi_gray = gray[y1:y2, x1:x2]170 roi_color = result_frame[y1:y2, x1:x2]171 face_width = x2 - x1172 face_height = y2 - y1173 174 # Detect eyes if enabled175 if detect_eyes:176 # Adjust region of interest to focus on the upper part of the face177 upper_face_y1 = y1178 upper_face_y2 = y1 + int(face_height * 0.55) # Slightly reduced to focus more on the eye area179 180 # For profile faces, we need to search the entire upper region181 # as well as the left and right sides separately182 183 # Full upper region for profile faces184 upper_face_roi_gray = gray[upper_face_y1:upper_face_y2, x1:x2]185 upper_face_roi_color = result_frame[upper_face_y1:upper_face_y2, x1:x2]186 187 # Split the upper region into two halves (left and right) to search for eyes individually188 mid_x = x1 + face_width // 2189 left_eye_roi_gray = gray[upper_face_y1:upper_face_y2, x1:mid_x]190 right_eye_roi_gray = gray[upper_face_y1:upper_face_y2, mid_x:x2]191 192 left_eye_roi_color = result_frame[upper_face_y1:upper_face_y2, x1:mid_x]193 right_eye_roi_color = result_frame[upper_face_y1:upper_face_y2, mid_x:x2]194 195 # Apply histogram equalization and contrast enhancement for all regions196 if upper_face_roi_gray.size > 0:197 upper_face_roi_gray = cv2.equalizeHist(upper_face_roi_gray)198 199 # Enhance contrast200 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))201 upper_face_roi_gray = clahe.apply(upper_face_roi_gray)202 203 # First try to detect eyes in the full upper region (for profile faces)204 full_eyes = eye_cascade.detectMultiScale(205 upper_face_roi_gray, 206 scaleFactor=1.02, # More sensitive for profile faces207 minNeighbors=max(1, eye_sensitivity-3), # Even more sensitive208 minSize=(int(face_width * 0.07), int(face_width * 0.07)),209 maxSize=(int(face_width * 0.3), int(face_width * 0.3))210 )211 212 # If we found eyes in the full region, use those213 if len(full_eyes) > 0:214 # Sort by size (area) and take up to 2 largest215 full_eyes = sorted(full_eyes, key=lambda e: e[2] * e[3], reverse=True)216 full_eyes = full_eyes[:2] # Take at most 2 eyes217 218 for ex, ey, ew, eh in full_eyes:219 eye_count += 1220 cv2.rectangle(upper_face_roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)221 cv2.putText(upper_face_roi_color, "Eye", (ex, ey-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)222 else:223 # If no eyes found in full region, try left and right separately224 if left_eye_roi_gray.size > 0:225 left_eye_roi_gray = cv2.equalizeHist(left_eye_roi_gray)226 left_eye_roi_gray = clahe.apply(left_eye_roi_gray)227 228 left_eyes = eye_cascade.detectMultiScale(229 left_eye_roi_gray, 230 scaleFactor=1.03,231 minNeighbors=max(1, eye_sensitivity-2),232 minSize=(int(face_width * 0.08), int(face_width * 0.08)),233 maxSize=(int(face_width * 0.25), int(face_width * 0.25))234 )235 236 if len(left_eyes) > 0:237 # Sort by size and take the largest238 left_eyes = sorted(left_eyes, key=lambda e: e[2] * e[3], reverse=True)239 left_eye = left_eyes[0]240 eye_count += 1241 242 # Draw rectangle for the left eye243 ex, ey, ew, eh = left_eye244 cv2.rectangle(left_eye_roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)245 cv2.putText(left_eye_roi_color, "Eye", (ex, ey-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)246 247 if right_eye_roi_gray.size > 0:248 right_eye_roi_gray = cv2.equalizeHist(right_eye_roi_gray)249 right_eye_roi_gray = clahe.apply(right_eye_roi_gray)250 251 right_eyes = eye_cascade.detectMultiScale(252 right_eye_roi_gray, 253 scaleFactor=1.03,254 minNeighbors=max(1, eye_sensitivity-2),255 minSize=(int(face_width * 0.08), int(face_width * 0.08)),256 maxSize=(int(face_width * 0.25), int(face_width * 0.25))257 )258 259 if len(right_eyes) > 0:260 # Sort by size and take the largest261 right_eyes = sorted(right_eyes, key=lambda e: e[2] * e[3], reverse=True)262 right_eye = right_eyes[0]263 eye_count += 1264 265 # Draw rectangle for the right eye266 ex, ey, ew, eh = right_eye267 cv2.rectangle(right_eye_roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)268 cv2.putText(right_eye_roi_color, "Eye", (ex, ey-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)269 270 # Detect smile if enabled271 if detect_smile:272 # For profile faces, we need to adjust the region of interest273 # Try multiple regions to improve detection274 275 # Standard region (middle to bottom)276 lower_face_y1 = y1 + int(face_height * 0.5)277 lower_face_roi_gray = gray[lower_face_y1:y2, x1:x2]278 lower_face_roi_color = result_frame[lower_face_y1:y2, x1:x2]279 280 # Alternative region (lower third)281 alt_lower_face_y1 = y1 + int(face_height * 0.65)282 alt_lower_face_roi_gray = gray[alt_lower_face_y1:y2, x1:x2]283 284 # Apply histogram equalization and enhance contrast285 smile_detected = False286 287 if lower_face_roi_gray.size > 0:288 lower_face_roi_gray = cv2.equalizeHist(lower_face_roi_gray)289 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))290 lower_face_roi_gray = clahe.apply(lower_face_roi_gray)291 292 # Try with standard parameters293 smiles = smile_cascade.detectMultiScale(294 lower_face_roi_gray, 295 scaleFactor=1.2,296 minNeighbors=smile_sensitivity,297 minSize=(int(face_width * 0.25), int(face_width * 0.15)),298 maxSize=(int(face_width * 0.7), int(face_width * 0.4))299 )300 301 if len(smiles) > 0:302 # Sort by size and take the largest303 smiles = sorted(smiles, key=lambda s: s[2] * s[3], reverse=True)304 sx, sy, sw, sh = smiles[0]305 306 # Increment smile counter307 smile_count += 1308 smile_detected = True309 310 # Draw rectangle for the smile311 cv2.rectangle(lower_face_roi_color, (sx, sy), (sx+sw, sy+sh), (0, 0, 255), 2)312 cv2.putText(lower_face_roi_color, "Smile", (sx, sy-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)313 314 # If no smile detected in standard region, try alternative region315 if not smile_detected and alt_lower_face_roi_gray.size > 0:316 alt_lower_face_roi_gray = cv2.equalizeHist(alt_lower_face_roi_gray)317 alt_lower_face_roi_gray = clahe.apply(alt_lower_face_roi_gray)318 319 # Try with more sensitive parameters320 alt_smiles = smile_cascade.detectMultiScale(321 alt_lower_face_roi_gray, 322 scaleFactor=1.1,323 minNeighbors=max(1, smile_sensitivity-5), # More sensitive324 minSize=(int(face_width * 0.2), int(face_width * 0.1)),325 maxSize=(int(face_width * 0.6), int(face_width * 0.3))326 )327 328 if len(alt_smiles) > 0:329 # Sort by size and take the largest330 alt_smiles = sorted(alt_smiles, key=lambda s: s[2] * s[3], reverse=True)331 sx, sy, sw, sh = alt_smiles[0]332 333 # Adjust coordinates for the alternative region334 adjusted_sy = sy + (alt_lower_face_y1 - lower_face_y1)335 336 # Increment smile counter337 smile_count += 1338 339 # Draw rectangle for the smile (in the original lower face ROI)340 cv2.rectangle(lower_face_roi_color, (sx, adjusted_sy), (sx+sw, adjusted_sy+sh), (0, 0, 255), 2)341 cv2.putText(lower_face_roi_color, "Smile", (sx, adjusted_sy-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)342 343 return result_frame, eye_count, smile_count344 345 # Funci贸n para detectar atributos faciales (edad, g茅nero, emoci贸n)346 def detect_face_attributes(image, bbox):347 """348 Detecta atributos faciales como edad, g茅nero y emoci贸n usando DeepFace.349 350 Args:351 image: Imagen en formato OpenCV (BGR)352 bbox: Bounding box de la cara [x1, y1, x2, y2, conf]353 354 Returns:355 Diccionario con los atributos detectados356 """357 if not DEEPFACE_AVAILABLE:358 return None359 360 try:361 x1, y1, x2, y2, _ = bbox362 face_img = image[y1:y2, x1:x2]363 364 # Convertir de BGR a RGB para DeepFace365 face_img_rgb = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)366 367 # Analyze atributos faciales368 attributes = DeepFace.analyze(369 img_path=face_img_rgb,370 actions=['age', 'gender', 'emotion'],371 enforce_detection=False,372 detector_backend="opencv"373 )374 375 return attributes[0]376 377 except Exception as e:378 st.error(f"Error detecting facial attributes: {str(e)}")379 return None380 381 # Function to apply age and gender detection (placeholder - would need additional models)382 def detect_age_gender(frame, bboxes):383 # Versi贸n mejorada que usa DeepFace si est谩 disponible384 result_frame = frame.copy()385 386 for i, bbox in enumerate(bboxes):387 x1, y1, x2, y2, _ = bbox388 389 if DEEPFACE_AVAILABLE:390 # Intentar usar DeepFace para an谩lisis facial391 attributes = detect_face_attributes(frame, bbox)392 393 if attributes:394 # Extraer informaci贸n de atributos395 age = attributes.get('age', 'Unknown')396 gender = attributes.get('gender', 'Unknown')397 emotion = attributes.get('dominant_emotion', 'Unknown').capitalize()398 gender_prob = attributes.get('gender', {}).get('Woman', 0)399 400 # Determinar color basado en confianza401 if gender == 'Woman':402 gender_color = (255, 0, 255) # Magenta para mujer403 else:404 gender_color = (255, 0, 0) # Azul para hombre405 406 # A帽adir texto con informaci贸n407 cv2.putText(result_frame, f"Age: {age}", (x1, y2+20), 408 cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)409 cv2.putText(result_frame, f"Gender: {gender}", (x1, y2+40), 410 cv2.FONT_HERSHEY_SIMPLEX, 0.5, gender_color, 2)411 cv2.putText(result_frame, f"Emotion: {emotion}", (x1, y2+60), 412 cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)413 else:414 # Fallback si DeepFace falla415 cv2.putText(result_frame, "Age: Unknown", (x1, y2+20), 416 cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)417 cv2.putText(result_frame, "Gender: Unknown", (x1, y2+40), 418 cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)419 else:420 # Usar texto placeholder si DeepFace no est谩 disponible421 cv2.putText(result_frame, "Age: 25-35", (x1, y2+20), 422 cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)423 cv2.putText(result_frame, "Gender: Unknown", (x1, y2+40), 424 cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)425 426 return result_frame427 428 # Function to generate download link for processed image429 def get_image_download_link(img, filename, text):430 buffered = BytesIO()431 img.save(buffered, format="JPEG")432 img_str = base64.b64encode(buffered.getvalue()).decode()433 href = f'<a href="data:file/txt;base64,{img_str}" download="{filename}">{text}</a>'434 return href435 436 # Function to process video frames437 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):438 cap = cv2.VideoCapture(video_path)439 440 # Get video properties441 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))442 frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))443 fps = int(cap.get(cv2.CAP_PROP_FPS))444 445 # Create temporary output file446 temp_dir = tempfile.mkdtemp()447 temp_output_path = os.path.join(temp_dir, "processed_video.mp4")448 449 # Initialize video writer450 fourcc = cv2.VideoWriter_fourcc(*'mp4v')451 out = cv2.VideoWriter(temp_output_path, fourcc, fps, (frame_width, frame_height))452 453 # Create a progress bar454 frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))455 progress_bar = st.progress(0)456 status_text = st.empty()457 458 # Process video frames459 current_frame = 0460 processing_times = []461 462 # Total counters for statistics463 total_faces = 0464 total_eyes = 0465 total_smiles = 0466 467 while cap.isOpened():468 ret, frame = cap.read()469 if not ret:470 break471 472 # Start timing for performance metrics473 start_time = time.time()474 475 # Detect faces476 detections = detect_face_dnn(face_net, frame, conf_threshold)477 processed_frame, bboxes = process_face_detections(frame, detections, conf_threshold, bbox_color)478 479 # Update face counter480 total_faces += len(bboxes)481 482 # Detect facial features if enabled483 if detect_eyes or detect_smile:484 processed_frame, eye_count, smile_count = detect_facial_features(485 processed_frame, 486 bboxes, 487 eye_cascade, 488 smile_cascade,489 detect_eyes,490 detect_smile,491 smile_sensitivity,492 eye_sensitivity493 )494 # Update counters495 total_eyes += eye_count496 total_smiles += smile_count497 498 # End timing499 processing_times.append(time.time() - start_time)500 501 # Write the processed frame502 out.write(processed_frame)503 504 # Update progress505 current_frame += 1506 progress_bar.progress(current_frame / frame_count)507 status_text.text(f"Processing frame {current_frame}/{frame_count}")508 509 # Release resources510 cap.release()511 out.release()512 513 # Calculate and display performance metrics514 if processing_times:515 avg_time = sum(processing_times) / len(processing_times)516 status_text.text(f"Processing complete! Average processing time: {avg_time:.4f}s per frame")517 518 # Return detection statistics519 detection_stats = {520 "faces": total_faces // max(1, current_frame), # Average per frame521 "eyes": total_eyes // max(1, current_frame), # Average per frame522 "smiles": total_smiles // max(1, current_frame) # Average per frame523 }524 525 return temp_output_path, temp_dir, detection_stats526 527 # Camera control functions528 def start_camera():529 st.session_state.camera_running = True530 531 def stop_camera():532 st.session_state.camera_running = False533 st.session_state.camera_stopped = True534 535 def start_feature_camera():536 st.session_state.feature_camera_running = True537 538 def stop_feature_camera():539 st.session_state.feature_camera_running = False540 st.session_state.feature_camera_stopped = True541 542 if app_mode == "About":543 st.markdown("""544 ## About This App545 546 This application uses OpenCV's Deep Neural Network (DNN) module and Haar Cascade classifiers to detect faces and facial features in images and videos.547 548 ### Features:549 - Face detection using OpenCV DNN550 - Eye and smile detection using Haar Cascades551 - Support for both image and video processing552 - Adjustable confidence threshold553 - Download options for processed media554 - Performance metrics555 556 ### How to use:557 1. Select a mode from the sidebar558 2. Upload an image or video559 3. Adjust settings as needed560 4. View and download the results561 562 ### Technologies Used:563 - Streamlit for the web interface564 - OpenCV for computer vision operations565 - Python for backend processing566 567 ### Models:568 - SSD MobileNet for face detection569 - Haar Cascades for facial features570 """)571 572 # Display a sample image or GIF573 st.image("https://opencv.org/wp-content/uploads/2019/07/detection.gif", caption="Sample face detection", use_container_width=True)574 575 elif app_mode == "Face Detection":576 # Load the face detection model577 face_net = load_face_model()578 579 # Input type selection (Image or Video)580 input_type = st.sidebar.radio("Select Input Type", ["Image", "Video"])581 582 # Confidence threshold slider583 conf_threshold = st.sidebar.slider(584 "Confidence Threshold", 585 min_value=0.0, 586 max_value=1.0, 587 value=0.5, 588 step=0.05,589 help="Adjust the threshold for face detection confidence (higher = fewer detections but more accurate)"590 )591 592 # Style options593 bbox_color = st.sidebar.color_picker("Bounding Box Color", "#00FF00")594 # Convert hex color to BGR for OpenCV595 bbox_color_rgb = tuple(int(bbox_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))596 bbox_color_bgr = (bbox_color_rgb[2], bbox_color_rgb[1], bbox_color_rgb[0]) # Convert RGB to BGR597 598 # Display processing metrics599 show_metrics = st.sidebar.checkbox("Show Processing Metrics", True)600 601 if input_type == "Image":602 # File uploader for images603 file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])604 605 if file_buffer is not None:606 # Read the file and convert it to OpenCV format607 raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)608 image = cv2.imdecode(raw_bytes, cv2.IMREAD_COLOR)609 610 # Save la imagen original en session_state para reprocesarla cuando cambie el umbral611 # Usar un identificador 煤nico para cada archivo para detectar cambios612 file_id = file_buffer.name + str(file_buffer.size)613 614 if 'file_id' not in st.session_state or st.session_state.file_id != file_id:615 st.session_state.file_id = file_id616 st.session_state.original_image = image.copy()617 618 # Display original image619 col1, col2 = st.columns(2)620 with col1:621 st.subheader("Original Image")622 st.image(st.session_state.original_image, channels='BGR', use_container_width=True)623 624 # Start timing for performance metrics625 start_time = time.time()626 627 # Detect faces628 detections = detect_face_dnn(face_net, st.session_state.original_image, conf_threshold)629 processed_image, bboxes = process_face_detections(st.session_state.original_image, detections, conf_threshold, bbox_color_bgr)630 631 # Calculate processing time632 processing_time = time.time() - start_time633 634 # Display the processed image635 with col2:636 st.subheader("Processed Image")637 st.image(processed_image, channels='BGR', use_container_width=True)638 639 # Convert OpenCV image to PIL for download640 pil_img = Image.fromarray(processed_image[:, :, ::-1])641 st.markdown(642 get_image_download_link(pil_img, "face_detection_result.jpg", "馃摜 Download Processed Image"),643 unsafe_allow_html=True644 )645 646 # Show metrics if enabled647 if show_metrics:648 st.subheader("Processing Metrics")649 col1, col2, col3 = st.columns(3)650 col1.metric("Processing Time", f"{processing_time:.4f} seconds")651 col2.metric("Faces Detected", len(bboxes))652 col3.metric("Confidence Threshold", f"{conf_threshold:.2f}")653 654 # Display detailed metrics in an expandable section655 with st.expander("Detailed Detection Information"):656 if bboxes:657 st.write("Detected faces with confidence scores:")658 for i, bbox in enumerate(bboxes):659 st.write(f"Face #{i+1}: Confidence = {bbox[4]:.4f}")660 else:661 st.write("No faces detected in the image.")662 663 else: # Video mode664 # Video mode options665 video_source = st.radio("Select video source", ["Upload video", "Use webcam"])666 667 if video_source == "Upload video":668 # File uploader for videos669 file_buffer = st.file_uploader("Upload a video", type=['mp4', 'avi', 'mov'])670 671 if file_buffer is not None:672 # Save uploaded video to temporary file673 temp_dir = tempfile.mkdtemp()674 temp_path = os.path.join(temp_dir, "input_video.mp4")675 676 with open(temp_path, "wb") as f:677 f.write(file_buffer.read())678 679 # Display original video680 st.subheader("Original Video")681 st.video(temp_path)682 683 # Load models for feature detection (will be used in the processing)684 eye_cascade, smile_cascade = load_feature_models()685 686 # Process video button687 if st.button("Process Video"):688 with st.spinner("Processing video... This may take a while depending on the video length."):689 # Process the video690 output_path, output_dir, detection_stats = process_video(691 temp_path, 692 face_net, 693 eye_cascade,694 smile_cascade,695 conf_threshold,696 detect_eyes=True,697 detect_smile=True,698 bbox_color=bbox_color_bgr,699 eye_sensitivity=5700 )701 702 # Display processed video703 st.subheader("Processed Video")704 st.video(output_path)705 706 # Mostrar estad铆sticas de detecci贸n707 st.subheader("Detection Summary")708 summary_col1, summary_col2, summary_col3 = st.columns(3)709 summary_col1.metric("Avg. Faces per Frame", detection_stats["faces"])710 711 if detect_eyes: # type: ignore712 summary_col2.metric("Avg. Eyes per Frame", detection_stats["eyes"])713 else:714 summary_col2.metric("Eyes Detected", "N/A")715 716 if detect_smile: # type: ignore717 summary_col3.metric("Avg. Smiles per Frame", detection_stats["smiles"])718 else:719 summary_col3.metric("Smiles Detected", "N/A")720 721 # Provide download link722 with open(output_path, 'rb') as f:723 video_bytes = f.read()724 725 st.download_button(726 label="馃摜 Download Processed Video",727 data=video_bytes,728 file_name="processed_video.mp4",729 mime="video/mp4"730 )731 732 # Clean up temporary files733 try:734 os.remove(temp_path)735 os.remove(output_path)736 os.rmdir(temp_dir)737 os.rmdir(output_dir)738 except:739 pass740 else: # Use webcam741 st.subheader("Real-time face detection")742 st.write("Click 'Start Camera' to begin real-time face detection.")743 744 # Placeholder for webcam video745 camera_placeholder = st.empty()746 747 # Buttons to control the camera748 col1, col2 = st.columns(2)749 start_button = col1.button("Start Camera", on_click=start_camera)750 stop_button = col2.button("Stop Camera", on_click=stop_camera)751 752 # Show message when camera is stopped753 if 'camera_stopped' in st.session_state and st.session_state.camera_stopped:754 st.info("Camera stopped. Click 'Start Camera' to activate it again.")755 st.session_state.camera_stopped = False756 757 if st.session_state.camera_running:758 st.info("Camera activated. Processing real-time video...")759 # Initialize webcam760 cap = cv2.VideoCapture(0) # 0 is typically the main webcam761 762 if not cap.isOpened():763 st.error("Could not access webcam. Make sure it's connected and not being used by another application.")764 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.")765 st.session_state.camera_running = False766 else:767 # Display real-time video with face detection768 try:769 while st.session_state.camera_running:770 ret, frame = cap.read()771 if not ret:772 st.error("Error reading frame from camera.")773 break774 775 # Detect faces776 detections = detect_face_dnn(face_net, frame, conf_threshold)777 processed_frame, bboxes = process_face_detections(frame, detections, conf_threshold, bbox_color_bgr)778 779 # Display the processed frame780 camera_placeholder.image(processed_frame, channels="BGR", use_container_width=True)781 782 # Small pause to avoid overloading the CPU783 time.sleep(0.01)784 finally:785 # Release the camera when stopped786 cap.release()787 788 elif app_mode == "Feature Detection":789 # Load all required models790 face_net = load_face_model()791 eye_cascade, smile_cascade = load_feature_models()792 793 # Feature selection checkboxes794 st.sidebar.subheader("Feature Detection Options")795 detect_eyes = st.sidebar.checkbox("Detect Eyes", True)796 797 # Add controls for eye detection sensitivity798 eye_sensitivity = 5 # Default value799 if detect_eyes:800 eye_sensitivity = st.sidebar.slider(801 "Eye Detection Sensitivity", 802 min_value=1, 803 max_value=10, 804 value=5, 805 step=1,806 help="Adjust the sensitivity of eye detection (lower value = more detections)"807 )808 809 detect_smile = st.sidebar.checkbox("Detect Smile", True)810 811 # Add controls for smile detection sensitivity812 smile_sensitivity = 15 # Default value813 if detect_smile:814 smile_sensitivity = st.sidebar.slider(815 "Smile Detection Sensitivity", 816 min_value=5, 817 max_value=30, 818 value=15, 819 step=1,820 help="Adjust the sensitivity of smile detection (lower value = more detections)"821 )822 823 detect_age_gender_option = st.sidebar.checkbox("Detect Age/Gender (Demo)", False)824 825 # Confidence threshold slider826 conf_threshold = st.sidebar.slider(827 "Face Detection Confidence", 828 min_value=0.0, 829 max_value=1.0, 830 value=0.5, 831 step=0.05832 )833 834 # Style options835 bbox_color = st.sidebar.color_picker("Bounding Box Color", "#00FF00")836 # Convert hex color to BGR for OpenCV837 bbox_color_rgb = tuple(int(bbox_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))838 bbox_color_bgr = (bbox_color_rgb[2], bbox_color_rgb[1], bbox_color_rgb[0]) # Convert RGB to BGR839 840 # Input type selection841 input_type = st.sidebar.radio("Select Input Type", ["Image", "Video"])842 843 if input_type == "Image":844 # File uploader for images845 file_buffer = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])846 847 if file_buffer is not None:848 # Read the file and convert it to OpenCV format849 raw_bytes = np.asarray(bytearray(file_buffer.read()), dtype=np.uint8)850 image = cv2.imdecode(raw_bytes, cv2.IMREAD_COLOR)851 852 # Save la imagen original en session_state para reprocesarla cuando cambie el umbral853 # Usar un identificador 煤nico para cada archivo para detectar cambios854 file_id = file_buffer.name + str(file_buffer.size)855 856 if 'feature_file_id' not in st.session_state or st.session_state.feature_file_id != file_id:857 st.session_state.feature_file_id = file_id858 st.session_state.feature_original_image = image.copy()859 860 # Display original image861 col1, col2 = st.columns(2)862 with col1:863 st.subheader("Original Image")864 st.image(st.session_state.feature_original_image, channels='BGR', use_container_width=True)865 866 # Start processing with face detection867 detections = detect_face_dnn(face_net, st.session_state.feature_original_image, conf_threshold)868 processed_image, bboxes = process_face_detections(st.session_state.feature_original_image, detections, conf_threshold, bbox_color_bgr)869 870 # Inicializar contadores871 eye_count = 0872 smile_count = 0873 874 # Detect facial features if any options are enabled875 if detect_eyes or detect_smile:876 processed_image, eye_count, smile_count = detect_facial_features(877 processed_image, 878 bboxes,879 eye_cascade,880 smile_cascade,881 detect_eyes,882 detect_smile,883 smile_sensitivity,884 eye_sensitivity885 )886 887 # Apply age/gender detection if enabled (demo purpose)888 if detect_age_gender_option:889 processed_image = detect_age_gender(processed_image, bboxes)890 891 # Display the processed image892 with col2:893 st.subheader("Processed Image")894 st.image(processed_image, channels='BGR', use_container_width=True)895 896 # Convert OpenCV image to PIL for download897 pil_img = Image.fromarray(processed_image[:, :, ::-1])898 st.markdown(899 get_image_download_link(pil_img, "feature_detection_result.jpg", "馃摜 Download Processed Image"),900 unsafe_allow_html=True901 )902 903 # Display detection summary904 st.subheader("Detection Summary")905 summary_col1, summary_col2, summary_col3 = st.columns(3)906 summary_col1.metric("Faces Detected", len(bboxes))907 908 if detect_eyes:909 summary_col2.metric("Eyes Detected", eye_count)910 else:911 summary_col2.metric("Eyes Detected", "N/A")912 913 if detect_smile:914 summary_col3.metric("Smiles Detected", smile_count)915 else:916 summary_col3.metric("Smiles Detected", "N/A")917 918 else: # Video mode919 st.write("Facial feature detection in video")920 921 # Video mode options922 video_source = st.radio("Select video source", ["Upload video", "Use webcam"])923 924 if video_source == "Upload video":925 st.write("Upload a video to process with facial feature detection.")926 # Similar implementation to Face Detection mode for uploaded videos927 file_buffer = st.file_uploader("Upload a video", type=['mp4', 'avi', 'mov'])928 929 if file_buffer is not None:930 # Save uploaded video to temporary file931 temp_dir = tempfile.mkdtemp()932 temp_path = os.path.join(temp_dir, "input_video.mp4")933 934 with open(temp_path, "wb") as f:935 f.write(file_buffer.read())936 937 # Display original video938 st.subheader("Original Video")939 st.video(temp_path)940 941 # Process video button942 if st.button("Process Video"):943 with st.spinner("Processing video... This may take a while depending on the video length."):944 # Process the video with feature detection945 output_path, output_dir, detection_stats = process_video(946 temp_path, 947 face_net, 948 eye_cascade,949 smile_cascade,950 conf_threshold,951 detect_eyes=True,952 detect_smile=True,953 bbox_color=bbox_color_bgr,954 smile_sensitivity=smile_sensitivity,955 eye_sensitivity=eye_sensitivity956 )957 958 # Display processed video959 st.subheader("Processed Video")960 st.video(output_path)961 962 # Mostrar estad铆sticas de detecci贸n963 st.subheader("Detection Summary")964 summary_col1, summary_col2, summary_col3 = st.columns(3)965 summary_col1.metric("Avg. Faces per Frame", detection_stats["faces"])966 967 if detect_eyes:968 summary_col2.metric("Avg. Eyes per Frame", detection_stats["eyes"])969 else:970 summary_col2.metric("Eyes Detected", "N/A")971 972 if detect_smile:973 summary_col3.metric("Avg. Smiles per Frame", detection_stats["smiles"])974 else:975 summary_col3.metric("Smiles Detected", "N/A")976 977 # Provide download link978 with open(output_path, 'rb') as f:979 video_bytes = f.read()980 981 st.download_button(982 label="馃摜 Download Processed Video",983 data=video_bytes,984 file_name="feature_detection_video.mp4",985 mime="video/mp4"986 )987 988 # Clean up temporary files989 try:990 os.remove(temp_path)991 os.remove(output_path)992 os.rmdir(temp_dir)993 os.rmdir(output_dir)994 except:995 pass996 else: # Usar c谩mara web997 st.subheader("Real-time facial feature detection")998 st.write("Click 'Start Camera' to begin real-time detection.")999 1000 # Placeholder for webcam video1001 camera_placeholder = st.empty()1002 1003 # Buttons to control the camera1004 col1, col2 = st.columns(2)1005 start_button = col1.button("Start Camera", on_click=start_feature_camera)1006 stop_button = col2.button("Stop Camera", on_click=stop_feature_camera)1007 1008 # Show message when camera is stopped1009 if 'feature_camera_stopped' in st.session_state and st.session_state.feature_camera_stopped:1010 st.info("Camera stopped. Click 'Start Camera' to activate it again.")1011 st.session_state.feature_camera_stopped = False1012 1013 if st.session_state.feature_camera_running:1014 st.info("Camera activated. Processing real-time video with feature detection...")1015 # Initialize webcam1016 cap = cv2.VideoCapture(0) # 0 is typically the main webcam1017 1018 if not cap.isOpened():1019 st.error("Could not access webcam. Make sure it's connected and not being used by another application.")1020 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.")1021 st.session_state.feature_camera_running = False1022 else:1023 # Display real-time video with face and feature detection1024 try:1025 # Create placeholders for metrics1026 metrics_placeholder = st.empty()1027 metrics_col1, metrics_col2, metrics_col3 = metrics_placeholder.columns(3)1028 1029 # Initialize counters1030 face_count_total = 01031 eye_count_total = 01032 smile_count_total = 01033 frame_count = 01034 1035 while st.session_state.feature_camera_running:1036 ret, frame = cap.read()1037 if not ret:1038 st.error("Error reading frame from camera.")1039 break1040 1041 # Detect faces1042 detections = detect_face_dnn(face_net, frame, conf_threshold)1043 processed_frame, bboxes = process_face_detections(frame, detections, conf_threshold, bbox_color_bgr)1044 1045 # Update face counter1046 face_count = len(bboxes)1047 face_count_total += face_count1048 1049 # Initialize counters for this frame1050 eye_count = 01051 smile_count = 01052 1053 # Detect facial features if enabled1054 if detect_eyes or detect_smile:1055 processed_frame, eye_count, smile_count = detect_facial_features(1056 processed_frame, 1057 bboxes,1058 eye_cascade,1059 smile_cascade,1060 detect_eyes,1061 detect_smile,1062 smile_sensitivity,1063 eye_sensitivity1064 )1065 1066 # Update total counters1067 eye_count_total += eye_count1068 smile_count_total += smile_count1069 1070 # Apply age/gender detection if enabled1071 if detect_age_gender_option:1072 processed_frame = detect_age_gender(processed_frame, bboxes)1073 1074 # Display the processed frame1075 camera_placeholder.image(processed_frame, channels="BGR", use_container_width=True)1076 1077 # Update frame counter1078 frame_count += 11079 1080 # Update metrics every 5 frames to avoid overloading the interface1081 if frame_count % 5 == 0:1082 metrics_col1.metric("Faces Detected", face_count)1083 1084 if detect_eyes:1085 metrics_col2.metric("Eyes Detected", eye_count)1086 else:1087 metrics_col2.metric("Eyes Detected", "N/A")1088 1089 if detect_smile:1090 metrics_col3.metric("Smiles Detected", smile_count)1091 else:1092 metrics_col3.metric("Smiles Detected", "N/A")1093 1094 # Small pause to avoid overloading the CPU1095 time.sleep(0.01)1096 finally:1097 # Release the camera when stopped1098 cap.release()1099 1100 elif app_mode == "Comparison Mode":1101 st.subheader("Face Comparison")1102 st.write("Upload two images to compare faces between them.")1103 1104 # A帽adir explicaci贸n sobre la interpretaci贸n de resultados1105 with st.expander("馃搶 How to interpret similarity results"):1106 st.markdown("""1107 ### Facial Similarity Interpretation Guide1108 1109 The system calculates similarity between faces based on multiple facial features and characteristics.1110 1111 **Similarity Ranges:**1112 - **70-100%**: HIGH Similarity - Very likely to be the same person or identical twins1113 - **50-70%**: MEDIUM Similarity - Possible match, requires verification1114 - **30-50%**: LOW Similarity - Different people with some similar features1115 - **0-30%**: VERY LOW Similarity - Completely different people1116 1117 **Enhanced Comparison System:**1118 The system uses a sophisticated approach that:1119 1. Analyzes multiple facial characteristics with advanced precision1120 2. Evaluates hair style/color, facial structure, texture patterns, and expressions with improved accuracy1121 3. Applies a balanced differentiation between similar and different individuals1122 4. Creates a clear gap between similar and different people's scores1123 5. Reduces scores for people with different facial structures1124 6. Applies penalty factors for critical differences in facial features1125 1126 **Features Analyzed:**1127 - Facial texture patterns (HOG features)1128 - Eye region characteristics (highly weighted)1129 - Nose bridge features1130 - Hair style and color patterns (enhanced detection)1131 - Precise facial proportions and structure1132 - Texture and edge patterns1133 - Facial expressions1134 - Critical difference markers (aspect ratio, brightness patterns, texture variance)1135 1136 **Factors affecting similarity:**1137 - Face angle and expression1138 - Lighting conditions1139 - Age differences1140 - Image quality1141 - Gender characteristics (with stronger weighting)1142 - Critical facial structure differences1143 1144 **Important note:** This system is designed to provide highly accurate similarity scores that create a clear distinction between different individuals while still recognizing truly similar people. The algorithm now applies multiple reduction factors to ensure that different people receive appropriately low similarity scores. For official identification, always use certified systems.1145 """)1146 1147 # Load face detection model1148 face_net = load_face_model()1149 1150 # Side-by-side file uploaders1151 col1, col2 = st.columns(2)1152 1153 with col1:1154 st.write("First Image")1155 file1 = st.file_uploader("Upload first image", type=['jpg', 'jpeg', 'png'], key="file1")1156 1157 with col2:1158 st.write("Second Image")1159 file2 = st.file_uploader("Upload second image", type=['jpg', 'jpeg', 'png'], key="file2")1160 1161 # Set confidence threshold1162 conf_threshold = st.slider("Face Detection Confidence", min_value=0.0, max_value=1.0, value=0.5, step=0.05)1163 1164 # Similarity threshold for considering a match1165 similarity_threshold = st.slider("Similarity Threshold (%)", min_value=35.0, max_value=95.0, value=45.0, step=5.0,1166 help="Minimum percentage of similarity to consider two faces as a match")1167 1168 # Selecci贸n del m茅todo de comparaci贸n1169 comparison_method = st.radio(1170 "Facial Comparison Method",1171 ["HOG (Fast, effective)", "Embeddings (Slow, more precise)"],1172 help="HOG uses histograms of oriented gradients for quick comparison. Embeddings use deep neural networks for greater precision."1173 )1174 1175 # Si se selecciona embeddings, mostrar opciones de modelos y advertencia1176 embedding_model = "VGG-Face"1177 if comparison_method == "Embeddings (Slow, more precise)" and DEEPFACE_AVAILABLE:1178 st.warning("WARNING: The current version of TensorFlow (2.19) may have incompatibilities with some models. It is recommended to use HOG if you experience problems.")1179 1180 embedding_model = st.selectbox(1181 "Embedding model",1182 ["VGG-Face", "Facenet", "OpenFace", "ArcFace"], # Eliminado "DeepFace" de la lista1183 help="Select the neural network model to extract facial embeddings"1184 )1185 elif comparison_method == "Embeddings (Slow, more precise)" and not DEEPFACE_AVAILABLE:1186 st.warning("The DeepFace library is not available. Please install with 'pip install deepface' to use embeddings.")1187 st.info("Using HOG method by default.")1188 comparison_method = "HOG (Fast, effective)"1189 1190 # Style options1191 bbox_color = st.color_picker("Bounding Box Color", "#00FF00")1192 # Convert hex color to BGR for OpenCV1193 bbox_color_rgb = tuple(int(bbox_color.lstrip('#')[i:i+2], 16) for i in (0, 2, 4))1194 bbox_color_bgr = (bbox_color_rgb[2], bbox_color_rgb[1], bbox_color_rgb[0]) # Convert RGB to BGR1195 1196 # Process the images when both are uploaded1197 if file1 is not None and file2 is not None:1198 # Read both images1199 raw_bytes1 = np.asarray(bytearray(file1.read()), dtype=np.uint8)1200 image1 = cv2.imdecode(raw_bytes1, cv2.IMREAD_COLOR)