MogensR commited on
Commit
f0f27f4
·
1 Parent(s): 4525504

Update ui_components.py

Browse files
Files changed (1) hide show
  1. ui_components.py +139 -507
ui_components.py CHANGED
@@ -1,492 +1,195 @@
1
  #!/usr/bin/env python3
2
  """
3
- Enhanced UI Components - BackgroundFX Pro
4
- Streamlined interface with better error handling and user experience
5
  """
6
 
7
  import gradio as gr
8
- import os
9
- import json
10
  import time
11
- import traceback
12
- from typing import Optional, Dict, Any, Tuple
13
- from pathlib import Path
14
 
15
- # Remove redundant Gradio schema patching - handled in core/app.py
16
- print("UI Components: Initializing interface...")
17
-
18
- # Import core functions with comprehensive error handling - Updated paths
19
  try:
20
  from core.app import (
21
- VideoProcessor,
22
  processor,
23
- load_models_with_validation,
24
- process_video_fixed,
25
- get_model_status,
26
- get_cache_status
27
  )
28
  CORE_FUNCTIONS_AVAILABLE = True
29
- print("UI Components: Core functions imported successfully")
30
  except Exception as e:
31
- print(f"UI Components: Core functions import failed: {e}")
32
  CORE_FUNCTIONS_AVAILABLE = False
33
 
34
- # Import utilities with error handling - Updated path
35
  try:
36
- from utils.backgrounds import PROFESSIONAL_BACKGROUNDS # Updated path
37
  UTILITIES_AVAILABLE = True
38
- print("UI Components: Utilities imported successfully")
39
  except Exception as e:
40
- print(f"UI Components: Utilities import failed: {e}")
41
  UTILITIES_AVAILABLE = False
42
- PROFESSIONAL_BACKGROUNDS = {
43
- "office_modern": {"name": "Modern Office", "description": "Clean office environment"},
44
- "studio_blue": {"name": "Professional Blue", "description": "Blue studio background"},
45
- "minimalist": {"name": "Minimalist White", "description": "Clean white background"}
46
- }
47
-
48
- # Import two-stage processor with error handling - Updated path
49
- try:
50
- from processing.two_stage.two_stage_processor import CHROMA_PRESETS # Updated path
51
- TWO_STAGE_AVAILABLE = True
52
- print("UI Components: Two-stage processor available")
53
- except ImportError:
54
- TWO_STAGE_AVAILABLE = False
55
- CHROMA_PRESETS = {
56
- 'standard': {'name': 'Standard Quality'},
57
- 'balanced': {'name': 'Balanced'},
58
- 'high': {'name': 'High Quality'}
59
- }
60
- print("UI Components: Two-stage processor not available")
61
-
62
- class UIStateManager:
63
- """Manage UI state and provide user feedback"""
64
-
65
- def __init__(self):
66
- self.processing_active = False
67
- self.models_loaded = False
68
- self.last_processing_time = None
69
- self.processing_history = []
70
-
71
- def update_processing_state(self, active: bool):
72
- self.processing_active = active
73
- if not active and self.last_processing_time:
74
- duration = time.time() - self.last_processing_time
75
- self.processing_history.append({
76
- 'timestamp': time.time(),
77
- 'duration': duration
78
- })
79
- elif active:
80
- self.last_processing_time = time.time()
81
-
82
- def get_average_processing_time(self) -> float:
83
- if not self.processing_history:
84
- return 0
85
- recent_history = self.processing_history[-5:] # Last 5 processing sessions
86
- return sum(h['duration'] for h in recent_history) / len(recent_history)
87
 
88
- # Global UI state
89
- ui_state = UIStateManager()
90
 
91
  def create_interface():
92
- """Create the enhanced Gradio interface with better UX"""
93
-
94
- # Enhanced processing function with better error handling
95
  def enhanced_process_video(
96
  video_path, bg_method, custom_img, prof_choice,
97
  use_two_stage, chroma_preset, quality_preset,
98
- progress: Optional[gr.Progress] = None
99
  ):
100
- """Enhanced video processing with comprehensive error handling and user feedback"""
101
-
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  if not CORE_FUNCTIONS_AVAILABLE:
103
- return None, "Error: Core processing functions not available", "System Error: Please check installation"
104
-
105
  if not processor.models_loaded:
106
- return None, "Error: Models not loaded", "Please load models first using the 'Load Models' button"
107
-
108
  if not video_path:
109
- return None, "Error: No video uploaded", "Please upload a video file first"
110
-
111
- # Validate inputs
112
  if bg_method == "professional" and not prof_choice:
113
- return None, "Error: No background selected", "Please select a professional background"
114
-
115
  if bg_method == "upload" and not custom_img:
116
- return None, "Error: No custom background", "Please upload a custom background image"
117
-
118
  try:
119
- ui_state.update_processing_state(True)
120
-
121
- # Set quality preset in processor config
122
- if quality_preset and hasattr(processor, 'config'):
123
- processor.config.quality_preset = quality_preset
124
-
125
- def progress_callback(pct, desc):
126
  if progress:
127
  progress(pct, desc)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  return desc
129
-
130
- # Determine background choice
131
- if bg_method == "professional":
132
- background_choice = prof_choice
133
- custom_background_path = None
134
- else:
135
- background_choice = "custom"
136
- custom_background_path = custom_img
137
-
138
- # Process video
139
  result_path, result_message = process_video_fixed(
140
  video_path=video_path,
141
- background_choice=background_choice,
142
- custom_background_path=custom_background_path,
143
- progress_callback=progress_callback,
144
  use_two_stage=bool(use_two_stage),
145
  chroma_preset=chroma_preset or "standard",
146
  preview_mask=False,
147
- preview_greenscreen=False
 
148
  )
149
-
150
- ui_state.update_processing_state(False)
151
-
152
- if result_path:
153
- # Enhanced success message
154
- avg_time = ui_state.get_average_processing_time()
155
- success_info = f"""
156
- Processing Complete!
157
 
158
- Results:
159
- {result_message}
160
-
161
- Performance:
162
- - Average processing time: {avg_time:.1f}s
163
- - Two-stage mode: {'Enabled' if use_two_stage else 'Disabled'}
164
- - Quality preset: {quality_preset or 'Default'}
165
-
166
- Tips:
167
- - Try two-stage mode for better quality
168
- - Use 'fast' preset for quicker processing
169
- - Shorter videos process faster
170
- """
171
- return result_path, success_info, "Processing completed successfully!"
172
- else:
173
- return None, f"Processing failed: {result_message}", f"Error: {result_message}"
174
-
175
  except Exception as e:
176
- ui_state.update_processing_state(False)
177
- error_msg = f"Processing error: {str(e)}"
178
- print(f"UI Error: {error_msg}\n{traceback.format_exc()}")
179
- return None, error_msg, f"System Error: {error_msg}"
180
-
181
- # Enhanced model loading with better feedback
182
- def enhanced_load_models(progress: Optional[gr.Progress] = None):
183
- """Enhanced model loading with detailed feedback"""
184
-
185
- if not CORE_FUNCTIONS_AVAILABLE:
186
- return "Error: Core functions not available", "System Error: Installation incomplete"
187
-
188
- try:
189
- def progress_callback(pct, desc):
190
- if progress:
191
- progress(pct, desc)
192
- return desc
193
-
194
- result = load_models_with_validation(progress_callback)
195
-
196
- if "SUCCESS" in result or "successful" in result.lower():
197
- ui_state.models_loaded = True
198
- enhanced_result = f"""
199
- Models Loaded Successfully!
200
 
201
- Status:
202
- {result}
203
-
204
- Ready for Processing:
205
- - High-quality segmentation (SAM2)
206
- - Professional mask refinement (MatAnyone)
207
- - {'Two-stage green screen mode available' if TWO_STAGE_AVAILABLE else 'Single-stage processing only'}
208
-
209
- Next Steps:
210
- 1. Upload your video
211
- 2. Choose background method
212
- 3. Click 'Process Video'
213
- """
214
- return enhanced_result, "Models loaded successfully! Ready to process videos."
215
- else:
216
- return result, f"Model loading failed: {result}"
217
-
218
- except Exception as e:
219
- error_msg = f"Model loading error: {str(e)}"
220
- print(f"UI Model Loading Error: {error_msg}\n{traceback.format_exc()}")
221
- return error_msg, error_msg
222
-
223
- # Enhanced status functions
224
- def get_enhanced_model_status():
225
- """Get enhanced model status with user-friendly formatting"""
226
- try:
227
- status = get_model_status()
228
- if isinstance(status, dict):
229
- formatted_status = {
230
- "SAM2 Segmentation": "Ready" if status.get('sam2_available') else "Not Loaded",
231
- "MatAnyone Refinement": "Ready" if status.get('matanyone_available') else "Not Loaded",
232
- "Two-Stage Mode": "Available" if status.get('two_stage_available') else "Not Available",
233
- "Device": status.get('device', 'Unknown'),
234
- "Models Validated": "Yes" if status.get('models_loaded') else "No"
235
- }
236
- if 'memory_usage' in status and status['memory_usage']:
237
- memory = status['memory_usage']
238
- if 'gpu_percent' in memory:
239
- formatted_status["GPU Memory"] = f"{memory['gpu_percent']:.1f}% used"
240
- return formatted_status
241
- else:
242
- return {"Status": str(status)}
243
- except Exception as e:
244
- return {"Error": f"Failed to get status: {e}"}
245
-
246
- def get_enhanced_cache_status():
247
- """Get enhanced cache status with detailed information"""
248
- try:
249
- status = get_cache_status()
250
- if isinstance(status, dict):
251
- return {
252
- "Cache Status": "Active" if status.get('models_loaded') else "Inactive",
253
- "Processing Mode": "Two-Stage" if status.get('two_stage_available') else "Single-Stage",
254
- "Configuration": status.get('config', {}),
255
- "System Device": status.get('device', 'Unknown')
256
- }
257
- else:
258
- return {"Cache": str(status)}
259
- except Exception as e:
260
- return {"Error": f"Failed to get cache info: {e}"}
261
-
262
- # Create the main interface
263
  with gr.Blocks(
264
- title="BackgroundFX Pro - Professional Video Background Replacement",
265
- theme=gr.themes.Soft(
266
- primary_hue="blue",
267
- secondary_hue="gray",
268
- neutral_hue="slate"
269
- ),
270
  css="""
271
  .main-header { text-align: center; margin-bottom: 20px; }
272
  .status-box { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0; }
273
- .error-box { background: #fee; border-left: 4px solid #dc3545; padding: 15px; }
274
- .success-box { background: #efe; border-left: 4px solid #28a745; padding: 15px; }
275
- .feature-list { columns: 2; column-gap: 20px; }
276
  """
277
  ) as demo:
278
-
279
- # Header
280
  with gr.Row():
281
- gr.Markdown("""
282
- # BackgroundFX Pro - Video Background Replacement
283
-
284
- Professional-quality video background replacement using AI segmentation and advanced compositing techniques.
285
- """, elem_classes=["main-header"])
286
-
287
- # System status indicator
288
- with gr.Row():
289
- with gr.Column(scale=1):
290
- system_status = gr.HTML(f"""
291
- <div class="status-box">
292
- <h4>System Status</h4>
293
- <ul>
294
- <li>Core Functions: {'Available' if CORE_FUNCTIONS_AVAILABLE else 'Not Available'}</li>
295
- <li>Utilities: {'Available' if UTILITIES_AVAILABLE else 'Not Available'}</li>
296
- <li>Two-Stage Mode: {'Available' if TWO_STAGE_AVAILABLE else 'Not Available'}</li>
297
- </ul>
298
- </div>
299
- """)
300
-
301
  with gr.Row():
302
- # Left column - Input and controls
303
- with gr.Column(scale=1):
304
- gr.Markdown("### Step 1: Upload Your Video")
305
- video_input = gr.Video(
306
- label="Upload your video (MP4, AVI, MOV supported)",
307
- height=300
308
- )
309
-
310
- with gr.Accordion("Video Requirements", open=False):
311
- gr.Markdown("""
312
- **Supported Formats:** MP4, AVI, MOV, MKV
313
- **Max Duration:** 5 minutes (300 seconds)
314
- **Max Resolution:** 4096x4096
315
- **Max File Size:** 2GB
316
-
317
- **Recommendations:**
318
- - Use 1080p or lower for faster processing
319
- - Shorter videos (10-30s) are ideal for testing
320
- - Ensure good lighting and clear person visibility
321
- """)
322
-
323
- gr.Markdown("### Step 2: Choose Background")
324
  background_method = gr.Radio(
325
- choices=["professional", "upload"],
326
- value="professional",
327
- label="Background Method"
328
  )
329
-
330
- # Professional backgrounds
331
- with gr.Group(visible=True) as professional_group:
332
- gr.Markdown("**Professional Background Presets**")
333
-
334
- if UTILITIES_AVAILABLE and PROFESSIONAL_BACKGROUNDS:
335
- choices = [(f"{bg['name']} - {bg['description']}", key)
336
- for key, bg in PROFESSIONAL_BACKGROUNDS.items()]
337
- default_choice = list(PROFESSIONAL_BACKGROUNDS.keys())[0]
338
- else:
339
- choices = [("Modern Office - Clean office environment", "office_modern")]
340
- default_choice = "office_modern"
341
-
342
- professional_choice = gr.Dropdown(
343
- choices=choices,
344
- value=default_choice,
345
- label="Select Professional Background"
346
- )
347
-
348
- # Custom upload - FIXED: Removed 'info' parameter
349
- with gr.Group(visible=False) as upload_group:
350
- gr.Markdown("**Upload Custom Background**")
351
- custom_background = gr.Image(
352
- label="Upload background image",
353
- type="filepath"
354
- )
355
- gr.Markdown("JPG, PNG supported. Will be resized to match video resolution.")
356
-
357
- # Background method visibility control
358
  def update_background_visibility(method):
359
  return (
360
  gr.update(visible=(method == "professional")),
361
  gr.update(visible=(method == "upload"))
362
  )
363
-
364
  background_method.change(
365
  fn=update_background_visibility,
366
  inputs=background_method,
367
- outputs=[professional_group, upload_group]
368
  )
369
-
370
- gr.Markdown("### Step 3: Processing Options")
371
-
372
- with gr.Row():
373
- quality_preset = gr.Dropdown(
374
- choices=[
375
- ("Fast - Quick processing", "fast"),
376
- ("Balanced - Good quality/speed", "balanced"),
377
- ("High - Best quality", "high")
378
- ],
379
- value="balanced",
380
- label="Quality Preset"
381
- )
382
-
383
- with gr.Accordion("Advanced Settings", open=False):
384
- use_two_stage = gr.Checkbox(
385
- label="Enable Two-Stage Processing",
386
- value=False,
387
- interactive=TWO_STAGE_AVAILABLE
388
- )
389
- gr.Markdown("Cinema-quality green screen mode (slower but much better quality)")
390
-
391
- if TWO_STAGE_AVAILABLE:
392
- chroma_preset = gr.Dropdown(
393
- choices=[
394
- ("Standard - General use", "standard"),
395
- ("Studio - Broadcast quality", "studio"),
396
- ("Outdoor - Challenging lighting", "outdoor")
397
- ],
398
- value="standard",
399
- label="Chroma Key Preset"
400
- )
401
- else:
402
- chroma_preset = gr.Dropdown(
403
- choices=[("Standard", "standard")],
404
- value="standard",
405
- label="Chroma Key Preset",
406
- interactive=False
407
- )
408
-
409
- gr.Markdown("### Step 4: Process")
410
-
411
  with gr.Row():
412
- load_models_btn = gr.Button(
413
- "Load Models",
414
- variant="secondary",
415
- size="lg"
416
- )
417
- process_btn = gr.Button(
418
- "Process Video",
419
- variant="primary",
420
- size="lg",
421
- scale=2
422
- )
423
-
424
- # Status and feedback
425
- status_text = gr.Textbox(
426
- label="Status Updates",
427
- value="Ready - Click 'Load Models' to begin",
428
- interactive=False,
429
- lines=6,
430
- max_lines=10
431
- )
432
-
433
- # System monitoring
434
- with gr.Accordion("System Monitoring", open=False):
435
- with gr.Row():
436
- model_status_btn = gr.Button("Check Models", variant="secondary")
437
- cache_status_btn = gr.Button("Check Cache", variant="secondary")
438
-
439
- model_status_display = gr.JSON(label="Model Status", visible=False)
440
- cache_status_display = gr.JSON(label="Cache Status", visible=False)
441
-
442
- # Right column - Output and results
443
- with gr.Column(scale=1):
444
- gr.Markdown("### Results")
445
-
446
- video_output = gr.Video(
447
- label="Processed Video",
448
- height=400
449
- )
450
-
451
- result_info = gr.Textbox(
452
- label="Processing Information",
453
- interactive=False,
454
- lines=12,
455
- max_lines=15,
456
- placeholder="Processing results and statistics will appear here..."
457
- )
458
-
459
- debug_info = gr.Textbox(
460
- label="Debug Information",
461
- interactive=False,
462
- lines=6,
463
- max_lines=10,
464
- placeholder="Debug and system information will appear here...",
465
- visible=False
466
- )
467
-
468
- # Toggle debug visibility
469
- show_debug_btn = gr.Button("Show Debug Info", variant="secondary", size="sm")
470
-
471
- def toggle_debug_visibility(current_visibility):
472
- new_visibility = not current_visibility
473
- return (
474
- gr.update(visible=new_visibility),
475
- "Hide Debug Info" if new_visibility else "Show Debug Info"
476
- )
477
-
478
- show_debug_btn.click(
479
- fn=lambda: toggle_debug_visibility(False), # Will be managed by state
480
- outputs=[debug_info, show_debug_btn]
481
- )
482
-
483
- # Event handlers
484
- load_models_btn.click(
485
- fn=enhanced_load_models,
486
- outputs=[status_text, debug_info],
487
- show_progress=True
488
- )
489
-
490
  process_btn.click(
491
  fn=enhanced_process_video,
492
  inputs=[
@@ -496,92 +199,21 @@ def toggle_debug_visibility(current_visibility):
496
  professional_choice,
497
  use_two_stage,
498
  chroma_preset,
499
- quality_preset
 
 
500
  ],
501
- outputs=[video_output, result_info, debug_info],
502
  show_progress=True
503
  )
504
-
505
- model_status_btn.click(
506
- fn=get_enhanced_model_status,
507
- outputs=[model_status_display],
508
- show_progress=False
509
- ).then(
510
- fn=lambda: gr.update(visible=True),
511
- outputs=[model_status_display]
512
- )
513
-
514
- cache_status_btn.click(
515
- fn=get_enhanced_cache_status,
516
- outputs=[cache_status_display],
517
- show_progress=False
518
- ).then(
519
- fn=lambda: gr.update(visible=True),
520
- outputs=[cache_status_display]
521
  )
522
-
523
- # Information and help section
524
- with gr.Accordion("Help & Information", open=False):
525
- gr.Markdown(f"""
526
- ### How to Use
527
-
528
- 1. **Load Models**: Click 'Load Models' and wait for completion (first-time setup)
529
- 2. **Upload Video**: Choose a video file (MP4 recommended, under 5 minutes)
530
- 3. **Select Background**: Use professional presets or upload your own image
531
- 4. **Configure Quality**: Choose preset based on your speed/quality preference
532
- 5. **Process**: Click 'Process Video' and wait for completion
533
-
534
- ### Processing Modes
535
-
536
- **Single-Stage (Default)**
537
- - Direct background replacement
538
- - Faster processing (2-5x speed)
539
- - Good quality for most use cases
540
- - Recommended for: Social media, quick edits, testing
541
-
542
- **Two-Stage (Premium)**
543
- - Green screen intermediate step
544
- - Cinema-quality edge compositing
545
- - Advanced chroma key algorithms
546
- - Recommended for: Professional content, broadcast, film
547
-
548
- ### Performance Tips
549
-
550
- - **Fast Processing**: Use 'fast' preset, disable two-stage mode
551
- - **Best Quality**: Use 'high' preset, enable two-stage mode
552
- - **GPU Memory**: Processing automatically manages memory and provides fallbacks
553
- - **Video Length**: Shorter videos (10-30s) process much faster
554
-
555
- ### Troubleshooting
556
-
557
- **Models Won't Load**
558
- - Check internet connection (models download from Hugging Face)
559
- - Wait for downloads to complete (may take several minutes first time)
560
- - Try restarting if stuck
561
-
562
- **Processing Fails**
563
- - Ensure video file is not corrupted
564
- - Try shorter clips first (under 30 seconds)
565
- - Check video format (MP4 works best)
566
- - Verify sufficient disk space
567
-
568
- **Poor Quality Results**
569
- - Use higher quality preset
570
- - Enable two-stage mode
571
- - Ensure good lighting in original video
572
- - Try different professional backgrounds
573
-
574
- ### System Information
575
-
576
- - **Core Functions**: {'Available' if CORE_FUNCTIONS_AVAILABLE else 'Not Available'}
577
- - **Background Library**: {'Available' if UTILITIES_AVAILABLE else 'Not Available'}
578
- - **Two-Stage Processing**: {'Available' if TWO_STAGE_AVAILABLE else 'Not Available'}
579
- - **Professional Backgrounds**: {len(PROFESSIONAL_BACKGROUNDS)} presets available
580
- """)
581
-
582
  return demo
583
 
584
- # Compatibility function for existing imports
585
  def create_ui():
586
- """Compatibility wrapper for create_interface"""
587
- return create_interface()
 
1
  #!/usr/bin/env python3
2
  """
3
+ UI Components for BackgroundFX Pro - with Start/Stop, frame counter, and live FPS
 
4
  """
5
 
6
  import gradio as gr
 
 
7
  import time
8
+ import threading
 
 
9
 
10
+ # --- Import backend logic ---
 
 
 
11
  try:
12
  from core.app import (
 
13
  processor,
14
+ load_models_with_validation,
15
+ process_video_fixed,
16
+ get_model_status,
17
+ get_cache_status,
18
  )
19
  CORE_FUNCTIONS_AVAILABLE = True
 
20
  except Exception as e:
21
+ print(f"[UI] Core functions import failed: {e}")
22
  CORE_FUNCTIONS_AVAILABLE = False
23
 
 
24
  try:
25
+ from utils.backgrounds import PROFESSIONAL_BACKGROUNDS
26
  UTILITIES_AVAILABLE = True
 
27
  except Exception as e:
28
+ PROFESSIONAL_BACKGROUNDS = {"office_modern": {"name": "Modern Office", "description": "Default office background"}}
29
  UTILITIES_AVAILABLE = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ # --- UI state management ---
32
+ stop_event = threading.Event()
33
 
34
  def create_interface():
35
+ is_processing = gr.State(False)
36
+ frame_progress = gr.State({"current": 0, "total": 0, "fps": 0.0})
37
+
38
  def enhanced_process_video(
39
  video_path, bg_method, custom_img, prof_choice,
40
  use_two_stage, chroma_preset, quality_preset,
41
+ is_processing_state, frame_progress_state, progress: gr.Progress = None
42
  ):
43
+ """
44
+ Handles video processing (start/stop) and live frame/fps reporting.
45
+ """
46
+ # If already processing: user wants to STOP
47
+ if is_processing_state:
48
+ stop_event.set()
49
+ return None, "Processing stopped by user.", "Processing stopped.", False, {"current": 0, "total": 0, "fps": 0.0}
50
+
51
+ # Otherwise: START processing
52
+ stop_event.clear()
53
+ is_processing_state = True
54
+ frame_progress_state = {"current": 0, "total": 0, "fps": 0.0}
55
+ last_update = time.time()
56
+ last_frame = 0
57
+
58
  if not CORE_FUNCTIONS_AVAILABLE:
59
+ return None, "Core backend not available.", "System error.", False, frame_progress_state
 
60
  if not processor.models_loaded:
61
+ return None, "Models not loaded.", "Load models first.", False, frame_progress_state
 
62
  if not video_path:
63
+ return None, "No video uploaded.", "Upload a video.", False, frame_progress_state
64
+
65
+ # Choose background
66
  if bg_method == "professional" and not prof_choice:
67
+ return None, "No professional background selected.", "Choose a background.", False, frame_progress_state
 
68
  if bg_method == "upload" and not custom_img:
69
+ return None, "No custom background image.", "Upload a background image.", False, frame_progress_state
70
+
71
  try:
72
+ def progress_callback(pct, desc, current_frame=None, total_frames=None):
73
+ # User cancellation check
74
+ if stop_event.is_set():
75
+ raise Exception("Processing stopped by user.")
76
+
77
+ # Standard Gradio progress
 
78
  if progress:
79
  progress(pct, desc)
80
+
81
+ # Update frame count/fps
82
+ nonlocal last_update, last_frame, frame_progress_state
83
+ now = time.time()
84
+ fps = 0.0
85
+ if current_frame is not None and total_frames is not None:
86
+ dt = now - last_update if last_update else 0.01
87
+ frames_done = current_frame - last_frame if last_frame is not None else 0
88
+ fps = frames_done / dt if dt > 0 else 0.0
89
+ last_update = now
90
+ last_frame = current_frame
91
+ frame_progress_state = {
92
+ "current": int(current_frame),
93
+ "total": int(total_frames),
94
+ "fps": round(fps, 2)
95
+ }
96
  return desc
97
+
 
 
 
 
 
 
 
 
 
98
  result_path, result_message = process_video_fixed(
99
  video_path=video_path,
100
+ background_choice=prof_choice if bg_method == "professional" else "custom",
101
+ custom_background_path=custom_img if bg_method == "upload" else None,
102
+ progress_callback=lambda pct, desc: progress_callback(pct, desc), # You may want to pass current_frame/total_frames
103
  use_two_stage=bool(use_two_stage),
104
  chroma_preset=chroma_preset or "standard",
105
  preview_mask=False,
106
+ preview_greenscreen=False,
107
+ stop_event=stop_event, # <-- Backend must support this argument!
108
  )
109
+ is_processing_state = False
110
+ return result_path, result_message, "Processing completed.", False, frame_progress_state
 
 
 
 
 
 
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  except Exception as e:
113
+ is_processing_state = False
114
+ return None, f"Error: {str(e)}", "Error during processing.", False, frame_progress_state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ # --- UI setup ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  with gr.Blocks(
118
+ title="BackgroundFX Pro",
 
 
 
 
 
119
  css="""
120
  .main-header { text-align: center; margin-bottom: 20px; }
121
  .status-box { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0; }
122
+ .live-progress { font-size: 1.1em; font-weight: bold; color: #004085; }
 
 
123
  """
124
  ) as demo:
 
 
125
  with gr.Row():
126
+ gr.Markdown("# BackgroundFX Pro - Video Background Replacement", elem_classes=["main-header"])
127
+
128
+ # --- Main UI fields ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  with gr.Row():
130
+ with gr.Column():
131
+ video_input = gr.Video(label="Upload Video", height=280)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  background_method = gr.Radio(
133
+ ["professional", "upload"], value="professional", label="Background Method"
 
 
134
  )
135
+ professional_choice = gr.Dropdown(
136
+ choices=[(bg['name'], key) for key, bg in PROFESSIONAL_BACKGROUNDS.items()],
137
+ value=list(PROFESSIONAL_BACKGROUNDS.keys())[0],
138
+ label="Professional Background"
139
+ )
140
+ custom_background = gr.Image(label="Upload Background", type="filepath", visible=False)
141
+ quality_preset = gr.Dropdown(
142
+ choices=[
143
+ ("Fast", "fast"),
144
+ ("Balanced", "balanced"),
145
+ ("High Quality", "high")
146
+ ],
147
+ value="balanced",
148
+ label="Quality Preset"
149
+ )
150
+ use_two_stage = gr.Checkbox("Enable Two-Stage Mode", value=False)
151
+ chroma_preset = gr.Dropdown(
152
+ choices=[("Standard", "standard"), ("Studio", "studio"), ("Outdoor", "outdoor")],
153
+ value="standard",
154
+ label="Chroma Key Preset"
155
+ )
156
+ # Dynamic show/hide
 
 
 
 
 
 
 
157
  def update_background_visibility(method):
158
  return (
159
  gr.update(visible=(method == "professional")),
160
  gr.update(visible=(method == "upload"))
161
  )
 
162
  background_method.change(
163
  fn=update_background_visibility,
164
  inputs=background_method,
165
+ outputs=[professional_choice, custom_background]
166
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  with gr.Row():
168
+ load_models_btn = gr.Button("Load Models", variant="secondary")
169
+ process_btn = gr.Button("Process Video", variant="primary")
170
+
171
+ status_text = gr.Textbox(label="Status", value="Ready", interactive=False)
172
+ frame_info = gr.Textbox(label="Frame Progress", value="", interactive=False)
173
+ fps_info = gr.Textbox(label="Frames/sec", value="", interactive=False)
174
+
175
+ with gr.Column():
176
+ video_output = gr.Video(label="Processed Video", height=360)
177
+ result_info = gr.Textbox(label="Processing Info", lines=8, interactive=False)
178
+ debug_info = gr.Textbox(label="Debug Log", lines=6, interactive=False, visible=False)
179
+
180
+ # --- Button actions ---
181
+ def update_process_btn(is_processing):
182
+ return gr.update(value="Stop Processing" if is_processing else "Process Video")
183
+ is_processing.change(update_process_btn, inputs=is_processing, outputs=process_btn)
184
+
185
+ def update_frame_fields(frame_progress):
186
+ total = frame_progress.get("total", 0)
187
+ current = frame_progress.get("current", 0)
188
+ fps = frame_progress.get("fps", 0.0)
189
+ txt = f"{current}/{total}" if total else ""
190
+ return txt, f"{fps:.2f} FPS" if fps else ""
191
+ frame_progress.change(update_frame_fields, inputs=frame_progress, outputs=[frame_info, fps_info])
192
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  process_btn.click(
194
  fn=enhanced_process_video,
195
  inputs=[
 
199
  professional_choice,
200
  use_two_stage,
201
  chroma_preset,
202
+ quality_preset,
203
+ is_processing,
204
+ frame_progress
205
  ],
206
+ outputs=[video_output, result_info, debug_info, is_processing, frame_progress],
207
  show_progress=True
208
  )
209
+
210
+ load_models_btn.click(
211
+ fn=lambda progress=gr.Progress(): load_models_with_validation(progress),
212
+ outputs=[status_text],
213
+ show_progress=True
 
 
 
 
 
 
 
 
 
 
 
 
214
  )
215
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  return demo
217
 
 
218
  def create_ui():
219
+ return create_interface()