|
|
|
|
|
""" |
|
|
Enhanced UI Components - BackgroundFX Pro |
|
|
Streamlined interface with better error handling and user experience |
|
|
""" |
|
|
|
|
|
import gradio as gr |
|
|
import os |
|
|
import json |
|
|
import time |
|
|
import traceback |
|
|
from typing import Optional, Dict, Any, Tuple |
|
|
from pathlib import Path |
|
|
|
|
|
|
|
|
print("UI Components: Initializing interface...") |
|
|
|
|
|
|
|
|
try: |
|
|
from app import ( |
|
|
VideoProcessor, |
|
|
processor, |
|
|
load_models_with_validation, |
|
|
process_video_fixed, |
|
|
get_model_status, |
|
|
get_cache_status |
|
|
) |
|
|
CORE_FUNCTIONS_AVAILABLE = True |
|
|
print("UI Components: Core functions imported successfully") |
|
|
except Exception as e: |
|
|
print(f"UI Components: Core functions import failed: {e}") |
|
|
CORE_FUNCTIONS_AVAILABLE = False |
|
|
|
|
|
|
|
|
try: |
|
|
from utilities import PROFESSIONAL_BACKGROUNDS |
|
|
UTILITIES_AVAILABLE = True |
|
|
print("UI Components: Utilities imported successfully") |
|
|
except Exception as e: |
|
|
print(f"UI Components: Utilities import failed: {e}") |
|
|
UTILITIES_AVAILABLE = False |
|
|
PROFESSIONAL_BACKGROUNDS = { |
|
|
"office_modern": {"name": "Modern Office", "description": "Clean office environment"}, |
|
|
"studio_blue": {"name": "Professional Blue", "description": "Blue studio background"}, |
|
|
"minimalist": {"name": "Minimalist White", "description": "Clean white background"} |
|
|
} |
|
|
|
|
|
|
|
|
try: |
|
|
from two_stage_processor import CHROMA_PRESETS |
|
|
TWO_STAGE_AVAILABLE = True |
|
|
print("UI Components: Two-stage processor available") |
|
|
except ImportError: |
|
|
TWO_STAGE_AVAILABLE = False |
|
|
CHROMA_PRESETS = { |
|
|
'standard': {'name': 'Standard Quality'}, |
|
|
'balanced': {'name': 'Balanced'}, |
|
|
'high': {'name': 'High Quality'} |
|
|
} |
|
|
print("UI Components: Two-stage processor not available") |
|
|
|
|
|
class UIStateManager: |
|
|
"""Manage UI state and provide user feedback""" |
|
|
|
|
|
def __init__(self): |
|
|
self.processing_active = False |
|
|
self.models_loaded = False |
|
|
self.last_processing_time = None |
|
|
self.processing_history = [] |
|
|
|
|
|
def update_processing_state(self, active: bool): |
|
|
self.processing_active = active |
|
|
if not active and self.last_processing_time: |
|
|
duration = time.time() - self.last_processing_time |
|
|
self.processing_history.append({ |
|
|
'timestamp': time.time(), |
|
|
'duration': duration |
|
|
}) |
|
|
elif active: |
|
|
self.last_processing_time = time.time() |
|
|
|
|
|
def get_average_processing_time(self) -> float: |
|
|
if not self.processing_history: |
|
|
return 0 |
|
|
recent_history = self.processing_history[-5:] |
|
|
return sum(h['duration'] for h in recent_history) / len(recent_history) |
|
|
|
|
|
|
|
|
ui_state = UIStateManager() |
|
|
|
|
|
def create_interface(): |
|
|
"""Create the enhanced Gradio interface with better UX""" |
|
|
|
|
|
|
|
|
def enhanced_process_video( |
|
|
video_path, bg_method, custom_img, prof_choice, |
|
|
use_two_stage, chroma_preset, quality_preset, |
|
|
progress: Optional[gr.Progress] = None |
|
|
): |
|
|
"""Enhanced video processing with comprehensive error handling and user feedback""" |
|
|
|
|
|
if not CORE_FUNCTIONS_AVAILABLE: |
|
|
return None, "Error: Core processing functions not available", "System Error: Please check installation" |
|
|
|
|
|
if not processor.models_loaded: |
|
|
return None, "Error: Models not loaded", "Please load models first using the 'Load Models' button" |
|
|
|
|
|
if not video_path: |
|
|
return None, "Error: No video uploaded", "Please upload a video file first" |
|
|
|
|
|
|
|
|
if bg_method == "professional" and not prof_choice: |
|
|
return None, "Error: No background selected", "Please select a professional background" |
|
|
|
|
|
if bg_method == "upload" and not custom_img: |
|
|
return None, "Error: No custom background", "Please upload a custom background image" |
|
|
|
|
|
try: |
|
|
ui_state.update_processing_state(True) |
|
|
|
|
|
|
|
|
if quality_preset and hasattr(processor, 'config'): |
|
|
processor.config.quality_preset = quality_preset |
|
|
|
|
|
def progress_callback(pct, desc): |
|
|
if progress: |
|
|
progress(pct, desc) |
|
|
return desc |
|
|
|
|
|
|
|
|
if bg_method == "professional": |
|
|
background_choice = prof_choice |
|
|
custom_background_path = None |
|
|
else: |
|
|
background_choice = "custom" |
|
|
custom_background_path = custom_img |
|
|
|
|
|
|
|
|
result_path, result_message = process_video_fixed( |
|
|
video_path=video_path, |
|
|
background_choice=background_choice, |
|
|
custom_background_path=custom_background_path, |
|
|
progress_callback=progress_callback, |
|
|
use_two_stage=bool(use_two_stage), |
|
|
chroma_preset=chroma_preset or "standard", |
|
|
preview_mask=False, |
|
|
preview_greenscreen=False |
|
|
) |
|
|
|
|
|
ui_state.update_processing_state(False) |
|
|
|
|
|
if result_path: |
|
|
|
|
|
avg_time = ui_state.get_average_processing_time() |
|
|
success_info = f""" |
|
|
β
Processing Complete! |
|
|
|
|
|
π Results: |
|
|
{result_message} |
|
|
|
|
|
β±οΈ Performance: |
|
|
- Average processing time: {avg_time:.1f}s |
|
|
- Two-stage mode: {'Enabled' if use_two_stage else 'Disabled'} |
|
|
- Quality preset: {quality_preset or 'Default'} |
|
|
|
|
|
π‘ Tips: |
|
|
- Try two-stage mode for better quality |
|
|
- Use 'fast' preset for quicker processing |
|
|
- Shorter videos process faster |
|
|
""" |
|
|
return result_path, success_info, "Processing completed successfully!" |
|
|
else: |
|
|
return None, f"Processing failed: {result_message}", f"Error: {result_message}" |
|
|
|
|
|
except Exception as e: |
|
|
ui_state.update_processing_state(False) |
|
|
error_msg = f"Processing error: {str(e)}" |
|
|
print(f"UI Error: {error_msg}\n{traceback.format_exc()}") |
|
|
return None, error_msg, f"System Error: {error_msg}" |
|
|
|
|
|
|
|
|
def enhanced_load_models(progress: Optional[gr.Progress] = None): |
|
|
"""Enhanced model loading with detailed feedback""" |
|
|
|
|
|
if not CORE_FUNCTIONS_AVAILABLE: |
|
|
return "Error: Core functions not available", "System Error: Installation incomplete" |
|
|
|
|
|
try: |
|
|
def progress_callback(pct, desc): |
|
|
if progress: |
|
|
progress(pct, desc) |
|
|
return desc |
|
|
|
|
|
result = load_models_with_validation(progress_callback) |
|
|
|
|
|
if "SUCCESS" in result or "successful" in result.lower(): |
|
|
ui_state.models_loaded = True |
|
|
enhanced_result = f""" |
|
|
β
Models Loaded Successfully! |
|
|
|
|
|
π Status: |
|
|
{result} |
|
|
|
|
|
π― Ready for Processing: |
|
|
- High-quality segmentation (SAM2) |
|
|
- Professional mask refinement (MatAnyone) |
|
|
- {'Two-stage green screen mode available' if TWO_STAGE_AVAILABLE else 'Single-stage processing only'} |
|
|
|
|
|
π‘ Next Steps: |
|
|
1. Upload your video |
|
|
2. Choose background method |
|
|
3. Click 'Process Video' |
|
|
""" |
|
|
return enhanced_result, "Models loaded successfully! Ready to process videos." |
|
|
else: |
|
|
return result, f"Model loading failed: {result}" |
|
|
|
|
|
except Exception as e: |
|
|
error_msg = f"Model loading error: {str(e)}" |
|
|
print(f"UI Model Loading Error: {error_msg}\n{traceback.format_exc()}") |
|
|
return error_msg, error_msg |
|
|
|
|
|
|
|
|
def get_enhanced_model_status(): |
|
|
"""Get enhanced model status with user-friendly formatting""" |
|
|
try: |
|
|
status = get_model_status() |
|
|
if isinstance(status, dict): |
|
|
formatted_status = { |
|
|
"SAM2 Segmentation": "β
Ready" if status.get('sam2_available') else "β Not Loaded", |
|
|
"MatAnyone Refinement": "β
Ready" if status.get('matanyone_available') else "β Not Loaded", |
|
|
"Two-Stage Mode": "β
Available" if status.get('two_stage_available') else "β Not Available", |
|
|
"Device": status.get('device', 'Unknown'), |
|
|
"Models Validated": "β
Yes" if status.get('models_loaded') else "β No" |
|
|
} |
|
|
if 'memory_usage' in status and status['memory_usage']: |
|
|
memory = status['memory_usage'] |
|
|
if 'gpu_percent' in memory: |
|
|
formatted_status["GPU Memory"] = f"{memory['gpu_percent']:.1f}% used" |
|
|
return formatted_status |
|
|
else: |
|
|
return {"Status": str(status)} |
|
|
except Exception as e: |
|
|
return {"Error": f"Failed to get status: {e}"} |
|
|
|
|
|
def get_enhanced_cache_status(): |
|
|
"""Get enhanced cache status with detailed information""" |
|
|
try: |
|
|
status = get_cache_status() |
|
|
if isinstance(status, dict): |
|
|
return { |
|
|
"Cache Status": "β
Active" if status.get('models_loaded') else "β Inactive", |
|
|
"Processing Mode": "Two-Stage" if status.get('two_stage_available') else "Single-Stage", |
|
|
"Configuration": status.get('config', {}), |
|
|
"System Device": status.get('device', 'Unknown') |
|
|
} |
|
|
else: |
|
|
return {"Cache": str(status)} |
|
|
except Exception as e: |
|
|
return {"Error": f"Failed to get cache info: {e}"} |
|
|
|
|
|
|
|
|
with gr.Blocks( |
|
|
title="BackgroundFX Pro - Professional Video Background Replacement", |
|
|
theme=gr.themes.Soft( |
|
|
primary_hue="blue", |
|
|
secondary_hue="gray", |
|
|
neutral_hue="slate" |
|
|
), |
|
|
css=""" |
|
|
.main-header { text-align: center; margin-bottom: 20px; } |
|
|
.status-box { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0; } |
|
|
.error-box { background: #fee; border-left: 4px solid #dc3545; padding: 15px; } |
|
|
.success-box { background: #efe; border-left: 4px solid #28a745; padding: 15px; } |
|
|
.feature-list { columns: 2; column-gap: 20px; } |
|
|
""" |
|
|
) as demo: |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
gr.Markdown(""" |
|
|
# π¬ BackgroundFX Pro - Video Background Replacement |
|
|
|
|
|
Professional-quality video background replacement using AI segmentation and advanced compositing techniques. |
|
|
""", elem_classes=["main-header"]) |
|
|
|
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
system_status = gr.HTML(f""" |
|
|
<div class="status-box"> |
|
|
<h4>π System Status</h4> |
|
|
<ul> |
|
|
<li>Core Functions: {'β
Available' if CORE_FUNCTIONS_AVAILABLE else 'β Not Available'}</li> |
|
|
<li>Utilities: {'β
Available' if UTILITIES_AVAILABLE else 'β Not Available'}</li> |
|
|
<li>Two-Stage Mode: {'β
Available' if TWO_STAGE_AVAILABLE else 'β Not Available'}</li> |
|
|
</ul> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### πΉ Step 1: Upload Your Video") |
|
|
video_input = gr.Video( |
|
|
label="Upload your video (MP4, AVI, MOV supported)", |
|
|
height=300 |
|
|
) |
|
|
|
|
|
with gr.Accordion("π Video Requirements", open=False): |
|
|
gr.Markdown(""" |
|
|
**Supported Formats:** MP4, AVI, MOV, MKV |
|
|
**Max Duration:** 5 minutes (300 seconds) |
|
|
**Max Resolution:** 4096x4096 |
|
|
**Max File Size:** 2GB |
|
|
|
|
|
**Recommendations:** |
|
|
- Use 1080p or lower for faster processing |
|
|
- Shorter videos (10-30s) are ideal for testing |
|
|
- Ensure good lighting and clear person visibility |
|
|
""") |
|
|
|
|
|
gr.Markdown("### π¨ Step 2: Choose Background") |
|
|
background_method = gr.Radio( |
|
|
choices=["professional", "upload"], |
|
|
value="professional", |
|
|
label="Background Method", |
|
|
info="Professional presets or upload your own image" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Group(visible=True) as professional_group: |
|
|
gr.Markdown("**Professional Background Presets**") |
|
|
|
|
|
if UTILITIES_AVAILABLE and PROFESSIONAL_BACKGROUNDS: |
|
|
choices = [(f"{bg['name']} - {bg['description']}", key) |
|
|
for key, bg in PROFESSIONAL_BACKGROUNDS.items()] |
|
|
default_choice = list(PROFESSIONAL_BACKGROUNDS.keys())[0] |
|
|
else: |
|
|
choices = [("Modern Office - Clean office environment", "office_modern")] |
|
|
default_choice = "office_modern" |
|
|
|
|
|
professional_choice = gr.Dropdown( |
|
|
choices=choices, |
|
|
value=default_choice, |
|
|
label="Select Professional Background", |
|
|
info="Each preset is optimized for different use cases" |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Group(visible=False) as upload_group: |
|
|
gr.Markdown("**Upload Custom Background**") |
|
|
custom_background = gr.Image( |
|
|
label="Upload background image", |
|
|
type="filepath", |
|
|
info="JPG, PNG supported. Will be resized to match video resolution." |
|
|
) |
|
|
|
|
|
|
|
|
def update_background_visibility(method): |
|
|
return ( |
|
|
gr.update(visible=(method == "professional")), |
|
|
gr.update(visible=(method == "upload")) |
|
|
) |
|
|
|
|
|
background_method.change( |
|
|
fn=update_background_visibility, |
|
|
inputs=background_method, |
|
|
outputs=[professional_group, upload_group] |
|
|
) |
|
|
|
|
|
gr.Markdown("### βοΈ Step 3: Processing Options") |
|
|
|
|
|
with gr.Row(): |
|
|
quality_preset = gr.Dropdown( |
|
|
choices=[ |
|
|
("Fast - Quick processing", "fast"), |
|
|
("Balanced - Good quality/speed", "balanced"), |
|
|
("High - Best quality", "high") |
|
|
], |
|
|
value="balanced", |
|
|
label="Quality Preset", |
|
|
info="Higher quality takes longer but produces better results" |
|
|
) |
|
|
|
|
|
with gr.Accordion("π§ Advanced Settings", open=False): |
|
|
use_two_stage = gr.Checkbox( |
|
|
label="Enable Two-Stage Processing", |
|
|
value=False, |
|
|
info="Cinema-quality green screen mode (slower but much better quality)", |
|
|
interactive=TWO_STAGE_AVAILABLE |
|
|
) |
|
|
|
|
|
if TWO_STAGE_AVAILABLE: |
|
|
chroma_preset = gr.Dropdown( |
|
|
choices=[ |
|
|
("Standard - General use", "standard"), |
|
|
("Studio - Broadcast quality", "studio"), |
|
|
("Outdoor - Challenging lighting", "outdoor") |
|
|
], |
|
|
value="standard", |
|
|
label="Chroma Key Preset", |
|
|
info="Only used in two-stage mode" |
|
|
) |
|
|
else: |
|
|
chroma_preset = gr.Dropdown( |
|
|
choices=[("Standard", "standard")], |
|
|
value="standard", |
|
|
label="Chroma Key Preset", |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
gr.Markdown("### π Step 4: Process") |
|
|
|
|
|
with gr.Row(): |
|
|
load_models_btn = gr.Button( |
|
|
"π Load Models", |
|
|
variant="secondary", |
|
|
size="lg" |
|
|
) |
|
|
process_btn = gr.Button( |
|
|
"π¬ Process Video", |
|
|
variant="primary", |
|
|
size="lg", |
|
|
scale=2 |
|
|
) |
|
|
|
|
|
|
|
|
status_text = gr.Textbox( |
|
|
label="π Status Updates", |
|
|
value="Ready - Click 'Load Models' to begin", |
|
|
interactive=False, |
|
|
lines=6, |
|
|
max_lines=10 |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Accordion("π System Monitoring", open=False): |
|
|
with gr.Row(): |
|
|
model_status_btn = gr.Button("Check Models", variant="secondary") |
|
|
cache_status_btn = gr.Button("Check Cache", variant="secondary") |
|
|
|
|
|
model_status_display = gr.JSON(label="Model Status", visible=False) |
|
|
cache_status_display = gr.JSON(label="Cache Status", visible=False) |
|
|
|
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### π₯ Results") |
|
|
|
|
|
video_output = gr.Video( |
|
|
label="Processed Video", |
|
|
height=400 |
|
|
) |
|
|
|
|
|
result_info = gr.Textbox( |
|
|
label="π Processing Information", |
|
|
interactive=False, |
|
|
lines=12, |
|
|
max_lines=15, |
|
|
placeholder="Processing results and statistics will appear here..." |
|
|
) |
|
|
|
|
|
debug_info = gr.Textbox( |
|
|
label="π Debug Information", |
|
|
interactive=False, |
|
|
lines=6, |
|
|
max_lines=10, |
|
|
placeholder="Debug and system information will appear here...", |
|
|
visible=False |
|
|
) |
|
|
|
|
|
|
|
|
show_debug_btn = gr.Button("Show Debug Info", variant="secondary", size="sm") |
|
|
|
|
|
def toggle_debug_visibility(current_visibility): |
|
|
new_visibility = not current_visibility |
|
|
return ( |
|
|
gr.update(visible=new_visibility), |
|
|
"Hide Debug Info" if new_visibility else "Show Debug Info" |
|
|
) |
|
|
|
|
|
show_debug_btn.click( |
|
|
fn=lambda: toggle_debug_visibility(False), |
|
|
outputs=[debug_info, show_debug_btn] |
|
|
) |
|
|
|
|
|
|
|
|
load_models_btn.click( |
|
|
fn=enhanced_load_models, |
|
|
outputs=[status_text, debug_info], |
|
|
show_progress=True |
|
|
) |
|
|
|
|
|
process_btn.click( |
|
|
fn=enhanced_process_video, |
|
|
inputs=[ |
|
|
video_input, |
|
|
background_method, |
|
|
custom_background, |
|
|
professional_choice, |
|
|
use_two_stage, |
|
|
chroma_preset, |
|
|
quality_preset |
|
|
], |
|
|
outputs=[video_output, result_info, debug_info], |
|
|
show_progress=True |
|
|
) |
|
|
|
|
|
model_status_btn.click( |
|
|
fn=get_enhanced_model_status, |
|
|
outputs=[model_status_display], |
|
|
show_progress=False |
|
|
).then( |
|
|
fn=lambda: gr.update(visible=True), |
|
|
outputs=[model_status_display] |
|
|
) |
|
|
|
|
|
cache_status_btn.click( |
|
|
fn=get_enhanced_cache_status, |
|
|
outputs=[cache_status_display], |
|
|
show_progress=False |
|
|
).then( |
|
|
fn=lambda: gr.update(visible=True), |
|
|
outputs=[cache_status_display] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Accordion("βΉοΈ Help & Information", open=False): |
|
|
gr.Markdown(f""" |
|
|
### π― How to Use |
|
|
|
|
|
1. **Load Models**: Click 'Load Models' and wait for completion (first-time setup) |
|
|
2. **Upload Video**: Choose a video file (MP4 recommended, under 5 minutes) |
|
|
3. **Select Background**: Use professional presets or upload your own image |
|
|
4. **Configure Quality**: Choose preset based on your speed/quality preference |
|
|
5. **Process**: Click 'Process Video' and wait for completion |
|
|
|
|
|
### π§ Processing Modes |
|
|
|
|
|
**Single-Stage (Default)** |
|
|
- Direct background replacement |
|
|
- Faster processing (2-5x speed) |
|
|
- Good quality for most use cases |
|
|
- Recommended for: Social media, quick edits, testing |
|
|
|
|
|
**Two-Stage (Premium)** |
|
|
- Green screen intermediate step |
|
|
- Cinema-quality edge compositing |
|
|
- Advanced chroma key algorithms |
|
|
- Recommended for: Professional content, broadcast, film |
|
|
|
|
|
### β‘ Performance Tips |
|
|
|
|
|
- **Fast Processing**: Use 'fast' preset, disable two-stage mode |
|
|
- **Best Quality**: Use 'high' preset, enable two-stage mode |
|
|
- **GPU Memory**: Processing automatically manages memory and provides fallbacks |
|
|
- **Video Length**: Shorter videos (10-30s) process much faster |
|
|
|
|
|
### π¨ Troubleshooting |
|
|
|
|
|
**Models Won't Load** |
|
|
- Check internet connection (models download from Hugging Face) |
|
|
- Wait for downloads to complete (may take several minutes first time) |
|
|
- Try restarting if stuck |
|
|
|
|
|
**Processing Fails** |
|
|
- Ensure video file is not corrupted |
|
|
- Try shorter clips first (under 30 seconds) |
|
|
- Check video format (MP4 works best) |
|
|
- Verify sufficient disk space |
|
|
|
|
|
**Poor Quality Results** |
|
|
- Use higher quality preset |
|
|
- Enable two-stage mode |
|
|
- Ensure good lighting in original video |
|
|
- Try different professional backgrounds |
|
|
|
|
|
### π System Information |
|
|
|
|
|
- **Core Functions**: {'β
Available' if CORE_FUNCTIONS_AVAILABLE else 'β Not Available'} |
|
|
- **Background Library**: {'β
Available' if UTILITIES_AVAILABLE else 'β Not Available'} |
|
|
- **Two-Stage Processing**: {'β
Available' if TWO_STAGE_AVAILABLE else 'β Not Available'} |
|
|
- **Professional Backgrounds**: {len(PROFESSIONAL_BACKGROUNDS)} presets available |
|
|
""") |
|
|
|
|
|
return demo |
|
|
|
|
|
|
|
|
def create_ui(): |
|
|
"""Compatibility wrapper for create_interface""" |
|
|
return create_interface() |