MogensR commited on
Commit
182f38b
Β·
1 Parent(s): 9b05094

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +579 -50
Dockerfile CHANGED
@@ -1,50 +1,579 @@
1
- # BackgroundFX Pro v2.1.0 - Optimized Dockerfile for HuggingFace Spaces
2
- FROM python:3.10-slim
3
-
4
- # Set environment variables for optimization
5
- ENV PYTHONUNBUFFERED=1 \
6
- PYTHONDONTWRITEBYTECODE=1 \
7
- PIP_NO_CACHE_DIR=1 \
8
- PIP_DISABLE_PIP_VERSION_CHECK=1 \
9
- DEBIAN_FRONTEND=noninteractive \
10
- PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512 \
11
- CUDA_LAUNCH_BLOCKING=0 \
12
- OMP_NUM_THREADS=4
13
-
14
- # Install system dependencies
15
- RUN apt-get update && apt-get install -y \
16
- ffmpeg \
17
- git \
18
- wget \
19
- curl \
20
- build-essential \
21
- && rm -rf /var/lib/apt/lists/*
22
-
23
- # Set working directory
24
- WORKDIR /app
25
-
26
- # Copy requirements first for better caching
27
- COPY requirements.txt .
28
-
29
- # Install Python dependencies
30
- RUN pip install --no-cache-dir -r requirements.txt
31
-
32
- # Copy application files
33
- COPY app.py .
34
- COPY startup.sh .
35
-
36
- # Make startup script executable
37
- RUN chmod +x startup.sh
38
-
39
- # Create necessary directories
40
- RUN mkdir -p /tmp/backgroundfx_pro/{uploads,outputs,temp,models}
41
-
42
- # Expose port
43
- EXPOSE 7860
44
-
45
- # Health check
46
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \
47
- CMD curl -f http://localhost:7860/health || exit 1
48
-
49
- # Use startup script
50
- CMD ["./startup.sh"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ High-Quality Video Background Replacement
4
+ Upload video β†’ Choose professional background β†’ Replace with cinema quality
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import tempfile
10
+ import cv2
11
+ import numpy as np
12
+ from pathlib import Path
13
+ import gradio as gr
14
+ import torch
15
+ import requests
16
+ from PIL import Image, ImageDraw
17
+ import json
18
+
19
+ # Suppress warnings and optimize for quality
20
+ import warnings
21
+ warnings.filterwarnings("ignore")
22
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:1024'
23
+ os.environ['CUDA_LAUNCH_BLOCKING'] = '0'
24
+
25
+ # Global variables for models
26
+ sam2_predictor = None
27
+ matanyone_model = None
28
+ models_loaded = False
29
+
30
+ # Professional background templates
31
+ PROFESSIONAL_BACKGROUNDS = {
32
+ "office_modern": {
33
+ "name": "Modern Office",
34
+ "type": "gradient",
35
+ "colors": ["#f8f9fa", "#e9ecef", "#dee2e6"],
36
+ "direction": "diagonal"
37
+ },
38
+ "office_executive": {
39
+ "name": "Executive Office",
40
+ "type": "gradient",
41
+ "colors": ["#2c3e50", "#34495e", "#5d6d7e"],
42
+ "direction": "vertical"
43
+ },
44
+ "studio_blue": {
45
+ "name": "Professional Blue",
46
+ "type": "gradient",
47
+ "colors": ["#1e3c72", "#2a5298", "#3498db"],
48
+ "direction": "radial"
49
+ },
50
+ "studio_green": {
51
+ "name": "Broadcast Green",
52
+ "type": "color",
53
+ "colors": ["#00b894"],
54
+ "chroma_key": True
55
+ },
56
+ "conference": {
57
+ "name": "Conference Room",
58
+ "type": "gradient",
59
+ "colors": ["#74b9ff", "#0984e3", "#6c5ce7"],
60
+ "direction": "horizontal"
61
+ },
62
+ "minimalist": {
63
+ "name": "Minimalist White",
64
+ "type": "gradient",
65
+ "colors": ["#ffffff", "#f1f2f6", "#ddd"],
66
+ "direction": "soft_radial"
67
+ },
68
+ "warm_gradient": {
69
+ "name": "Warm Sunset",
70
+ "type": "gradient",
71
+ "colors": ["#ff7675", "#fd79a8", "#fdcb6e"],
72
+ "direction": "diagonal"
73
+ },
74
+ "cool_gradient": {
75
+ "name": "Cool Ocean",
76
+ "type": "gradient",
77
+ "colors": ["#74b9ff", "#0984e3", "#00cec9"],
78
+ "direction": "vertical"
79
+ },
80
+ "corporate": {
81
+ "name": "Corporate Navy",
82
+ "type": "gradient",
83
+ "colors": ["#2d3436", "#636e72", "#74b9ff"],
84
+ "direction": "radial"
85
+ },
86
+ "creative": {
87
+ "name": "Creative Purple",
88
+ "type": "gradient",
89
+ "colors": ["#6c5ce7", "#a29bfe", "#fd79a8"],
90
+ "direction": "diagonal"
91
+ }
92
+ }
93
+
94
+ def download_and_setup_models():
95
+ """Download and setup SAM2 and MatAnyone models with quality optimizations"""
96
+ global sam2_predictor, matanyone_model, models_loaded
97
+
98
+ if models_loaded:
99
+ return "βœ… High-quality models already loaded"
100
+
101
+ try:
102
+ # Download SAM2 if needed
103
+ sam2_checkpoint = "/tmp/sam2_hiera_large.pt"
104
+ if not os.path.exists(sam2_checkpoint):
105
+ print("πŸ“₯ Downloading SAM2 large model for maximum quality...")
106
+ url = "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt"
107
+ response = requests.get(url, stream=True)
108
+ total_size = int(response.headers.get('content-length', 0))
109
+ downloaded = 0
110
+
111
+ with open(sam2_checkpoint, 'wb') as f:
112
+ for chunk in response.iter_content(chunk_size=8192):
113
+ f.write(chunk)
114
+ downloaded += len(chunk)
115
+ if total_size > 0:
116
+ percent = (downloaded / total_size) * 100
117
+ print(f"Download progress: {percent:.1f}%")
118
+
119
+ # Setup SAM2 with quality settings
120
+ sys.path.append('/tmp/segment-anything-2')
121
+ from sam2.build_sam import build_sam2
122
+ from sam2.sam2_image_predictor import SAM2ImagePredictor
123
+
124
+ device = "cuda" if torch.cuda.is_available() else "cpu"
125
+ print(f"πŸš€ Loading SAM2 on {device} for maximum quality...")
126
+
127
+ sam2_model = build_sam2("sam2_hiera_large.yaml", sam2_checkpoint, device=device)
128
+ sam2_predictor = SAM2ImagePredictor(sam2_model)
129
+
130
+ # Setup MatAnyone with quality optimizations
131
+ sys.path.append('/tmp/MatAnyone')
132
+ from inference import MatAnyoneInference
133
+
134
+ print("🎨 Loading MatAnyone for cinema-quality matting...")
135
+ matanyone_model = MatAnyoneInference()
136
+
137
+ models_loaded = True
138
+ gpu_info = f" (GPU: {torch.cuda.get_device_name(0)})" if torch.cuda.is_available() else " (CPU)"
139
+ return f"βœ… High-quality models loaded successfully!{gpu_info}"
140
+
141
+ except Exception as e:
142
+ return f"❌ Error loading models: {e}"
143
+
144
+ def segment_person_hq(image):
145
+ """High-quality person segmentation using SAM2"""
146
+ # Set image with quality optimizations
147
+ sam2_predictor.set_image(image)
148
+
149
+ h, w = image.shape[:2]
150
+
151
+ # Use multiple points for better segmentation
152
+ points = np.array([
153
+ [w//2, h//2], # Center
154
+ [w//2, h//3], # Upper body
155
+ [w//2, 2*h//3], # Lower body
156
+ [w//3, h//2], # Left side
157
+ [2*w//3, h//2], # Right side
158
+ ])
159
+ labels = np.array([1, 1, 1, 1, 1]) # All positive points
160
+
161
+ # Predict with high quality settings
162
+ masks, scores, _ = sam2_predictor.predict(
163
+ point_coords=points,
164
+ point_labels=labels,
165
+ multimask_output=True
166
+ )
167
+
168
+ # Select best mask and apply smoothing
169
+ best_mask = masks[np.argmax(scores)]
170
+
171
+ # Smooth mask edges for better quality
172
+ kernel = np.ones((3,3), np.uint8)
173
+ best_mask = cv2.morphologyEx(best_mask.astype(np.uint8), cv2.MORPH_CLOSE, kernel)
174
+ best_mask = cv2.GaussianBlur(best_mask.astype(np.float32), (3,3), 1.0)
175
+
176
+ return (best_mask * 255).astype(np.uint8)
177
+
178
+ def refine_mask_hq(image, mask):
179
+ """Cinema-quality mask refinement using MatAnyone"""
180
+ # Apply edge-preserving filtering before MatAnyone
181
+ image_filtered = cv2.bilateralFilter(image, 9, 75, 75)
182
+
183
+ # Use MatAnyone for professional matting
184
+ refined_mask = matanyone_model.infer(image_filtered, mask)
185
+
186
+ # Post-process for smooth edges
187
+ refined_mask = cv2.medianBlur(refined_mask, 3)
188
+
189
+ return refined_mask
190
+
191
+ def create_professional_background(bg_config, width, height):
192
+ """Create professional background based on configuration"""
193
+ if bg_config["type"] == "color":
194
+ # Solid color
195
+ color_hex = bg_config["colors"][0].lstrip('#')
196
+ color_rgb = tuple(int(color_hex[i:i+2], 16) for i in (0, 2, 4))
197
+ color_bgr = color_rgb[::-1] # Convert to BGR
198
+ background = np.full((height, width, 3), color_bgr, dtype=np.uint8)
199
+
200
+ elif bg_config["type"] == "gradient":
201
+ background = create_gradient_background(bg_config, width, height)
202
+
203
+ return background
204
+
205
+ def create_gradient_background(bg_config, width, height):
206
+ """Create high-quality gradient backgrounds"""
207
+ colors = bg_config["colors"]
208
+ direction = bg_config.get("direction", "vertical")
209
+
210
+ # Convert hex colors to RGB
211
+ rgb_colors = []
212
+ for color_hex in colors:
213
+ color_hex = color_hex.lstrip('#')
214
+ rgb = tuple(int(color_hex[i:i+2], 16) for i in (0, 2, 4))
215
+ rgb_colors.append(rgb)
216
+
217
+ # Create PIL image for high-quality gradients
218
+ pil_img = Image.new('RGB', (width, height))
219
+ draw = ImageDraw.Draw(pil_img)
220
+
221
+ if direction == "vertical":
222
+ # Vertical gradient
223
+ for y in range(height):
224
+ # Interpolate between colors
225
+ progress = y / height
226
+ if len(rgb_colors) == 2:
227
+ r = int(rgb_colors[0][0] + (rgb_colors[1][0] - rgb_colors[0][0]) * progress)
228
+ g = int(rgb_colors[0][1] + (rgb_colors[1][1] - rgb_colors[0][1]) * progress)
229
+ b = int(rgb_colors[0][2] + (rgb_colors[1][2] - rgb_colors[0][2]) * progress)
230
+ else:
231
+ # Multi-color gradient
232
+ segment = progress * (len(rgb_colors) - 1)
233
+ idx = int(segment)
234
+ local_progress = segment - idx
235
+
236
+ if idx >= len(rgb_colors) - 1:
237
+ r, g, b = rgb_colors[-1]
238
+ else:
239
+ c1, c2 = rgb_colors[idx], rgb_colors[idx + 1]
240
+ r = int(c1[0] + (c2[0] - c1[0]) * local_progress)
241
+ g = int(c1[1] + (c2[1] - c1[1]) * local_progress)
242
+ b = int(c1[2] + (c2[2] - c1[2]) * local_progress)
243
+
244
+ draw.line([(0, y), (width, y)], fill=(r, g, b))
245
+
246
+ elif direction == "horizontal":
247
+ # Horizontal gradient
248
+ for x in range(width):
249
+ progress = x / width
250
+ if len(rgb_colors) == 2:
251
+ r = int(rgb_colors[0][0] + (rgb_colors[1][0] - rgb_colors[0][0]) * progress)
252
+ g = int(rgb_colors[0][1] + (rgb_colors[1][1] - rgb_colors[0][1]) * progress)
253
+ b = int(rgb_colors[0][2] + (rgb_colors[1][2] - rgb_colors[0][2]) * progress)
254
+ else:
255
+ segment = progress * (len(rgb_colors) - 1)
256
+ idx = int(segment)
257
+ local_progress = segment - idx
258
+
259
+ if idx >= len(rgb_colors) - 1:
260
+ r, g, b = rgb_colors[-1]
261
+ else:
262
+ c1, c2 = rgb_colors[idx], rgb_colors[idx + 1]
263
+ r = int(c1[0] + (c2[0] - c1[0]) * local_progress)
264
+ g = int(c1[1] + (c2[1] - c1[1]) * local_progress)
265
+ b = int(c1[2] + (c2[2] - c1[2]) * local_progress)
266
+
267
+ draw.line([(x, 0), (x, height)], fill=(r, g, b))
268
+
269
+ elif direction == "diagonal":
270
+ # Diagonal gradient
271
+ for y in range(height):
272
+ for x in range(width):
273
+ progress = (x + y) / (width + height)
274
+ progress = min(1.0, progress)
275
+
276
+ if len(rgb_colors) == 2:
277
+ r = int(rgb_colors[0][0] + (rgb_colors[1][0] - rgb_colors[0][0]) * progress)
278
+ g = int(rgb_colors[0][1] + (rgb_colors[1][1] - rgb_colors[0][1]) * progress)
279
+ b = int(rgb_colors[0][2] + (rgb_colors[1][2] - rgb_colors[0][2]) * progress)
280
+ else:
281
+ segment = progress * (len(rgb_colors) - 1)
282
+ idx = int(segment)
283
+ local_progress = segment - idx
284
+
285
+ if idx >= len(rgb_colors) - 1:
286
+ r, g, b = rgb_colors[-1]
287
+ else:
288
+ c1, c2 = rgb_colors[idx], rgb_colors[idx + 1]
289
+ r = int(c1[0] + (c2[0] - c1[0]) * local_progress)
290
+ g = int(c1[1] + (c2[1] - c1[1]) * local_progress)
291
+ b = int(c1[2] + (c2[2] - c1[2]) * local_progress)
292
+
293
+ pil_img.putpixel((x, y), (r, g, b))
294
+
295
+ elif direction in ["radial", "soft_radial"]:
296
+ # Radial gradient
297
+ center_x, center_y = width // 2, height // 2
298
+ max_distance = np.sqrt(center_x**2 + center_y**2)
299
+
300
+ for y in range(height):
301
+ for x in range(width):
302
+ distance = np.sqrt((x - center_x)**2 + (y - center_y)**2)
303
+ progress = distance / max_distance
304
+ progress = min(1.0, progress)
305
+
306
+ if direction == "soft_radial":
307
+ progress = progress**0.7 # Softer falloff
308
+
309
+ if len(rgb_colors) == 2:
310
+ r = int(rgb_colors[0][0] + (rgb_colors[1][0] - rgb_colors[0][0]) * progress)
311
+ g = int(rgb_colors[0][1] + (rgb_colors[1][1] - rgb_colors[0][1]) * progress)
312
+ b = int(rgb_colors[0][2] + (rgb_colors[1][2] - rgb_colors[0][2]) * progress)
313
+ else:
314
+ segment = progress * (len(rgb_colors) - 1)
315
+ idx = int(segment)
316
+ local_progress = segment - idx
317
+
318
+ if idx >= len(rgb_colors) - 1:
319
+ r, g, b = rgb_colors[-1]
320
+ else:
321
+ c1, c2 = rgb_colors[idx], rgb_colors[idx + 1]
322
+ r = int(c1[0] + (c2[0] - c1[0]) * local_progress)
323
+ g = int(c1[1] + (c2[1] - c1[1]) * local_progress)
324
+ b = int(c1[2] + (c2[2] - c1[2]) * local_progress)
325
+
326
+ pil_img.putpixel((x, y), (r, g, b))
327
+
328
+ # Convert PIL to OpenCV format
329
+ background = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
330
+ return background
331
+
332
+ def replace_background_hq(frame, mask, background):
333
+ """High-quality background replacement with edge feathering"""
334
+ # Resize background to match frame exactly
335
+ background = cv2.resize(background, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_LANCZOS4)
336
+
337
+ # Apply edge feathering for smooth transitions
338
+ mask_float = mask.astype(np.float32) / 255.0
339
+
340
+ # Create feathered mask
341
+ feather_radius = 3
342
+ mask_feathered = cv2.GaussianBlur(mask_float, (feather_radius*2+1, feather_radius*2+1), feather_radius/3)
343
+
344
+ # Expand mask to 3 channels
345
+ mask_3channel = np.stack([mask_feathered] * 3, axis=2)
346
+
347
+ # High-quality compositing with gamma correction
348
+ frame_linear = np.power(frame.astype(np.float32) / 255.0, 2.2)
349
+ background_linear = np.power(background.astype(np.float32) / 255.0, 2.2)
350
+
351
+ # Composite in linear space
352
+ result_linear = frame_linear * mask_3channel + background_linear * (1 - mask_3channel)
353
+
354
+ # Convert back to sRGB
355
+ result = np.power(result_linear, 1/2.2) * 255.0
356
+ result = np.clip(result, 0, 255).astype(np.uint8)
357
+
358
+ return result
359
+
360
+ def process_video_hq(video_path, background_choice, custom_background_path, progress=gr.Progress()):
361
+ """High-quality video processing with professional backgrounds"""
362
+ if not models_loaded:
363
+ return None, "❌ Models not loaded. Click 'Load Models' first."
364
+
365
+ try:
366
+ progress(0, desc="🎬 Initializing high-quality processing...")
367
+
368
+ # Read video with quality settings
369
+ cap = cv2.VideoCapture(video_path)
370
+ fps = cap.get(cv2.CAP_PROP_FPS)
371
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
372
+ frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
373
+ frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
374
+
375
+ # Prepare background
376
+ if background_choice == "custom" and custom_background_path:
377
+ # Use uploaded image
378
+ background = cv2.imread(custom_background_path)
379
+ if background is None:
380
+ return None, "❌ Could not read custom background image"
381
+ background_name = "Custom Image"
382
+ else:
383
+ # Use professional background
384
+ if background_choice in PROFESSIONAL_BACKGROUNDS:
385
+ bg_config = PROFESSIONAL_BACKGROUNDS[background_choice]
386
+ background = create_professional_background(bg_config, frame_width, frame_height)
387
+ background_name = bg_config["name"]
388
+ else:
389
+ return None, "❌ Invalid background selection"
390
+
391
+ # Setup high-quality output video
392
+ output_path = "/tmp/processed_video_hq.mp4"
393
+ # Use high-quality codec
394
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
395
+ out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
396
+
397
+ progress(0.1, desc=f"🎨 Using {background_name} background...")
398
+
399
+ # Process each frame with quality optimizations
400
+ frame_count = 0
401
+ while True:
402
+ ret, frame = cap.read()
403
+ if not ret:
404
+ break
405
+
406
+ # Update progress
407
+ progress_pct = 0.1 + (frame_count / total_frames) * 0.8
408
+ progress(progress_pct, desc=f"✨ Processing frame {frame_count + 1}/{total_frames} (High Quality)")
409
+
410
+ # High-quality person segmentation
411
+ mask = segment_person_hq(frame)
412
+
413
+ # Cinema-quality mask refinement
414
+ refined_mask = refine_mask_hq(frame, mask)
415
+
416
+ # High-quality background replacement
417
+ result_frame = replace_background_hq(frame, refined_mask, background)
418
+
419
+ # Write frame
420
+ out.write(result_frame)
421
+ frame_count += 1
422
+
423
+ cap.release()
424
+ out.release()
425
+
426
+ progress(0.9, desc="🎡 Adding high-quality audio...")
427
+
428
+ # Add audio back with high quality settings
429
+ final_output = "/tmp/final_output_hq.mp4"
430
+ audio_cmd = f'ffmpeg -y -i {output_path} -i {video_path} -c:v libx264 -crf 18 -preset slow -c:a aac -b:a 192k -map 0:v:0 -map 1:a:0? -shortest {final_output}'
431
+ os.system(audio_cmd)
432
+
433
+ # Save to MyAvatar/My Videos
434
+ myavatar_path = "/tmp/MyAvatar/My_Videos/"
435
+ os.makedirs(myavatar_path, exist_ok=True)
436
+
437
+ import shutil
438
+ import time
439
+ saved_filename = f"hq_background_replaced_{int(time.time())}.mp4"
440
+ saved_path = os.path.join(myavatar_path, saved_filename)
441
+ shutil.copy2(final_output, saved_path)
442
+
443
+ progress(1.0, desc="βœ… High-quality processing complete!")
444
+
445
+ return final_output, f"βœ… High-Quality Success!\n🎬 Background: {background_name}\nπŸ“ Saved: MyAvatar/My Videos/{saved_filename}\n🎯 Quality: Cinema-grade with SAM2 + MatAnyone"
446
+
447
+ except Exception as e:
448
+ return None, f"❌ Error: {str(e)}"
449
+
450
+ def get_model_status():
451
+ """Get current model loading status"""
452
+ if models_loaded:
453
+ gpu_info = f" (GPU: {torch.cuda.get_device_name(0)})" if torch.cuda.is_available() else " (CPU)"
454
+ return f"βœ… High-quality models loaded{gpu_info}"
455
+ else:
456
+ return "⏳ Models not loaded. Click 'Load Models' for cinema-quality processing."
457
+
458
+ def create_interface():
459
+ """Create enhanced Gradio interface with professional backgrounds"""
460
+
461
+ # Create background choices
462
+ bg_choices = ["custom"] + list(PROFESSIONAL_BACKGROUNDS.keys())
463
+ bg_labels = ["πŸ“· Custom Image"] + [f"🎨 {config['name']}" for config in PROFESSIONAL_BACKGROUNDS.values()]
464
+ bg_dropdown_choices = list(zip(bg_labels, bg_choices))
465
+
466
+ with gr.Blocks(title="High-Quality Video Background Replacement", theme=gr.themes.Soft()) as demo:
467
+ gr.Markdown("# 🎬 Cinema-Quality Video Background Replacement")
468
+ gr.Markdown("**Professional background replacement using SAM2 + MatAnyone AI models**")
469
+
470
+ with gr.Row():
471
+ with gr.Column(scale=1):
472
+ gr.Markdown("### πŸ“₯ Input")
473
+ video_input = gr.Video(label="πŸŽ₯ Upload Video (MP4, MOV, AVI)")
474
+
475
+ gr.Markdown("### 🎨 Background Selection")
476
+ background_choice = gr.Dropdown(
477
+ choices=bg_dropdown_choices,
478
+ value="office_modern",
479
+ label="Choose Background Type",
480
+ info="Select professional background or upload custom image"
481
+ )
482
+
483
+ custom_background = gr.Image(
484
+ label="πŸ“· Custom Background Image",
485
+ type="filepath",
486
+ visible=False,
487
+ info="Upload your own background image (will be resized to match video)"
488
+ )
489
+
490
+ # Show/hide custom background based on selection
491
+ def toggle_custom_bg(choice):
492
+ return gr.update(visible=(choice == "custom"))
493
+
494
+ background_choice.change(
495
+ fn=toggle_custom_bg,
496
+ inputs=background_choice,
497
+ outputs=custom_background
498
+ )
499
+
500
+ with gr.Row():
501
+ load_models_btn = gr.Button("πŸš€ Load High-Quality Models", variant="secondary", size="lg")
502
+ process_btn = gr.Button("✨ Process with Cinema Quality", variant="primary", size="lg")
503
+
504
+ status_text = gr.Textbox(
505
+ label="πŸ”§ System Status",
506
+ value=get_model_status(),
507
+ interactive=False,
508
+ lines=2
509
+ )
510
+
511
+ with gr.Column(scale=1):
512
+ gr.Markdown("### πŸ“€ High-Quality Output")
513
+ video_output = gr.Video(label="🎬 Processed Video", height=400)
514
+ result_text = gr.Textbox(
515
+ label="πŸ“Š Processing Results",
516
+ interactive=False,
517
+ lines=4
518
+ )
519
+
520
+ gr.Markdown("### 🎨 Professional Backgrounds Available")
521
+ bg_preview_html = "<div style='display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; padding: 10px;'>"
522
+ for key, config in PROFESSIONAL_BACKGROUNDS.items():
523
+ colors_display = " β†’ ".join(config["colors"][:2])
524
+ bg_preview_html += f"""
525
+ <div style='padding: 8px; border: 1px solid #ddd; border-radius: 8px; text-align: center; background: linear-gradient(45deg, {config["colors"][0]}, {config["colors"][-1]});'>
526
+ <strong style='color: white; text-shadow: 1px 1px 2px rgba(0,0,0,0.7);'>{config["name"]}</strong>
527
+ </div>
528
+ """
529
+ bg_preview_html += "</div>"
530
+ gr.HTML(bg_preview_html)
531
+
532
+ # Event handlers
533
+ load_models_btn.click(
534
+ fn=download_and_setup_models,
535
+ outputs=status_text
536
+ )
537
+
538
+ process_btn.click(
539
+ fn=process_video_hq,
540
+ inputs=[video_input, background_choice, custom_background],
541
+ outputs=[video_output, result_text]
542
+ )
543
+
544
+ # Info section
545
+ with gr.Accordion("ℹ️ Quality & Features", open=False):
546
+ gr.Markdown("""
547
+ ### πŸ† Cinema-Quality Features:
548
+ - **πŸ€– SAM2 Large Model**: Meta's most advanced segmentation
549
+ - **🎨 MatAnyone**: CVPR 2025 professional matting
550
+ - **✨ Edge Feathering**: Smooth, natural transitions
551
+ - **🎬 Gamma Correction**: Professional color compositing
552
+ - **🎡 High-Quality Audio**: 192kbps AAC preservation
553
+ - **πŸ“Ί H.264 Codec**: CRF 18 for broadcast quality
554
+
555
+ ### 🎨 Professional Backgrounds:
556
+ - **Office Environments**: Modern, Executive styles
557
+ - **Studio Backdrops**: Broadcast-quality gradients
558
+ - **Creative Themes**: Artistic color combinations
559
+ - **Custom Images**: Upload your own backgrounds
560
+
561
+ ### πŸ’Ύ Output:
562
+ - Saved to: **MyAvatar/My Videos/**
563
+ - Format: **MP4 (H.264)**
564
+ - Quality: **Cinema-grade**
565
+ """)
566
+
567
+ return demo
568
+
569
+ if __name__ == "__main__":
570
+ print("🎬 Starting Cinema-Quality Video Background Replacement...")
571
+
572
+ # Create and launch interface
573
+ demo = create_interface()
574
+ demo.launch(
575
+ server_name="0.0.0.0",
576
+ server_port=7860,
577
+ share=True,
578
+ show_error=True
579
+ )