Spaces:
Sleeping
Sleeping
Implemented dynamic face visualization with no page reloads: Added face_visualization module, updated UI controls, improved visual feedback
b9be998
| """ | |
| Face visualization module for dynamic UI controls and visual feedback. | |
| This module provides functionality for rendering face detection results | |
| with various visual states (selected, unselected, removed) without requiring | |
| page reloads. | |
| """ | |
| import streamlit as st | |
| import cv2 | |
| import numpy as np | |
| from typing import List, Dict, Tuple, Any, Optional, Set | |
| # Color constants for face boxes | |
| COLOR_SELECTED = (0, 255, 0) # Green | |
| COLOR_UNSELECTED = (100, 100, 100) # Gray | |
| COLOR_HIGHLIGHT = (255, 165, 0) # Orange - for highlighting changes | |
| def initialize_face_visualization_state(): | |
| """ | |
| Initialize session state variables used for face visualization. | |
| Should be called at the beginning of the app. | |
| """ | |
| if "face_display_mode" not in st.session_state: | |
| st.session_state.face_display_mode = "show_all" # Options: show_all, hide_unselected, hide_removed | |
| if "removed_faces" not in st.session_state: | |
| st.session_state.removed_faces = set() | |
| if "selected_faces" not in st.session_state: | |
| st.session_state.selected_faces = {} | |
| if "face_labels" not in st.session_state: | |
| st.session_state.face_labels = {} | |
| if "recent_action" not in st.session_state: | |
| st.session_state.recent_action = None # For tracking UI changes | |
| def draw_faces_with_state( | |
| image: np.ndarray, | |
| faces: List[Tuple[int, int, int, int]], | |
| max_faces: int = 5 | |
| ) -> np.ndarray: | |
| """ | |
| Draw faces on image with different visual states based on session state. | |
| Args: | |
| image: Image in numpy array format (RGB) | |
| faces: List of tuples (x, y, w, h) with face coordinates | |
| max_faces: Maximum number of faces to display | |
| Returns: | |
| Image with visualized faces according to current display mode | |
| """ | |
| # Get current display mode | |
| display_mode = st.session_state.get("face_display_mode", "show_all") | |
| removed_faces = st.session_state.get("removed_faces", set()) | |
| selected_faces = st.session_state.get("selected_faces", {}) | |
| # Work with a copy to avoid modifying the original | |
| labeled_img = image.copy() | |
| # Limit to max_faces faces | |
| faces_to_draw = faces[:max_faces] if len(faces) > max_faces else faces | |
| # Draw each face based on its state | |
| for i, (x, y, w, h) in enumerate(faces_to_draw): | |
| face_key = f"face_{i}" | |
| # Skip faces marked as false positives if not showing all | |
| if face_key in removed_faces and display_mode != "show_all": | |
| continue | |
| # Determine if the face is selected | |
| is_selected = selected_faces.get(face_key, True) | |
| # Skip unselected faces if in hide_unselected mode | |
| if not is_selected and display_mode == "hide_unselected": | |
| continue | |
| # Determine color based on state | |
| if face_key in removed_faces: | |
| # Skip drawing removed faces entirely | |
| continue | |
| elif is_selected: | |
| color = COLOR_SELECTED | |
| else: | |
| color = COLOR_UNSELECTED | |
| # Draw rectangle with appropriate color | |
| cv2.rectangle(labeled_img, (x, y), (x+w, y+h), color, 2) | |
| # Add numbered label with same color | |
| label = f"Face {i+1}" | |
| cv2.putText(labeled_img, label, (x, y-10), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.9, color, 2) | |
| return labeled_img | |
| def face_control_panel(): | |
| """ | |
| Renders a control panel for face visualization options. | |
| Returns the current display mode. | |
| """ | |
| st.markdown("### Display Options") | |
| # Create a row of buttons for display modes | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| if st.button("Show All Faces", key="btn_show_all"): | |
| st.session_state.face_display_mode = "show_all" | |
| with col2: | |
| if st.button("Hide Unselected", key="btn_hide_unselected"): | |
| st.session_state.face_display_mode = "hide_unselected" | |
| with col3: | |
| if st.button("Reset Removed Faces", key="btn_reset_removed"): | |
| # Clear any faces that were marked as removed | |
| st.session_state.removed_faces = set() | |
| # Display current statistics | |
| num_detected = len(st.session_state.get("detected_faces", [])) | |
| num_selected = sum(1 for v in st.session_state.get("selected_faces", {}).values() if v) | |
| num_removed = len(st.session_state.get("removed_faces", set())) | |
| st.markdown(f"**Status:** {num_detected} detected, {num_selected} selected, {num_removed} removed") | |
| return st.session_state.face_display_mode | |
| def on_face_select(face_idx: int, value: bool): | |
| """ | |
| Callback when a face is selected/unselected. | |
| Updates session state without page reload. | |
| Args: | |
| face_idx: Index of the face | |
| value: New selection state (True/False) | |
| """ | |
| face_key = f"face_{face_idx}" | |
| st.session_state.selected_faces[face_key] = value | |
| # Update recent action for UI feedback | |
| st.session_state.recent_action = "select" if value else "unselect" | |
| def on_face_remove(face_idx: int): | |
| """ | |
| Callback when a face is marked as a false positive. | |
| Updates session state without page reload. | |
| Args: | |
| face_idx: Index of the face to remove | |
| """ | |
| face_key = f"face_{face_idx}" | |
| # Mark as not selected | |
| st.session_state.selected_faces[face_key] = False | |
| # Remove any existing label | |
| if face_key in st.session_state.face_labels: | |
| del st.session_state.face_labels[face_key] | |
| # Add to set of removed faces | |
| st.session_state.removed_faces.add(face_key) | |
| # Update recent action for UI feedback | |
| st.session_state.recent_action = "remove" | |