MogensR commited on
Commit
bd2b18c
·
1 Parent(s): a542ea3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +230 -884
app.py CHANGED
@@ -1,6 +1,6 @@
1
  #!/usr/bin/env python3
2
  """
3
- High-Quality Video Background Replacement - COMPLETE VERSION
4
  Upload video → Choose professional background → Replace with cinema quality
5
  Features: SAM2 + MatAnyone with multi-fallback loading, professional backgrounds,
6
  cinema-quality processing, lazy loading, and enhanced stability
@@ -26,8 +26,8 @@
26
  from typing import Optional, Tuple, Dict, Any
27
  import logging
28
 
29
- # Import utility functions
30
- from utils import *
31
 
32
  # Fix OpenMP threads issue - remove problematic environment variable
33
  try:
@@ -73,865 +73,6 @@ def patched_get_type(schema):
73
  models_loaded = False
74
  loading_lock = threading.Lock()
75
 
76
- # Professional background templates - Enhanced collection
77
- PROFESSIONAL_BACKGROUNDS = {
78
- "office_modern": {
79
- "name": "Modern Office",
80
- "type": "gradient",
81
- "colors": ["#f8f9fa", "#e9ecef", "#dee2e6"],
82
- "direction": "diagonal",
83
- "description": "Clean, contemporary office environment"
84
- },
85
- "office_executive": {
86
- "name": "Executive Office",
87
- "type": "gradient",
88
- "colors": ["#2c3e50", "#34495e", "#5d6d7e"],
89
- "direction": "vertical",
90
- "description": "Professional executive setting"
91
- },
92
- "studio_blue": {
93
- "name": "Professional Blue",
94
- "type": "gradient",
95
- "colors": ["#1e3c72", "#2a5298", "#3498db"],
96
- "direction": "radial",
97
- "description": "Broadcast-quality blue studio"
98
- },
99
- "studio_green": {
100
- "name": "Broadcast Green",
101
- "type": "color",
102
- "colors": ["#00b894"],
103
- "chroma_key": True,
104
- "description": "Professional green screen replacement"
105
- },
106
- "conference": {
107
- "name": "Conference Room",
108
- "type": "gradient",
109
- "colors": ["#74b9ff", "#0984e3", "#6c5ce7"],
110
- "direction": "horizontal",
111
- "description": "Modern conference room setting"
112
- },
113
- "minimalist": {
114
- "name": "Minimalist White",
115
- "type": "gradient",
116
- "colors": ["#ffffff", "#f1f2f6", "#ddd"],
117
- "direction": "soft_radial",
118
- "description": "Clean, minimal background"
119
- },
120
- "warm_gradient": {
121
- "name": "Warm Sunset",
122
- "type": "gradient",
123
- "colors": ["#ff7675", "#fd79a8", "#fdcb6e"],
124
- "direction": "diagonal",
125
- "description": "Warm, inviting atmosphere"
126
- },
127
- "cool_gradient": {
128
- "name": "Cool Ocean",
129
- "type": "gradient",
130
- "colors": ["#74b9ff", "#0984e3", "#00cec9"],
131
- "direction": "vertical",
132
- "description": "Cool, calming ocean tones"
133
- },
134
- "corporate": {
135
- "name": "Corporate Navy",
136
- "type": "gradient",
137
- "colors": ["#2d3436", "#636e72", "#74b9ff"],
138
- "direction": "radial",
139
- "description": "Corporate professional setting"
140
- },
141
- "creative": {
142
- "name": "Creative Purple",
143
- "type": "gradient",
144
- "colors": ["#6c5ce7", "#a29bfe", "#fd79a8"],
145
- "direction": "diagonal",
146
- "description": "Creative, artistic environment"
147
- },
148
- "tech_dark": {
149
- "name": "Tech Dark",
150
- "type": "gradient",
151
- "colors": ["#0c0c0c", "#2d3748", "#4a5568"],
152
- "direction": "vertical",
153
- "description": "Modern tech/gaming setup"
154
- },
155
- "nature_green": {
156
- "name": "Nature Green",
157
- "type": "gradient",
158
- "colors": ["#27ae60", "#2ecc71", "#58d68d"],
159
- "direction": "soft_radial",
160
- "description": "Natural, organic background"
161
- },
162
- "luxury_gold": {
163
- "name": "Luxury Gold",
164
- "type": "gradient",
165
- "colors": ["#f39c12", "#e67e22", "#d68910"],
166
- "direction": "diagonal",
167
- "description": "Premium, luxury setting"
168
- },
169
- "medical_clean": {
170
- "name": "Medical Clean",
171
- "type": "gradient",
172
- "colors": ["#ecf0f1", "#bdc3c7", "#95a5a6"],
173
- "direction": "horizontal",
174
- "description": "Clean, medical/healthcare setting"
175
- },
176
- "education_blue": {
177
- "name": "Education Blue",
178
- "type": "gradient",
179
- "colors": ["#3498db", "#5dade2", "#85c1e9"],
180
- "direction": "vertical",
181
- "description": "Educational, learning environment"
182
- }
183
- }
184
-
185
- def download_and_setup_models():
186
- """ENHANCED download and setup with multiple fallback methods and lazy loading"""
187
- global sam2_predictor, matanyone_model, models_loaded
188
-
189
- with loading_lock:
190
- if models_loaded:
191
- return "✅ High-quality models already loaded"
192
-
193
- try:
194
- logger.info("🔄 Starting ENHANCED model loading with multiple fallbacks...")
195
-
196
- # Check environment and system capabilities
197
- is_hf_space = os.getenv("SPACE_ID") is not None
198
- is_colab = 'google.colab' in sys.modules
199
- is_kaggle = os.environ.get('KAGGLE_KERNEL_RUN_TYPE') is not None
200
-
201
- env_type = "HuggingFace Space" if is_hf_space else "Google Colab" if is_colab else "Kaggle" if is_kaggle else "Local"
202
- logger.info(f"Environment detected: {env_type}")
203
-
204
- # Load PyTorch and check GPU
205
- import torch
206
- logger.info(f"✅ PyTorch {torch.__version__} - CUDA: {torch.cuda.is_available()}")
207
-
208
- if torch.cuda.is_available():
209
- try:
210
- gpu_name = torch.cuda.get_device_name(0)
211
- gpu_memory = torch.cuda.get_device_properties(0).total_memory / 1e9
212
- logger.info(f"🎮 GPU: {gpu_name} ({gpu_memory:.1f}GB)")
213
- except Exception as e:
214
- logger.info(f"🎮 GPU available but details unavailable: {e}")
215
-
216
- # === ENHANCED SAM2 LOADING WITH MULTIPLE METHODS ===
217
- sam2_loaded = False
218
- device = "cuda" if torch.cuda.is_available() else "cpu"
219
-
220
- # Method 1: Try direct import (requirements.txt installation)
221
- try:
222
- logger.info("🔄 SAM2 Method 1: Direct import from requirements...")
223
- from sam2.build_sam import build_sam2
224
- from sam2.sam2_image_predictor import SAM2ImagePredictor
225
- sam2_loaded = True
226
- logger.info("✅ SAM2 imported directly from installed package")
227
- except ImportError as e:
228
- logger.info(f"❌ SAM2 Method 1 failed: {e}")
229
-
230
- # Method 2: Add known paths and try again
231
- if not sam2_loaded:
232
- try:
233
- logger.info("🔄 SAM2 Method 2: Adding SAM2 paths...")
234
- possible_paths = [
235
- '/tmp/segment-anything-2',
236
- './segment-anything-2',
237
- '/opt/ml/code/segment-anything-2',
238
- '/workspace/segment-anything-2',
239
- '/content/segment-anything-2', # Colab
240
- '/kaggle/working/segment-anything-2', # Kaggle
241
- os.path.expanduser('~/segment-anything-2'), # Home directory
242
- ]
243
-
244
- for path in possible_paths:
245
- if os.path.exists(path) and path not in sys.path:
246
- sys.path.insert(0, path)
247
- logger.info(f"✅ Added {path} to Python path")
248
-
249
- from sam2.build_sam import build_sam2
250
- from sam2.sam2_image_predictor import SAM2ImagePredictor
251
- sam2_loaded = True
252
- logger.info("✅ SAM2 imported via path addition")
253
- except ImportError as e:
254
- logger.info(f"❌ SAM2 Method 2 failed: {e}")
255
-
256
- # Method 3: Clone repository if needed
257
- if not sam2_loaded:
258
- try:
259
- logger.info("🔄 SAM2 Method 3: Cloning repository...")
260
- sam2_dir = "/tmp/segment-anything-2"
261
-
262
- if not os.path.exists(sam2_dir):
263
- logger.info("📥 Cloning SAM2 repository...")
264
- clone_cmd = f"git clone --depth 1 https://github.com/facebookresearch/segment-anything-2.git {sam2_dir}"
265
- result = os.system(clone_cmd)
266
- if result == 0:
267
- logger.info("✅ SAM2 repository cloned successfully")
268
- else:
269
- raise Exception("Git clone failed")
270
-
271
- if sam2_dir not in sys.path:
272
- sys.path.insert(0, sam2_dir)
273
-
274
- from sam2.build_sam import build_sam2
275
- from sam2.sam2_image_predictor import SAM2ImagePredictor
276
- sam2_loaded = True
277
- logger.info("✅ SAM2 imported after cloning")
278
- except Exception as e:
279
- logger.info(f"❌ SAM2 Method 3 failed: {e}")
280
-
281
- # Method 4: Install via pip as last resort
282
- if not sam2_loaded:
283
- try:
284
- logger.info("🔄 SAM2 Method 4: Installing via pip...")
285
- install_cmd = "pip install git+https://github.com/facebookresearch/segment-anything-2.git"
286
- result = os.system(install_cmd)
287
- if result == 0:
288
- from sam2.build_sam import build_sam2
289
- from sam2.sam2_image_predictor import SAM2ImagePredictor
290
- sam2_loaded = True
291
- logger.info("✅ SAM2 installed and imported via pip")
292
- else:
293
- raise Exception("Pip install failed")
294
- except Exception as e:
295
- logger.info(f"❌ SAM2 Method 4 failed: {e}")
296
-
297
- if not sam2_loaded:
298
- logger.warning("❌ All SAM2 loading methods failed, using OpenCV fallback")
299
- sam2_predictor = create_opencv_segmentation_fallback()
300
- else:
301
- # Choose model size based on environment and resources
302
- if (is_hf_space and not torch.cuda.is_available()) or device == "cpu":
303
- model_name = "sam2_hiera_tiny"
304
- checkpoint_url = "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.pt"
305
- logger.info("🔧 Using SAM2 Tiny for CPU/limited resources")
306
- else:
307
- model_name = "sam2_hiera_large"
308
- checkpoint_url = "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt"
309
- logger.info("🔧 Using SAM2 Large for maximum quality")
310
-
311
- # Download checkpoint with progress tracking and caching
312
- cache_dir = os.path.expanduser("~/.cache/sam2")
313
- os.makedirs(cache_dir, exist_ok=True)
314
- sam2_checkpoint = os.path.join(cache_dir, f"{model_name}.pt")
315
-
316
- if not os.path.exists(sam2_checkpoint):
317
- logger.info(f"📥 Downloading {model_name} checkpoint...")
318
- try:
319
- response = requests.get(checkpoint_url, stream=True)
320
- total_size = int(response.headers.get('content-length', 0))
321
- downloaded = 0
322
-
323
- with open(sam2_checkpoint, 'wb') as f:
324
- for chunk in response.iter_content(chunk_size=8192):
325
- if chunk:
326
- f.write(chunk)
327
- downloaded += len(chunk)
328
- if total_size > 0 and downloaded % (total_size // 20) < 8192:
329
- percent = (downloaded / total_size) * 100
330
- logger.info(f"📥 Download progress: {percent:.1f}%")
331
-
332
- logger.info(f"✅ {model_name} downloaded successfully")
333
- except Exception as e:
334
- logger.error(f"❌ Download failed: {e}")
335
- raise
336
- else:
337
- logger.info(f"✅ Using cached {model_name}")
338
-
339
- # Load SAM2 model with comprehensive fallbacks
340
- try:
341
- logger.info(f"🚀 Loading SAM2 {model_name} on {device}...")
342
- model_cfg = f"{model_name}.yaml"
343
-
344
- # Create config dynamically if missing
345
- config_path = os.path.join("/tmp/segment-anything-2/sam2_configs", model_cfg)
346
- if not os.path.exists(config_path):
347
- os.makedirs(os.path.dirname(config_path), exist_ok=True)
348
- if "tiny" in model_name:
349
- config_content = """
350
- model:
351
- _target_: sam2.modeling.sam2_base.SAM2Base
352
- image_encoder:
353
- _target_: sam2.modeling.backbones.hieradet.Hiera
354
- embed_dim: 96
355
- num_heads: 1
356
- memory_encoder:
357
- _target_: sam2.modeling.memory_encoder.MemoryEncoder
358
- out_dim: 64
359
- memory_attention:
360
- _target_: sam2.modeling.memory_attention.MemoryAttention
361
- d_model: 256
362
- sam_mask_decoder:
363
- _target_: sam2.modeling.sam.mask_decoder.MaskDecoder
364
- transformer_dim: 256
365
- """
366
- else:
367
- config_content = """
368
- model:
369
- _target_: sam2.modeling.sam2_base.SAM2Base
370
- image_encoder:
371
- _target_: sam2.modeling.backbones.hieradet.Hiera
372
- embed_dim: 144
373
- num_heads: 2
374
- memory_encoder:
375
- _target_: sam2.modeling.memory_encoder.MemoryEncoder
376
- out_dim: 64
377
- memory_attention:
378
- _target_: sam2.modeling.memory_attention.MemoryAttention
379
- d_model: 256
380
- sam_mask_decoder:
381
- _target_: sam2.modeling.sam.mask_decoder.MaskDecoder
382
- transformer_dim: 256
383
- """
384
- with open(config_path, 'w') as f:
385
- f.write(config_content)
386
- logger.info(f"✅ Created config: {config_path}")
387
-
388
- # Memory optimization for limited resources
389
- if device == "cpu" or is_hf_space:
390
- torch.set_num_threads(min(4, os.cpu_count() or 1))
391
- if torch.cuda.is_available():
392
- torch.cuda.empty_cache()
393
-
394
- # Try loading on specified device
395
- sam2_model = build_sam2(model_cfg, sam2_checkpoint, device=device)
396
- sam2_predictor = SAM2ImagePredictor(sam2_model)
397
- logger.info(f"✅ SAM2 model loaded successfully on {device}")
398
-
399
- except Exception as e:
400
- if device == "cuda":
401
- logger.warning(f"❌ GPU loading failed: {e}")
402
- logger.info("🔄 Trying CPU fallback...")
403
- try:
404
- # Force CPU loading
405
- sam2_model = build_sam2(model_cfg, sam2_checkpoint, device="cpu")
406
- sam2_predictor = SAM2ImagePredictor(sam2_model)
407
- device = "cpu"
408
- logger.info("✅ SAM2 loaded on CPU fallback")
409
- except Exception as e2:
410
- logger.error(f"❌ CPU fallback also failed: {e2}")
411
- logger.info("🔄 Using OpenCV segmentation fallback")
412
- sam2_predictor = create_opencv_segmentation_fallback()
413
- else:
414
- logger.error(f"❌ SAM2 loading failed: {e}")
415
- logger.info("🔄 Using OpenCV segmentation fallback")
416
- sam2_predictor = create_opencv_segmentation_fallback()
417
-
418
- # === ENHANCED MATANYONE LOADING WITH MULTIPLE METHODS ===
419
- matanyone_loaded = False
420
-
421
- # Method 1: Try HuggingFace Hub integration
422
- try:
423
- logger.info("🔄 MatAnyone Method 1: HuggingFace Hub...")
424
- from huggingface_hub import hf_hub_download
425
- from matanyone import InferenceCore
426
- matanyone_model = InferenceCore("PeiqingYang/MatAnyone")
427
- matanyone_loaded = True
428
- logger.info("✅ MatAnyone loaded via HuggingFace Hub")
429
- except Exception as e:
430
- logger.info(f"❌ MatAnyone Method 1 failed: {e}")
431
-
432
- # Method 2: Try direct import
433
- if not matanyone_loaded:
434
- try:
435
- logger.info("🔄 MatAnyone Method 2: Direct import...")
436
- matanyone_paths = [
437
- '/tmp/MatAnyone',
438
- './MatAnyone',
439
- '/content/MatAnyone',
440
- '/kaggle/working/MatAnyone'
441
- ]
442
-
443
- for path in matanyone_paths:
444
- if os.path.exists(path):
445
- sys.path.append(path)
446
- break
447
-
448
- from inference import MatAnyoneInference
449
- matanyone_model = MatAnyoneInference()
450
- matanyone_loaded = True
451
- logger.info("✅ MatAnyone loaded via direct import")
452
- except Exception as e:
453
- logger.info(f"❌ MatAnyone Method 2 failed: {e}")
454
-
455
- # Method 3: Try GitHub installation
456
- if not matanyone_loaded:
457
- try:
458
- logger.info("🔄 MatAnyone Method 3: Installing from GitHub...")
459
- install_cmd = "pip install git+https://github.com/pq-yang/MatAnyone.git"
460
- result = os.system(install_cmd)
461
- if result == 0:
462
- from matanyone import InferenceCore
463
- matanyone_model = InferenceCore("PeiqingYang/MatAnyone")
464
- matanyone_loaded = True
465
- logger.info("✅ MatAnyone installed and loaded via GitHub")
466
- else:
467
- raise Exception("GitHub install failed")
468
- except Exception as e:
469
- logger.info(f"❌ MatAnyone Method 3 failed: {e}")
470
-
471
- # Method 4: Enhanced OpenCV fallback (CINEMA QUALITY)
472
- if not matanyone_loaded:
473
- logger.info("🎨 Using ENHANCED OpenCV fallback for cinema-quality matting...")
474
- matanyone_model = create_enhanced_matting_fallback()
475
- matanyone_loaded = True
476
-
477
- # Memory cleanup
478
- gc.collect()
479
- if torch.cuda.is_available():
480
- torch.cuda.empty_cache()
481
-
482
- models_loaded = True
483
- gpu_info = ""
484
- if torch.cuda.is_available() and device == "cuda":
485
- try:
486
- gpu_info = f" (GPU: {torch.cuda.get_device_name(0)})"
487
- except:
488
- gpu_info = " (GPU)"
489
- else:
490
- gpu_info = " (CPU)"
491
-
492
- success_msg = f"✅ ENHANCED high-quality models loaded successfully!{gpu_info}"
493
- logger.info(success_msg)
494
- return success_msg
495
-
496
- except Exception as e:
497
- error_msg = f"❌ Enhanced loading failed: {str(e)}"
498
- logger.error(error_msg)
499
- logger.error(f"Full traceback: {traceback.format_exc()}")
500
- return error_msg
501
-
502
- def create_opencv_segmentation_fallback():
503
- """Create comprehensive OpenCV-based segmentation fallback"""
504
- class OpenCVSegmentationFallback:
505
- def __init__(self):
506
- logger.info("🔧 Initializing OpenCV segmentation fallback")
507
- # Initialize background subtractor for better segmentation
508
- self.bg_subtractor = cv2.createBackgroundSubtractorMOG2(detectShadows=True)
509
- self.image = None
510
-
511
- def set_image(self, image):
512
- self.image = image.copy()
513
-
514
- def predict(self, point_coords, point_labels, multimask_output=True):
515
- """Advanced OpenCV-based person segmentation with multiple techniques"""
516
- if self.image is None:
517
- raise ValueError("No image set")
518
-
519
- h, w = self.image.shape[:2]
520
-
521
- try:
522
- # Multi-method segmentation approach
523
- masks = []
524
-
525
- # Method 1: Skin tone detection
526
- hsv = cv2.cvtColor(self.image, cv2.COLOR_BGR2HSV)
527
-
528
- # Enhanced skin tone ranges
529
- lower_skin1 = np.array([0, 20, 70], dtype=np.uint8)
530
- upper_skin1 = np.array([20, 255, 255], dtype=np.uint8)
531
- lower_skin2 = np.array([0, 20, 70], dtype=np.uint8)
532
- upper_skin2 = np.array([25, 255, 255], dtype=np.uint8)
533
-
534
- skin_mask1 = cv2.inRange(hsv, lower_skin1, upper_skin1)
535
- skin_mask2 = cv2.inRange(hsv, lower_skin2, upper_skin2)
536
- skin_mask = cv2.bitwise_or(skin_mask1, skin_mask2)
537
-
538
- # Method 2: Edge detection for person outline
539
- gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
540
- edges = cv2.Canny(gray, 50, 150)
541
-
542
- # Method 3: Color-based segmentation
543
- lab = cv2.cvtColor(self.image, cv2.COLOR_BGR2LAB)
544
-
545
- # Method 4: Focus on center region with point guidance
546
- center_x, center_y = w//2, h//2
547
- if len(point_coords) > 0:
548
- # Use provided points as guidance
549
- center_x = int(np.mean(point_coords[:, 0]))
550
- center_y = int(np.mean(point_coords[:, 1]))
551
-
552
- # Create center-biased mask
553
- center_mask = np.zeros((h, w), dtype=np.uint8)
554
- roi_width = w // 3
555
- roi_height = h // 2
556
- cv2.ellipse(center_mask, (center_x, center_y), (roi_width, roi_height), 0, 0, 360, 255, -1)
557
-
558
- # Combine different segmentation methods
559
- combined_mask = cv2.bitwise_or(skin_mask, edges // 4)
560
- combined_mask = cv2.bitwise_and(combined_mask, center_mask)
561
-
562
- # Morphological operations for cleanup
563
- kernel_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
564
- kernel_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
565
-
566
- combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel_close)
567
- combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel_open)
568
-
569
- # Fill holes using contour detection
570
- contours, _ = cv2.findContours(combined_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
571
-
572
- if contours:
573
- # Find largest contour (likely person)
574
- largest_contour = max(contours, key=cv2.contourArea)
575
-
576
- # Create mask from largest contour
577
- mask = np.zeros((h, w), dtype=np.uint8)
578
- cv2.fillPoly(mask, [largest_contour], 255)
579
-
580
- # Smooth the mask
581
- mask = cv2.GaussianBlur(mask, (5, 5), 2.0)
582
- mask = (mask > 127).astype(np.uint8)
583
- else:
584
- # Fallback: use center region
585
- mask = center_mask
586
-
587
- # Additional refinement
588
- mask = cv2.medianBlur(mask, 5)
589
-
590
- # Return in SAM2-compatible format
591
- masks.append(mask)
592
- scores = [1.0]
593
-
594
- return masks, scores, None
595
-
596
- except Exception as e:
597
- logger.warning(f"OpenCV segmentation error: {e}")
598
- # Ultimate fallback: center rectangle
599
- mask = np.zeros((h, w), dtype=np.uint8)
600
- x1, y1 = w//4, h//6
601
- x2, y2 = 3*w//4, 5*h//6
602
- mask[y1:y2, x1:x2] = 255
603
- return [mask], [1.0], None
604
-
605
- return OpenCVSegmentationFallback()
606
-
607
- def create_enhanced_matting_fallback():
608
- """Create enhanced matting fallback with advanced OpenCV techniques"""
609
- class EnhancedMattingFallback:
610
- def __init__(self):
611
- logger.info("🎨 Initializing enhanced matting fallback")
612
-
613
- def infer(self, image, mask):
614
- """Enhanced mask refinement using advanced OpenCV techniques"""
615
- try:
616
- # Ensure proper format
617
- if len(mask.shape) == 3:
618
- mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
619
-
620
- # Multi-stage refinement process
621
-
622
- # Stage 1: Bilateral filter for edge-preserving smoothing
623
- refined_mask = cv2.bilateralFilter(mask, 9, 75, 75)
624
-
625
- # Stage 2: Morphological operations for structure cleanup
626
- kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
627
- refined_mask = cv2.morphologyEx(refined_mask, cv2.MORPH_CLOSE, kernel_ellipse)
628
- refined_mask = cv2.morphologyEx(refined_mask, cv2.MORPH_OPEN, kernel_ellipse)
629
-
630
- # Stage 3: Gaussian blur for smooth edges
631
- refined_mask = cv2.GaussianBlur(refined_mask, (3, 3), 1.0)
632
-
633
- # Stage 4: Edge enhancement for cinema quality
634
- edges = cv2.Canny(refined_mask, 50, 150)
635
- edge_enhancement = cv2.dilate(edges, np.ones((2, 2), np.uint8), iterations=1)
636
- refined_mask = cv2.bitwise_or(refined_mask, edge_enhancement // 4)
637
-
638
- # Stage 5: Distance transform for smooth transitions
639
- dist_transform = cv2.distanceTransform(refined_mask, cv2.DIST_L2, 5)
640
- dist_transform = cv2.normalize(dist_transform, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
641
-
642
- # Combine distance transform with original mask
643
- alpha = 0.7
644
- refined_mask = cv2.addWeighted(refined_mask, alpha, dist_transform, 1-alpha, 0)
645
-
646
- # Stage 6: Final smoothing and cleanup
647
- refined_mask = cv2.medianBlur(refined_mask, 3)
648
-
649
- # Stage 7: Ensure smooth gradients at edges
650
- refined_mask = cv2.GaussianBlur(refined_mask, (1, 1), 0.5)
651
-
652
- return refined_mask
653
-
654
- except Exception as e:
655
- logger.warning(f"Enhanced matting error: {e}")
656
- return mask if len(mask.shape) == 2 else cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
657
-
658
- return EnhancedMattingFallback()
659
-
660
- def segment_person_hq(image):
661
- """High-quality person segmentation using SAM2 or fallback with optimized points"""
662
- try:
663
- # Set image for segmentation
664
- sam2_predictor.set_image(image)
665
-
666
- h, w = image.shape[:2]
667
-
668
- # Enhanced point selection (covers head, torso, limbs, and edges)
669
- points = np.array([
670
- [w//2, h//4], # Top-center (head)
671
- [w//2, h//2], # Center (torso)
672
- [w//2, 3*h//4], # Bottom-center (legs)
673
- [w//4, h//2], # Left-side (arm)
674
- [3*w//4, h//2], # Right-side (arm)
675
- [w//5, h//5], # Top-left (hair/accessories)
676
- [4*w//5, h//5] # Top-right (hair/accessories)
677
- ])
678
- labels = np.ones(len(points)) # All positive points
679
-
680
- # Predict with high quality settings
681
- masks, scores, _ = sam2_predictor.predict(
682
- point_coords=points,
683
- point_labels=labels,
684
- multimask_output=True
685
- )
686
-
687
- # Select best mask based on score and size
688
- best_idx = np.argmax(scores)
689
- best_mask = masks[best_idx]
690
-
691
- # Post-processing for better quality
692
- if len(best_mask.shape) > 2:
693
- best_mask = best_mask.squeeze()
694
-
695
- # Ensure binary mask
696
- if best_mask.dtype != np.uint8:
697
- best_mask = (best_mask * 255).astype(np.uint8)
698
-
699
- # Sharper edges (reduced blur)
700
- kernel = np.ones((3, 3), np.uint8)
701
- best_mask = cv2.morphologyEx(best_mask, cv2.MORPH_CLOSE, kernel)
702
-
703
- # Apply reduced Gaussian smoothing for sharper edges
704
- best_mask = cv2.GaussianBlur(best_mask.astype(np.float32), (3, 3), 0.8) # Reduced from 1.0
705
-
706
- return (best_mask * 255).astype(np.uint8) if best_mask.max() <= 1.0 else best_mask.astype(np.uint8)
707
-
708
- except Exception as e:
709
- logger.error(f"Segmentation error: {e}")
710
- # Return center region as fallback
711
- h, w = image.shape[:2]
712
- fallback_mask = np.zeros((h, w), dtype=np.uint8)
713
- x1, y1 = w//4, h//6
714
- x2, y2 = 3*w//4, 5*h//6
715
- fallback_mask[y1:y2, x1:x2] = 255
716
- return fallback_mask
717
-
718
- def refine_mask_hq(image, mask):
719
- """Cinema-quality mask refinement with stronger edge preservation"""
720
- try:
721
- # Apply pre-processing to image for better matting
722
- image_filtered = cv2.bilateralFilter(image, 10, 75, 75) # Increased from 9 to 10
723
-
724
- # Use MatAnyone or fallback for professional matting
725
- refined_mask = matanyone_model.infer(image_filtered, mask)
726
-
727
- # Ensure proper format
728
- if len(refined_mask.shape) == 3:
729
- refined_mask = cv2.cvtColor(refined_mask, cv2.COLOR_BGR2GRAY)
730
-
731
- # Stronger edge preservation with bilateral filter
732
- refined_mask = cv2.bilateralFilter(refined_mask, 10, 75, 75) # Increased from default
733
-
734
- # Post-process for smooth edges
735
- refined_mask = cv2.medianBlur(refined_mask, 3)
736
-
737
- return refined_mask
738
-
739
- except Exception as e:
740
- logger.error(f"Mask refinement error: {e}")
741
- # Return original mask if refinement fails
742
- return mask if len(mask.shape) == 2 else cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
743
-
744
- def create_green_screen_background(frame):
745
- """Create green screen background (Stage 1 of two-stage process)"""
746
- h, w = frame.shape[:2]
747
- green_screen = np.full((h, w, 3), (0, 177, 64), dtype=np.uint8) # Professional green screen color
748
- return green_screen
749
-
750
- def create_professional_background(bg_config, width, height):
751
- """Create professional background based on configuration"""
752
- try:
753
- if bg_config["type"] == "color":
754
- # Solid color background
755
- color_hex = bg_config["colors"][0].lstrip('#')
756
- color_rgb = tuple(int(color_hex[i:i+2], 16) for i in (0, 2, 4))
757
- color_bgr = color_rgb[::-1] # Convert RGB to BGR
758
- background = np.full((height, width, 3), color_bgr, dtype=np.uint8)
759
-
760
- elif bg_config["type"] == "gradient":
761
- background = create_gradient_background(bg_config, width, height)
762
-
763
- else:
764
- # Fallback to solid color
765
- background = np.full((height, width, 3), (128, 128, 128), dtype=np.uint8)
766
-
767
- return background
768
-
769
- except Exception as e:
770
- logger.error(f"Background creation error: {e}")
771
- # Return neutral gray background as fallback
772
- return np.full((height, width, 3), (128, 128, 128), dtype=np.uint8)
773
-
774
- def create_gradient_background(bg_config, width, height):
775
- """Create high-quality gradient backgrounds with comprehensive direction support"""
776
- try:
777
- colors = bg_config["colors"]
778
- direction = bg_config.get("direction", "vertical")
779
-
780
- # Convert hex colors to RGB
781
- rgb_colors = []
782
- for color_hex in colors:
783
- color_hex = color_hex.lstrip('#')
784
- try:
785
- rgb = tuple(int(color_hex[i:i+2], 16) for i in (0, 2, 4))
786
- rgb_colors.append(rgb)
787
- except ValueError:
788
- # Fallback for invalid color
789
- rgb_colors.append((128, 128, 128))
790
-
791
- if not rgb_colors:
792
- rgb_colors = [(128, 128, 128)] # Fallback color
793
-
794
- # Create PIL image for high-quality gradients
795
- pil_img = Image.new('RGB', (width, height))
796
- draw = ImageDraw.Draw(pil_img)
797
-
798
- # Helper function for color interpolation
799
- def interpolate_color(colors, progress):
800
- if len(colors) == 1:
801
- return colors[0]
802
- elif len(colors) == 2:
803
- r = int(colors[0][0] + (colors[1][0] - colors[0][0]) * progress)
804
- g = int(colors[0][1] + (colors[1][1] - colors[0][1]) * progress)
805
- b = int(colors[0][2] + (colors[1][2] - colors[0][2]) * progress)
806
- return (r, g, b)
807
- else:
808
- # Multi-color gradient
809
- segment = progress * (len(colors) - 1)
810
- idx = int(segment)
811
- local_progress = segment - idx
812
-
813
- if idx >= len(colors) - 1:
814
- return colors[-1]
815
- else:
816
- c1, c2 = colors[idx], colors[idx + 1]
817
- r = int(c1[0] + (c2[0] - c1[0]) * local_progress)
818
- g = int(c1[1] + (c2[1] - c1[1]) * local_progress)
819
- b = int(c1[2] + (c2[2] - c1[2]) * local_progress)
820
- return (r, g, b)
821
-
822
- if direction == "vertical":
823
- # Vertical gradient - optimized line drawing
824
- for y in range(height):
825
- progress = y / height if height > 0 else 0
826
- color = interpolate_color(rgb_colors, progress)
827
- draw.line([(0, y), (width, y)], fill=color)
828
-
829
- elif direction == "horizontal":
830
- # Horizontal gradient - optimized line drawing
831
- for x in range(width):
832
- progress = x / width if width > 0 else 0
833
- color = interpolate_color(rgb_colors, progress)
834
- draw.line([(x, 0), (x, height)], fill=color)
835
-
836
- elif direction == "diagonal":
837
- # Diagonal gradient - optimized pixel setting
838
- max_distance = width + height
839
- for y in range(height):
840
- for x in range(width):
841
- progress = (x + y) / max_distance if max_distance > 0 else 0
842
- progress = min(1.0, progress)
843
- color = interpolate_color(rgb_colors, progress)
844
- pil_img.putpixel((x, y), color)
845
-
846
- elif direction in ["radial", "soft_radial"]:
847
- # Radial gradient - optimized with center calculation
848
- center_x, center_y = width // 2, height // 2
849
- max_distance = np.sqrt(center_x**2 + center_y**2)
850
-
851
- for y in range(height):
852
- for x in range(width):
853
- distance = np.sqrt((x - center_x)**2 + (y - center_y)**2)
854
- progress = distance / max_distance if max_distance > 0 else 0
855
- progress = min(1.0, progress)
856
-
857
- if direction == "soft_radial":
858
- progress = progress**0.7 # Softer falloff
859
-
860
- color = interpolate_color(rgb_colors, progress)
861
- pil_img.putpixel((x, y), color)
862
-
863
- else:
864
- # Default to vertical gradient for unknown directions
865
- for y in range(height):
866
- progress = y / height if height > 0 else 0
867
- color = interpolate_color(rgb_colors, progress)
868
- draw.line([(0, y), (width, y)], fill=color)
869
-
870
- # Convert PIL to OpenCV format (RGB to BGR)
871
- background = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
872
- return background
873
-
874
- except Exception as e:
875
- logger.error(f"Gradient creation error: {e}")
876
- # Return simple gradient fallback
877
- background = np.zeros((height, width, 3), dtype=np.uint8)
878
- for y in range(height):
879
- intensity = int(255 * (y / height)) if height > 0 else 128
880
- background[y, :] = [intensity, intensity, intensity]
881
- return background
882
-
883
- def replace_background_hq(frame, mask, background):
884
- """High-quality background replacement with advanced compositing"""
885
- try:
886
- # Resize background to match frame exactly with high-quality interpolation
887
- background = cv2.resize(background, (frame.shape[1], frame.shape[0]),
888
- interpolation=cv2.INTER_LANCZOS4)
889
-
890
- # Ensure mask is single channel
891
- if len(mask.shape) == 3:
892
- mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
893
-
894
- # Convert mask to float and normalize
895
- mask_float = mask.astype(np.float32) / 255.0
896
-
897
- # Apply edge feathering for smooth transitions
898
- feather_radius = 3
899
- kernel_size = feather_radius * 2 + 1
900
- mask_feathered = cv2.GaussianBlur(mask_float, (kernel_size, kernel_size), feather_radius/3)
901
-
902
- # Create 3-channel mask
903
- mask_3channel = np.stack([mask_feathered] * 3, axis=2)
904
-
905
- # High-quality compositing with gamma correction for realistic lighting
906
- frame_linear = np.power(frame.astype(np.float32) / 255.0, 2.2)
907
- background_linear = np.power(background.astype(np.float32) / 255.0, 2.2)
908
-
909
- # Composite in linear color space for accurate blending
910
- result_linear = frame_linear * mask_3channel + background_linear * (1 - mask_3channel)
911
-
912
- # Convert back to sRGB color space
913
- result = np.power(result_linear, 1/2.2) * 255.0
914
- result = np.clip(result, 0, 255).astype(np.uint8)
915
-
916
- return result
917
-
918
- except Exception as e:
919
- logger.error(f"Background replacement error: {e}")
920
- # Simple fallback compositing
921
- try:
922
- if len(mask.shape) == 3:
923
- mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
924
-
925
- background = cv2.resize(background, (frame.shape[1], frame.shape[0]))
926
- mask_normalized = mask.astype(np.float32) / 255.0
927
- mask_3channel = np.stack([mask_normalized] * 3, axis=2)
928
-
929
- result = frame * mask_3channel + background * (1 - mask_3channel)
930
- return result.astype(np.uint8)
931
- except:
932
- # Ultimate fallback - return original frame
933
- return frame
934
-
935
  def process_video_hq(video_path, background_choice, custom_background_path, progress=gr.Progress()):
936
  """TWO-STAGE High-quality video processing: Original → Green Screen → Final Background"""
937
  if not models_loaded:
@@ -1168,27 +309,6 @@ def process_video_hq(video_path, background_choice, custom_background_path, prog
1168
  logger.error(f"Video processing error: {traceback.format_exc()}")
1169
  return None, error_msg
1170
 
1171
- def get_model_status():
1172
- """Get current model loading status with detailed information"""
1173
- if models_loaded:
1174
- try:
1175
- gpu_info = ""
1176
- if torch.cuda.is_available():
1177
- try:
1178
- gpu_name = torch.cuda.get_device_name(0)
1179
- gpu_memory = torch.cuda.get_device_properties(0).total_memory / 1e9
1180
- gpu_info = f" (GPU: {gpu_name[:20]}{'...' if len(gpu_name) > 20 else ''} - {gpu_memory:.1f}GB)"
1181
- except:
1182
- gpu_info = " (GPU Available)"
1183
- else:
1184
- gpu_info = " (CPU Mode)"
1185
-
1186
- return f"✅ ENHANCED high-quality models loaded{gpu_info}"
1187
- except:
1188
- return "✅ ENHANCED high-quality models loaded"
1189
- else:
1190
- return "⏳ Models not loaded. Click 'Load Models' for ENHANCED cinema-quality processing."
1191
-
1192
  def create_interface():
1193
  """Create enhanced Gradio interface with comprehensive features and 4-method background system"""
1194
 
@@ -1366,4 +486,230 @@ def switch_background_method(method):
1366
  padding: 12px 8px;
1367
  border: 1px solid #ddd;
1368
  border-radius: 6px;
1369
- text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ High-Quality Video Background Replacement - MAIN APPLICATION
4
  Upload video → Choose professional background → Replace with cinema quality
5
  Features: SAM2 + MatAnyone with multi-fallback loading, professional backgrounds,
6
  cinema-quality processing, lazy loading, and enhanced stability
 
26
  from typing import Optional, Tuple, Dict, Any
27
  import logging
28
 
29
+ # Import all utilities
30
+ from utilities import *
31
 
32
  # Fix OpenMP threads issue - remove problematic environment variable
33
  try:
 
73
  models_loaded = False
74
  loading_lock = threading.Lock()
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def process_video_hq(video_path, background_choice, custom_background_path, progress=gr.Progress()):
77
  """TWO-STAGE High-quality video processing: Original → Green Screen → Final Background"""
78
  if not models_loaded:
 
309
  logger.error(f"Video processing error: {traceback.format_exc()}")
310
  return None, error_msg
311
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  def create_interface():
313
  """Create enhanced Gradio interface with comprehensive features and 4-method background system"""
314
 
 
486
  padding: 12px 8px;
487
  border: 1px solid #ddd;
488
  border-radius: 6px;
489
+ text-align: center;
490
+ background: {gradient};
491
+ min-height: 60px;
492
+ display: flex;
493
+ align-items: center;
494
+ justify-content: center;
495
+ '>
496
+ <div>
497
+ <strong style='color: white; text-shadow: 1px 1px 2px rgba(0,0,0,0.8); font-size: 12px; display: block;'>{config["name"]}</strong>
498
+ <small style='color: rgba(255,255,255,0.9); text-shadow: 1px 1px 1px rgba(0,0,0,0.6); font-size: 10px;'>{config.get("description", "")[:30]}...</small>
499
+ </div>
500
+ </div>
501
+ """
502
+
503
+ bg_preview_html += "</div>"
504
+ gr.HTML(bg_preview_html)
505
+
506
+ # AI Background Generation Function
507
+ def generate_ai_background(prompt, style):
508
+ """Generate AI background using procedural methods"""
509
+ if not prompt or not prompt.strip():
510
+ return None, "❌ Please enter a prompt"
511
+
512
+ try:
513
+ # Create procedural background based on prompt
514
+ bg_image = create_procedural_background(prompt, style, 1920, 1080)
515
+
516
+ if bg_image is not None:
517
+ # Save generated image
518
+ import tempfile
519
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
520
+ cv2.imwrite(tmp.name, bg_image)
521
+ return tmp.name, f"✅ Background generated: {prompt[:50]}..."
522
+ else:
523
+ return None, "❌ Generation failed, try different prompt"
524
+ except Exception as e:
525
+ logger.error(f"AI generation error: {e}")
526
+ return None, f"❌ Generation error: {str(e)}"
527
+
528
+ # Enhanced video processing function that handles all 4 methods
529
+ def process_video_enhanced(video_path, bg_method, custom_img, prof_choice, grad_type,
530
+ color1, color2, color3, use_third, ai_prompt, ai_style, ai_img,
531
+ progress=gr.Progress()):
532
+ """Process video with any of the 4 background methods using TWO-STAGE approach"""
533
+
534
+ if not models_loaded:
535
+ return None, "❌ Models not loaded. Click 'Load Models' first."
536
+
537
+ if not video_path:
538
+ return None, "❌ No video file provided."
539
+
540
+ try:
541
+ progress(0, desc="🎬 Preparing background...")
542
+
543
+ # Determine which background to use based on method
544
+ if bg_method == "upload":
545
+ if custom_img and os.path.exists(custom_img):
546
+ return process_video_hq(video_path, "custom", custom_img, progress)
547
+ else:
548
+ return None, "❌ No image uploaded. Please upload a background image."
549
+
550
+ elif bg_method == "professional":
551
+ if prof_choice and prof_choice in PROFESSIONAL_BACKGROUNDS:
552
+ return process_video_hq(video_path, prof_choice, None, progress)
553
+ else:
554
+ return None, f"❌ Invalid professional background: {prof_choice}"
555
+
556
+ elif bg_method == "colors":
557
+ # Create custom gradient as temporary image
558
+ try:
559
+ colors = [color1 or "#3498db", color2 or "#2ecc71"]
560
+ if use_third and color3:
561
+ colors.append(color3)
562
+
563
+ bg_config = {
564
+ "type": "gradient" if grad_type != "solid" else "color",
565
+ "colors": colors,
566
+ "direction": grad_type if grad_type != "solid" else "vertical"
567
+ }
568
+
569
+ if grad_type == "solid":
570
+ bg_config["colors"] = [colors[0]]
571
+
572
+ # Create temporary image for gradient
573
+ gradient_bg = create_professional_background(bg_config, 1920, 1080)
574
+ temp_path = f"/tmp/gradient_{int(time.time())}.png"
575
+ cv2.imwrite(temp_path, gradient_bg)
576
+
577
+ return process_video_hq(video_path, "custom", temp_path, progress)
578
+ except Exception as e:
579
+ return None, f"❌ Error creating gradient: {str(e)}"
580
+
581
+ elif bg_method == "ai":
582
+ if ai_img and os.path.exists(ai_img):
583
+ return process_video_hq(video_path, "custom", ai_img, progress)
584
+ else:
585
+ return None, "❌ No AI background generated. Click 'Generate Background' first."
586
+
587
+ else:
588
+ return None, f"❌ Unknown background method: {bg_method}"
589
+
590
+ except Exception as e:
591
+ logger.error(f"Enhanced processing error: {e}")
592
+ return None, f"❌ Processing error: {str(e)}"
593
+
594
+ # Connect all the functions
595
+ load_models_btn.click(
596
+ fn=download_and_setup_models,
597
+ outputs=status_text
598
+ )
599
+
600
+ generate_ai_btn.click(
601
+ fn=generate_ai_background,
602
+ inputs=[ai_prompt, ai_style],
603
+ outputs=[ai_generated_image, status_text]
604
+ )
605
+
606
+ process_btn.click(
607
+ fn=process_video_enhanced,
608
+ inputs=[
609
+ video_input, # video_path
610
+ background_method, # bg_method
611
+ custom_background, # custom_img
612
+ professional_choice, # prof_choice
613
+ gradient_type, # grad_type
614
+ color1, color2, color3, use_third_color, # colors
615
+ ai_prompt, ai_style, ai_generated_image # AI
616
+ ],
617
+ outputs=[video_output, result_text]
618
+ )
619
+
620
+ # Comprehensive info section
621
+ with gr.Accordion("ℹ️ ENHANCED Quality & Features", open=False):
622
+ gr.Markdown("""
623
+ ### 🏆 TWO-STAGE Cinema-Quality Features:
624
+
625
+ **🎬 Two-Stage Processing:**
626
+ - **Stage 1**: Original Video → Green Screen Video (SAM2 + MatAnyone segmentation)
627
+ - **Stage 2**: Green Screen Video → Final Background (Professional chroma key replacement)
628
+ - **Why Two-Stage?**: Better edge quality, cleaner separation, professional results
629
+
630
+ **🤖 Advanced AI Models:**
631
+ - **SAM2**: State-of-the-art segmentation (Large/Tiny auto-selection)
632
+ - **MatAnyone**: CVPR 2025 professional matting technology
633
+ - **Multi-Fallback Loading**: 4+ methods each for maximum reliability
634
+ - **OpenCV Fallbacks**: Enhanced backup systems for compatibility
635
+
636
+ **🎨 4 Background Methods:**
637
+ - **A) Upload Image**: Use any custom image as background
638
+ - **B) Professional Presets**: 15+ high-quality professional backgrounds
639
+ - **C) Colors/Gradients**: Custom color combinations with 6 gradient types
640
+ - **D) AI Generated**: Procedural backgrounds from text prompts
641
+
642
+ **🎬 Professional Quality:**
643
+ - **✨ Edge Feathering**: Smooth, natural transitions
644
+ - **🎬 Gamma Correction**: Professional color compositing
645
+ - **🔍 Multi-Point Segmentation**: 7-point strategic person detection
646
+ - **🧹 Morphological Processing**: Advanced mask cleanup
647
+ - **🟢 Green Screen Intermediate**: Professional chroma key workflow
648
+
649
+ **🎵 Audio & Video:**
650
+ - **High-Quality Audio**: 192kbps AAC preservation
651
+ - **📺 H.264 Codec**: CRF 18 for broadcast quality
652
+ - **🎞️ Frame Processing**: Advanced error handling
653
+ - **💾 Smart Caching**: Optimized memory management
654
+
655
+ ### 💡 Usage Tips:
656
+ - Upload videos in common formats (MP4, MOV, AVI)
657
+ - For best results, ensure good lighting in original video
658
+ - Custom backgrounds work best with high resolution images
659
+ - AI prompts: Try "modern office", "sunset mountain", "abstract tech"
660
+ - GPU processing is faster but CPU fallback always available
661
+ - Two-stage processing gives cinema-quality results
662
+ """)
663
+
664
+ # Footer
665
+ gr.Markdown("---")
666
+ gr.Markdown(
667
+ "*🎬 Cinema-Quality Video Background Replacement - "
668
+ "Enhanced with TWO-STAGE processing and 4-method background system*"
669
+ )
670
+
671
+ return demo
672
+
673
+ def main():
674
+ """Main application entry point"""
675
+ try:
676
+ print("🎬 Cinema-Quality Video Background Replacement")
677
+ print("=" * 50)
678
+
679
+ # Initialize application
680
+ os.makedirs("/tmp/MyAvatar/My_Videos/", exist_ok=True)
681
+ os.makedirs(os.path.expanduser("~/.cache/sam2"), exist_ok=True)
682
+
683
+ print("🚀 Features:")
684
+ print(" • SAM2 + MatAnyone AI models")
685
+ print(" • TWO-STAGE processing (Original → Green Screen → Final)")
686
+ print(" • 4 background methods (Upload/Professional/Colors/AI)")
687
+ print(" • Multi-fallback loading system")
688
+ print(" • Cinema-quality processing")
689
+ print(" • Enhanced stability & error handling")
690
+ print("=" * 50)
691
+
692
+ # Create and launch interface
693
+ logger.info("🌐 Creating Gradio interface...")
694
+ demo = create_interface()
695
+
696
+ logger.info("🚀 Launching application...")
697
+
698
+ demo.launch(
699
+ server_name="0.0.0.0",
700
+ server_port=7860,
701
+ share=True,
702
+ show_error=True
703
+ )
704
+
705
+ except KeyboardInterrupt:
706
+ logger.info("🛑 Application stopped by user")
707
+ print("\n🛑 Application stopped by user")
708
+ except Exception as e:
709
+ logger.error(f"❌ Application failed to start: {e}")
710
+ logger.error(f"Full traceback: {traceback.format_exc()}")
711
+ print(f"❌ Application failed to start: {e}")
712
+ print("Check logs for detailed error information.")
713
+
714
+ if __name__ == "__main__":
715
+ main()