Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image, ImageDraw | |
| import json | |
| from typing import Tuple, List, Dict, Any | |
| import time | |
| import threading | |
| import queue | |
| from models import load_detection_models, CV2_AVAILABLE # CV2_AVAILABLE needs to come from models.py | |
| from utils import draw_detections, process_image, generate_tone, play_sound, AlarmSystem, AUDIO_AVAILABLE | |
| # Global alarm system | |
| alarm_system = AlarmSystem() | |
| # Load models at startup | |
| face_cascade, object_net, object_classes = load_detection_models() | |
| def check_and_trigger_alarm(face_results, object_results, alarm_settings): | |
| """Check detection results and trigger alarm if conditions are met.""" | |
| if not alarm_settings.get("alarm_enabled", False): | |
| return False, "Alarm disabled" | |
| alarm_triggered = False | |
| alarm_reason = "" | |
| # Check face detection alarm | |
| if alarm_settings.get("face_alarm", False) and face_results: | |
| alarm_triggered = True | |
| alarm_reason = f"Face detected ({len(face_results)} faces)" | |
| # Check object detection alarm | |
| elif alarm_settings.get("object_alarm", False) and object_results: | |
| # Check for specific object types if specified | |
| target_objects = alarm_settings.get("target_objects", []) | |
| if target_objects: | |
| detected_objects = [obj["label"] for obj in object_results if obj["label"] in target_objects] | |
| if detected_objects: | |
| alarm_triggered = True | |
| alarm_reason = f"Target object detected: {', '.join(set(detected_objects))}" | |
| else: | |
| alarm_triggered = True | |
| alarm_reason = f"Object detected ({len(object_results)} objects)" | |
| # Trigger alarm if conditions are met | |
| if alarm_triggered: | |
| sound_type = alarm_settings.get("alarm_sound", "Beep") | |
| if sound_type == "Custom": | |
| sound_to_play = alarm_settings.get("custom_alarm_sound") | |
| else: | |
| sound_to_play = sound_type | |
| if alarm_system.trigger_alarm(sound_to_play): | |
| return True, f"π¨ ALARM TRIGGERED: {alarm_reason}" | |
| else: | |
| return False, "Alarm cooldown active" | |
| return False, "No alarm conditions met" | |
| def recognize_face_and_objects( | |
| image: np.ndarray, | |
| enable_face_detection: bool, | |
| enable_object_detection: bool, | |
| face_confidence: float, | |
| object_confidence: float, | |
| draw_boxes: bool, | |
| show_labels: bool, | |
| box_color: str, | |
| alarm_enabled_val: bool, # New parameter for alarm_enabled | |
| face_alarm_val: bool, # New parameter for face_alarm | |
| object_alarm_val: bool, # New parameter for object_alarm | |
| alarm_sound_val: str, # New parameter for alarm_sound | |
| target_objects_val: List[str], # New parameter for target_objects | |
| custom_alarm_sound_val: str | |
| ) -> Tuple[np.ndarray, str, str, str]: | |
| """ | |
| Perform face and object detection on the input image with alarm support. | |
| """ | |
| if image is None: | |
| return None, "[]", "[]", "No image provided" # Changed this line to return empty JSON arrays for face and object results | |
| # Convert PIL to numpy if needed | |
| if isinstance(image, Image.Image): | |
| image = np.array(image) | |
| # Construct alarm_settings dictionary from the passed values | |
| alarm_settings = { | |
| "alarm_enabled": alarm_enabled_val, | |
| "face_alarm": face_alarm_val, | |
| "object_alarm": object_alarm_val, | |
| "alarm_sound": alarm_sound_val, | |
| "target_objects": target_objects_val, | |
| "custom_alarm_sound": custom_alarm_sound_val | |
| } | |
| # Process image | |
| processed_image, face_results, object_results = process_image( | |
| image, | |
| face_cascade, | |
| object_net, | |
| object_classes, | |
| enable_face_detection, | |
| enable_object_detection, | |
| face_confidence, | |
| object_confidence | |
| ) | |
| # Check alarm conditions | |
| alarm_status, alarm_message = check_and_trigger_alarm(face_results, object_results, alarm_settings) | |
| # Draw detections if requested | |
| if draw_boxes: | |
| processed_image = draw_detections( | |
| processed_image.copy(), | |
| face_results, | |
| object_results, | |
| show_labels, | |
| box_color | |
| ) | |
| # Convert results to JSON | |
| face_json = json.dumps(face_results, indent=2) if face_results else "[]" | |
| object_json = json.dumps(object_results, indent=2) if object_results else "[]" | |
| return processed_image, face_json, object_json, alarm_message | |
| def webcam_recognition( | |
| image: np.ndarray, | |
| enable_face_detection: bool, | |
| enable_object_detection: bool, | |
| face_confidence: float, | |
| object_confidence: float, | |
| draw_boxes: bool, | |
| show_labels: bool, | |
| box_color: str, | |
| alarm_enabled_val: bool, # New parameter for alarm_enabled | |
| face_alarm_val: bool, # New parameter for face_alarm | |
| object_alarm_val: bool, # New parameter for object_alarm | |
| alarm_sound_val: str, # New parameter for alarm_sound | |
| target_objects_val: List[str], # New parameter for target_objects | |
| custom_alarm_sound_val: str | |
| ) -> np.ndarray: | |
| """Real-time webcam recognition with alarm.""" | |
| if image is None: | |
| return None | |
| # Construct alarm_settings dictionary from the passed values | |
| alarm_settings = { | |
| "alarm_enabled": alarm_enabled_val, | |
| "face_alarm": face_alarm_val, | |
| "object_alarm": object_alarm_val, | |
| "alarm_sound": alarm_sound_val, | |
| "target_objects": target_objects_val | |
| } | |
| processed_image, _, _, _ = recognize_face_and_objects( | |
| image, | |
| enable_face_detection, | |
| enable_object_detection, | |
| face_confidence, | |
| object_confidence, | |
| draw_boxes, | |
| show_labels, | |
| box_color, | |
| alarm_enabled_val, # Pass these directly | |
| face_alarm_val, | |
| object_alarm_val, | |
| alarm_sound_val, | |
| target_objects_val, | |
| custom_alarm_sound_val | |
| ) | |
| return processed_image | |
| def get_detection_statistics() -> str: | |
| """Get information about available detection models.""" | |
| if CV2_AVAILABLE: | |
| stats = { | |
| "face_detection": { | |
| "model": "Haar Cascade (OpenCV)", | |
| "features": ["Face detection", "Eye detection", "Smile detection"], | |
| "speed": "Fast", | |
| "accuracy": "Medium" | |
| }, | |
| "object_detection": { | |
| "model": "OpenCV DNN with MobileNet-SSD" if object_net else "Simulation Mode", | |
| "classes": len(object_classes) if object_classes else 21, | |
| "input_size": "300x300", | |
| "speed": "Real-time capable" if object_net else "Simulation", | |
| "accuracy": "High" if object_net else "Demo Mode" | |
| } | |
| } | |
| else: | |
| stats = { | |
| "face_detection": { | |
| "model": "PIL-based Simulation", | |
| "features": ["Demo face detection"], | |
| "speed": "Fast", | |
| "accuracy": "Demo Mode", | |
| "note": "Install OpenCV for real detection" | |
| }, | |
| "object_detection": { | |
| "model": "PIL-based Simulation", | |
| "classes": 8, | |
| "input_size": "Variable", | |
| "speed": "Demo", | |
| "accuracy": "Demo Mode", | |
| "note": "Install OpenCV for real detection" | |
| } | |
| } | |
| return json.dumps(stats, indent=2) | |
| def test_alarm_sound(sound_type, custom_sound_file): | |
| """Test alarm sound.""" | |
| if not AUDIO_AVAILABLE: | |
| return "β οΈ Audio not available. Install pyaudio for sound support." | |
| try: | |
| if sound_type == "Custom": | |
| sound_to_play = custom_sound_file | |
| if sound_to_play is None: | |
| return "Custom sound selected, but no file uploaded." | |
| else: | |
| sound_to_play = sound_type | |
| play_sound(sound_to_play) | |
| # Give a more descriptive message for custom sounds | |
| if sound_type == "Custom": | |
| return f"β Played custom sound" | |
| else: | |
| return f"β Played {sound_type} sound" | |
| except Exception as e: | |
| return f"β Error playing sound: {str(e)}" | |
| # Create custom CSS for better styling | |
| custom_css = """ | |
| .main-container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| } | |
| .settings-panel { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border-radius: 10px; | |
| padding: 20px; | |
| } | |
| .result-panel { | |
| border: 2px solid #e0e0e0; | |
| border-radius: 10px; | |
| padding: 15px; | |
| } | |
| .image-container { | |
| border: 1px solid #ddd; | |
| border-radius: 8px; | |
| overflow: hidden; | |
| } | |
| .warning-box { | |
| background-color: #fff3cd; | |
| border: 1px solid #ffeaa7; | |
| border-radius: 8px; | |
| padding: 15px; | |
| margin-bottom: 20px; | |
| } | |
| .alarm-box { | |
| background-color: #f8d7da; | |
| border: 2px solid #f5c6cb; | |
| border-radius: 8px; | |
| padding: 15px; | |
| margin-bottom: 20px; | |
| animation: pulse 1s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 1; } | |
| 50% { opacity: 0.7; } | |
| 100% { opacity: 1; } | |
| } | |
| """ | |
| with gr.Blocks(title="Face & Object Recognition Platform") as demo: | |
| gr.HTML(f"<style>{custom_css}</style>") | |
| gr.Markdown(""" | |
| # π Face & Object Recognition Platform with Alarm System | |
| Built with [anycoder](https://huggingface.co/spaces/akhaliq/anycoder) | |
| Advanced computer vision platform for real-time face and object detection with customizable settings and alarm notifications. | |
| """) | |
| # Show warnings if dependencies are not available | |
| if not CV2_AVAILABLE: | |
| with gr.Row(): | |
| gr.Markdown(""" | |
| <div class="warning-box"> | |
| β οΈ **OpenCV Not Available**: Running in demonstration mode with simulated detections. | |
| Install OpenCV (`pip install opencv-python`) for real face and object detection capabilities. | |
| </div> | |
| """) | |
| if not AUDIO_AVAILABLE: | |
| with gr.Row(): | |
| gr.Markdown(""" | |
| <div class="warning-box"> | |
| β οΈ **Audio Not Available**: Install audio libraries for alarm sounds: `pip install pyaudio` | |
| </div> | |
| """) | |
| # Alarm state | |
| alarm_status = gr.Textbox(label="Alarm Status", visible=False, interactive=False) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π€ Input Source") | |
| with gr.Tabs(): | |
| with gr.TabItem("Upload Image"): | |
| input_image = gr.Image( | |
| label="Upload an image for analysis", | |
| type="numpy", | |
| height=400 | |
| ) | |
| analyze_btn = gr.Button("π Analyze Image", variant="primary", size="lg") | |
| with gr.TabItem("Webcam"): | |
| webcam_image = gr.Image( | |
| label="Webcam Feed", | |
| sources="webcam", | |
| type="numpy", | |
| streaming=True, | |
| height=400 | |
| ) | |
| gr.Markdown("*Webcam provides real-time detection with alarm system*") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### βοΈ Detection Settings") | |
| with gr.Group(elem_classes=["settings-panel"]): | |
| gr.Markdown("#### Detection Modes") | |
| enable_face = gr.Checkbox(label="π€ Enable Face Detection", value=True) | |
| enable_objects = gr.Checkbox(label="π¦ Enable Object Detection", value=True) | |
| gr.Markdown("#### Confidence Thresholds") | |
| face_conf = gr.Slider( | |
| label="Face Detection Confidence", | |
| minimum=0.1, | |
| maximum=1.0, | |
| value=0.7, | |
| step=0.1, | |
| info="Lower values detect more faces" | |
| ) | |
| object_conf = gr.Slider( | |
| label="Object Detection Confidence", | |
| minimum=0.1, | |
| maximum=1.0, | |
| value=0.5, | |
| step=0.1, | |
| info="Lower values detect more objects" | |
| ) | |
| gr.Markdown("#### Display Options") | |
| draw_boxes = gr.Checkbox(label="π Draw Bounding Boxes", value=True) | |
| show_labels = gr.Checkbox(label="π·οΈ Show Labels", value=True) | |
| box_color = gr.Dropdown( | |
| label="Box Color", | |
| choices=["red", "green", "blue", "yellow", "purple", "orange"], | |
| value="red" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### πΌοΈ Detection Results") | |
| output_image = gr.Image( | |
| label="Processed Image with Detections", | |
| type="numpy", | |
| height=400, | |
| elem_classes=["image-container"] | |
| ) | |
| # Alarm status display | |
| alarm_display = gr.Textbox( | |
| label="π¨ Alarm Status", | |
| value="Ready", | |
| interactive=False, | |
| elem_classes=["alarm-box" if False else ""] | |
| ) | |
| with gr.Column(): | |
| with gr.Tabs(): | |
| with gr.TabItem("π€ Face Results"): | |
| face_results = gr.JSON( | |
| label="Face Detection Data", | |
| elem_classes=["result-panel"] | |
| ) | |
| with gr.TabItem("π¦ Object Results"): | |
| object_results = gr.JSON( | |
| label="Object Detection Data", | |
| elem_classes=["result-panel"] | |
| ) | |
| with gr.TabItem("π¨ Alarm Settings"): | |
| gr.Markdown("#### Configure Alarm System") | |
| alarm_enabled = gr.Checkbox(label="π Enable Alarm System", value=False) | |
| face_alarm = gr.Checkbox(label="π€ Alarm on Face Detection", value=True) | |
| object_alarm = gr.Checkbox(label="π¦ Alarm on Object Detection", value=True) | |
| alarm_sound = gr.Dropdown( | |
| label="π Alarm Sound", | |
| choices=["Beep", "Siren", "Chime", "Alert", "Buzzer", "Ring", "Custom"], | |
| value="Beep", | |
| info="Select alarm sound type" | |
| ) | |
| custom_alarm_sound = gr.File( | |
| label="Upload Custom Alarm Sound (.wav)", | |
| file_types=[".wav"], | |
| visible=False | |
| ) | |
| def toggle_custom_sound(sound_choice): | |
| return gr.update(visible=sound_choice == "Custom") | |
| alarm_sound.change( | |
| fn=toggle_custom_sound, | |
| inputs=alarm_sound, | |
| outputs=custom_alarm_sound | |
| ) | |
| target_objects = gr.CheckboxGroup( | |
| label="π― Specific Objects to Trigger Alarm (optional)", | |
| choices=["person", "car", "dog", "cat", "bottle", "chair", "laptop", "phone"], | |
| info="Leave empty to alarm on any object" | |
| ) | |
| test_sound_btn = gr.Button("π Test Sound", variant="secondary") | |
| sound_test_result = gr.Textbox(label="Sound Test Result", interactive=False) | |
| with gr.TabItem("βΉοΈ Model Info"): | |
| model_info = gr.JSON( | |
| label="Detection Models Information", | |
| value=json.loads(get_detection_statistics()), | |
| elem_classes=["result-panel"] | |
| ) | |
| # Event handlers | |
| # NOTE: The gr.State values are captured at the time the UI is created. | |
| # To get the current values, we need to pass the Gradio components themselves | |
| # and then read their values in the `recognize_face_and_objects` function. | |
| analyze_btn.click( | |
| fn=recognize_face_and_objects, | |
| inputs=[ | |
| input_image, | |
| enable_face, | |
| enable_objects, | |
| face_conf, | |
| object_conf, | |
| draw_boxes, | |
| show_labels, | |
| box_color, | |
| # Pass the Gradio components, not their values | |
| alarm_enabled, | |
| face_alarm, | |
| object_alarm, | |
| alarm_sound, | |
| target_objects, | |
| custom_alarm_sound | |
| ], | |
| outputs=[output_image, face_results, object_results, alarm_display] | |
| ) | |
| # Real-time webcam processing | |
| webcam_image.stream( | |
| fn=webcam_recognition, | |
| inputs=[ | |
| webcam_image, | |
| enable_face, | |
| enable_objects, | |
| face_conf, | |
| object_conf, | |
| draw_boxes, | |
| show_labels, | |
| box_color, | |
| # Pass the Gradio components, not their values | |
| alarm_enabled, | |
| face_alarm, | |
| object_alarm, | |
| alarm_sound, | |
| target_objects, | |
| custom_alarm_sound | |
| ], | |
| outputs=[output_image], | |
| time_limit=30, | |
| stream_every=0.5 | |
| ) | |
| # Test sound button | |
| test_sound_btn.click( | |
| fn=test_alarm_sound, | |
| inputs=[alarm_sound, custom_alarm_sound], | |
| outputs=[sound_test_result] | |
| ) | |
| gr.Markdown(""" | |
| --- | |
| ### π Usage Instructions | |
| 1. **Upload Image**: Select an image from your device for analysis | |
| 2. **Webcam**: Use your webcam for real-time detection with alarms | |
| 3. **Adjust Settings**: Customize confidence thresholds and display options | |
| 4. **Configure Alarm**: Set up alarm conditions and sounds in the Alarm Settings tab | |
| 5. **View Results**: See detections overlayed on the image with detailed JSON data | |
| ### π¨ Alarm Features | |
| - **Face Detection Alarm**: Triggers when faces are detected | |
| - **Object Detection Alarm**: Triggers when objects are detected (all or specific types) | |
| - **Multiple Sounds**: Choose from 6 different alarm sounds | |
| - **Cooldown Period**: Prevents alarm spam (2-second cooldown) | |
| - **Real-time Monitoring**: Works with webcam for continuous monitoring | |
| ### π― Features | |
| - **Face Detection**: Identifies faces in images using Haar Cascade classifiers (or simulation mode) | |
| - **Object Detection**: Recognizes object classes using MobileNet-SSD (or simulation mode) | |
| - **Real-time Processing**: Webcam support with live detection and alarms | |
| - **Customizable**: Adjustable confidence thresholds and visual settings | |
| - **Detailed Output**: JSON formatted results with coordinates and confidence scores | |
| ### βοΈ Installation Notes | |
| - Install OpenCV for full functionality: `pip install opencv-python` | |
| - Install audio support for alarms: `pip install pyaudio` | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch(share=True, debug=True) |