MogensR commited on
Commit
e6a1f2d
·
1 Parent(s): c8ba35a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -650
app.py CHANGED
@@ -1,183 +1,82 @@
1
- #!/usr/bin/env python3
2
- """
3
- BackgroundFX - Video Background Replacement with Green Screen Workflow
4
- Fixed for Hugging Face Space - Handles video preview issues
5
- FIXED: Video display issue by properly handling file stream
6
- Updated: 2025-08-13 - PROPER FIX: Removed restart loop but kept all advanced features
7
- IMPROVED: Added rembg as primary fallback when SAM2 unavailable
8
- """
9
-
10
- import streamlit as st
11
- import cv2
12
- import numpy as np
13
- import tempfile
14
- import os
15
- from PIL import Image
16
- import requests
17
- from io import BytesIO
18
- import logging
19
- import base64
20
-
21
- # Configure logging
22
- logging.basicConfig(level=logging.INFO)
23
- logger = logging.getLogger(__name__)
24
-
25
- # FIXED: Clean GPU setup without restart loop
26
- def setup_environment():
27
- """Setup environment variables without restart loop"""
28
- os.environ['OMP_NUM_THREADS'] = '4'
29
- os.environ['ORT_PROVIDERS'] = 'CUDAExecutionProvider,CPUExecutionProvider'
30
- os.environ['CUDA_VISIBLE_DEVICES'] = '0'
31
- os.environ['TORCH_CUDA_ARCH_LIST'] = '7.5'
32
-
33
- # Check GPU availability
34
- try:
35
- import torch
36
- if torch.cuda.is_available():
37
- gpu_name = torch.cuda.get_device_name(0)
38
- logger.info(f"🚀 GPU: {gpu_name}")
39
- torch.cuda.init()
40
- torch.cuda.set_device(0)
41
- dummy = torch.zeros(1).cuda()
42
- del dummy
43
- torch.cuda.empty_cache()
44
- return True, gpu_name
45
- else:
46
- logger.warning("⚠️ CUDA not available")
47
- return False, None
48
- except ImportError:
49
- logger.warning("⚠️ PyTorch not available")
50
- return False, None
51
-
52
- # Initialize environment (NO RESTART LOOP!)
53
- CUDA_AVAILABLE, GPU_NAME = setup_environment()
54
-
55
- # Try to import SAM2 and MatAnyone (PRESERVED FROM ORIGINAL)
56
- try:
57
- from sam2.build_sam import build_sam2_video_predictor
58
- from sam2.sam2_image_predictor import SAM2ImagePredictor
59
- SAM2_AVAILABLE = True
60
- logger.info("✅ SAM2 loaded successfully")
61
- except ImportError as e:
62
- SAM2_AVAILABLE = False
63
- logger.warning(f"⚠️ SAM2 not available: {e}")
64
 
65
- try:
66
- import matanyone
67
- MATANYONE_AVAILABLE = True
68
- logger.info("✅ MatAnyone loaded successfully")
69
- except ImportError as e:
70
- MATANYONE_AVAILABLE = False
71
- logger.warning(f"⚠️ MatAnyone not available: {e}")
72
-
73
- # Import rembg with proper error handling (NO RESTART!)
74
  try:
75
  from rembg import remove, new_session
 
 
76
  REMBG_AVAILABLE = True
77
  logger.info("✅ Rembg loaded")
78
- # Initialize rembg session
79
- rembg_session = new_session('u2net_human_seg')
80
- except ImportError:
81
- REMBG_AVAILABLE = False
82
- rembg_session = None
83
- logger.warning("⚠️ Rembg not available")
84
-
85
- # Import advanced matting libraries (PRESERVED)
86
- try:
87
- import pymatting
88
- PYMATTING_AVAILABLE = True
89
- logger.info("✅ PyMatting loaded for advanced matting")
90
- except ImportError:
91
- PYMATTING_AVAILABLE = False
92
- logger.info("ℹ️ PyMatting not available")
93
-
94
- # PRESERVED: All original functions
95
- def load_background_image(background_url):
96
- """Load background image from URL"""
97
- try:
98
- if background_url == "default_brick":
99
- return create_default_background()
100
-
101
- response = requests.get(background_url)
102
- response.raise_for_status()
103
- image = Image.open(BytesIO(response.content))
104
- return np.array(image.convert('RGB'))
105
- except Exception as e:
106
- logger.error(f"Failed to load background image: {e}")
107
- # Return default brick wall background
108
- return create_default_background()
109
-
110
- def create_default_background():
111
- """Create a default brick wall background"""
112
- # Create a simple brick pattern
113
- background = np.zeros((720, 1280, 3), dtype=np.uint8)
114
- background[:, :] = [139, 69, 19] # Brown color
115
 
116
- # Add brick pattern
117
- for y in range(0, 720, 60):
118
- for x in range(0, 1280, 120):
119
- cv2.rectangle(background, (x, y), (x+115, y+55), (160, 82, 45), -1)
120
- cv2.rectangle(background, (x, y), (x+115, y+55), (101, 67, 33), 2)
121
-
122
- return background
123
-
124
- def check_premium_access():
125
- """Check if user has premium access - placeholder for now"""
126
- # This would integrate with your authentication system
127
- return True # For demo purposes
128
-
129
- def get_professional_backgrounds():
130
- """Get professional background collection for premium users"""
131
- return {
132
- "🏢 Modern Office": "https://images.unsplash.com/photo-1497366216548-37526070297c?w=1920&h=1080&fit=crop",
133
- "🌆 City Skyline": "https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=1920&h=1080&fit=crop",
134
- "🏖️ Tropical Beach": "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=1920&h=1080&fit=crop",
135
- "🌲 Forest Path": "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=1920&h=1080&fit=crop",
136
- "🎨 Abstract Blue": "https://images.unsplash.com/photo-1557683316-973673baf926?w=1920&h=1080&fit=crop",
137
- "🏔️ Mountain View": "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920&h=1080&fit=crop",
138
- "🌅 Sunset Gradient": "https://images.unsplash.com/photo-1495616811223-4d98c6e9c869?w=1920&h=1080&fit=crop",
139
- "💼 Executive Suite": "https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=1920&h=1080&fit=crop"
140
- }
141
-
142
- def get_basic_backgrounds():
143
- """Get basic background collection for free users"""
144
- return {
145
- "🧱 Brick Wall": "default_brick",
146
- "🌫️ Soft Blur": "https://images.unsplash.com/photo-1557683316-973673baf926?w=1920&h=1080&fit=crop&blur=20",
147
- "🌊 Ocean Blue": "https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=1920&h=1080&fit=crop",
148
- "🌿 Nature Green": "https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=1920&h=1080&fit=crop"
149
- }
150
 
151
- # IMPROVED: Enhanced segmentation functions with rembg
152
- def segment_person_sam2(frame):
153
- """Segment person using SAM2 - advanced method"""
154
- try:
155
- if SAM2_AVAILABLE:
156
- # SAM2 implementation would go here
157
- # For now, return None to fall back to other methods
158
- logger.debug("SAM2 segmentation attempted")
159
- return None
160
- except Exception as e:
161
- logger.error(f"SAM2 segmentation failed: {e}")
162
- return None
163
-
164
- def segment_person_rembg(frame):
165
- """Segment person using rembg - high quality fallback"""
 
 
166
  try:
167
  if REMBG_AVAILABLE and rembg_session:
168
  # Convert frame to PIL Image
169
  pil_image = Image.fromarray(frame)
170
 
171
- # Remove background using rembg
172
- output = remove(pil_image, session=rembg_session, alpha_matting=True)
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
  # Extract alpha channel as mask
175
  output_array = np.array(output)
176
  if output_array.shape[2] == 4:
177
- mask = output_array[:, :, 3].astype(float) / 255.0
178
  else:
179
- # If no alpha channel, create mask from difference
180
- mask = np.ones((frame.shape[0], frame.shape[1]))
181
 
182
  return mask
183
  return None
@@ -185,493 +84,45 @@ def segment_person_rembg(frame):
185
  logger.error(f"Rembg segmentation failed: {e}")
186
  return None
187
 
188
- def segment_person_fallback(frame):
189
- """Fallback person segmentation using color-based method"""
190
- try:
191
- # First try rembg if available
192
- if REMBG_AVAILABLE:
193
- mask = segment_person_rembg(frame)
194
- if mask is not None:
195
- return mask
196
-
197
- # Otherwise use simple skin color detection as fallback
198
- hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
199
-
200
- # Define skin color range
201
- lower_skin = np.array([0, 20, 70])
202
- upper_skin = np.array([20, 255, 255])
203
-
204
- mask = cv2.inRange(hsv, lower_skin, upper_skin)
205
-
206
- # Clean up the mask
207
- kernel = np.ones((5, 5), np.uint8)
208
- mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
209
- mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
210
-
211
- # Convert to 0-1 range
212
- return mask.astype(float) / 255
213
-
214
- except Exception as e:
215
- logger.error(f"Fallback segmentation failed: {e}")
216
- return None
217
-
218
- def insert_green_screen(frame, person_mask):
219
- """Insert green screen behind person"""
220
- try:
221
- # Create green background
222
- green_bg = np.zeros_like(frame)
223
- green_bg[:, :] = [0, 255, 0] # Pure green
224
-
225
- # Ensure mask is the right shape
226
- if person_mask.ndim == 2:
227
- person_mask = np.expand_dims(person_mask, axis=2)
228
-
229
- # Composite person on green background
230
- result = frame * person_mask + green_bg * (1 - person_mask)
231
- return result.astype(np.uint8)
232
-
233
- except Exception as e:
234
- logger.error(f"Green screen insertion failed: {e}")
235
- return frame
236
-
237
- def chroma_key_replacement(green_screen_frame, background_image):
238
- """Replace green screen with background using chroma key"""
239
- try:
240
- # Resize background to match frame
241
- h, w = green_screen_frame.shape[:2]
242
- background_resized = cv2.resize(background_image, (w, h))
243
-
244
- # Convert to HSV for better green detection
245
- hsv = cv2.cvtColor(green_screen_frame, cv2.COLOR_RGB2HSV)
246
-
247
- # Define green color range for chroma key
248
- lower_green = np.array([40, 50, 50])
249
- upper_green = np.array([80, 255, 255])
250
-
251
- # Create mask for green pixels
252
- green_mask = cv2.inRange(hsv, lower_green, upper_green)
253
-
254
- # Smooth the mask
255
- kernel = np.ones((3, 3), np.uint8)
256
- green_mask = cv2.morphologyEx(green_mask, cv2.MORPH_CLOSE, kernel)
257
- green_mask = cv2.GaussianBlur(green_mask, (5, 5), 0)
258
-
259
- # Normalize mask to 0-1 range
260
- mask_normalized = green_mask.astype(float) / 255
261
-
262
- # Apply chroma key replacement
263
- result = green_screen_frame.copy()
264
- for c in range(3):
265
- result[:, :, c] = (green_screen_frame[:, :, c] * (1 - mask_normalized) +
266
- background_resized[:, :, c] * mask_normalized)
267
-
268
- return result.astype(np.uint8)
269
-
270
- except Exception as e:
271
- logger.error(f"Chroma key replacement failed: {e}")
272
- return green_screen_frame
273
-
274
- # PRESERVED: Video processing with all features
275
- def process_video_with_green_screen(video_path, background_url, progress_callback=None):
276
- """Process video with proper green screen workflow"""
277
- try:
278
- # Load background image
279
- background_image = load_background_image(background_url)
280
-
281
- # Open video
282
- cap = cv2.VideoCapture(video_path)
283
-
284
- # Get video properties
285
- fps = int(cap.get(cv2.CAP_PROP_FPS))
286
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
287
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
288
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
289
-
290
- # Create output video writer
291
- output_path = tempfile.mktemp(suffix='.mp4')
292
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
293
- out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
294
-
295
- frame_count = 0
296
-
297
- while True:
298
- ret, frame = cap.read()
299
- if not ret:
300
- break
301
-
302
- # Convert BGR to RGB
303
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
304
-
305
- # Step 1: Segment person - try methods in order
306
- person_mask = None
307
- method_used = "None"
308
-
309
- # Try SAM2 first
310
- if SAM2_AVAILABLE:
311
- person_mask = segment_person_sam2(frame_rgb)
312
- if person_mask is not None:
313
- method_used = "SAM2"
314
-
315
- # Try rembg if SAM2 didn't work
316
- if person_mask is None and REMBG_AVAILABLE:
317
- person_mask = segment_person_rembg(frame_rgb)
318
- if person_mask is not None:
319
- method_used = "Rembg"
320
-
321
- # Fall back to color-based method
322
- if person_mask is None:
323
- person_mask = segment_person_fallback(frame_rgb)
324
- if person_mask is not None:
325
- method_used = "Color-based"
326
-
327
- if person_mask is not None:
328
- # Step 2: Insert green screen
329
- green_screen_frame = insert_green_screen(frame_rgb, person_mask)
330
-
331
- # Step 3: Chroma key replacement
332
- final_frame = chroma_key_replacement(green_screen_frame, background_image)
333
- else:
334
- # If segmentation fails, use original frame
335
- final_frame = frame_rgb
336
- method_used = "No segmentation"
337
-
338
- # Convert back to BGR for video writer
339
- final_frame_bgr = cv2.cvtColor(final_frame, cv2.COLOR_RGB2BGR)
340
- out.write(final_frame_bgr)
341
-
342
- frame_count += 1
343
-
344
- # Update progress
345
- if progress_callback:
346
- progress = frame_count / total_frames
347
- progress_callback(progress, f"Processing frame {frame_count}/{total_frames} ({method_used})")
348
-
349
- # Release resources
350
- cap.release()
351
- out.release()
352
-
353
- return output_path
354
-
355
- except Exception as e:
356
- logger.error(f"Video processing failed: {e}")
357
- return None
358
-
359
- def process_video_with_custom_background(video_path, background_array, progress_callback=None):
360
- """Process video with custom background array"""
361
- try:
362
- # Open video
363
- cap = cv2.VideoCapture(video_path)
364
-
365
- # Get video properties
366
- fps = int(cap.get(cv2.CAP_PROP_FPS))
367
- width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
368
- height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
369
- total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
370
-
371
- # Resize background to match video
372
- background_resized = cv2.resize(background_array, (width, height))
373
-
374
- # Create output video writer
375
- output_path = tempfile.mktemp(suffix='.mp4')
376
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
377
- out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
378
-
379
- frame_count = 0
380
-
381
- while True:
382
- ret, frame = cap.read()
383
- if not ret:
384
- break
385
-
386
- # Convert BGR to RGB
387
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
388
-
389
- # Direct background replacement using rembg if available
390
- if REMBG_AVAILABLE:
391
- pil_image = Image.fromarray(frame_rgb)
392
- output = remove(pil_image, session=rembg_session)
393
- output_array = np.array(output)
394
-
395
- if output_array.shape[2] == 4:
396
- # Use alpha channel for compositing
397
- alpha = output_array[:, :, 3:4] / 255.0
398
- person = output_array[:, :, :3]
399
- final_frame = person * alpha + background_resized * (1 - alpha)
400
- final_frame = final_frame.astype(np.uint8)
401
- else:
402
- final_frame = output_array
403
- else:
404
- # Use green screen workflow
405
- person_mask = segment_person_fallback(frame_rgb)
406
- if person_mask is not None:
407
- green_screen_frame = insert_green_screen(frame_rgb, person_mask)
408
- final_frame = chroma_key_replacement(green_screen_frame, background_resized)
409
- else:
410
- final_frame = frame_rgb
411
-
412
- # Convert back to BGR for video writer
413
- final_frame_bgr = cv2.cvtColor(final_frame, cv2.COLOR_RGB2BGR)
414
- out.write(final_frame_bgr)
415
-
416
- frame_count += 1
417
-
418
- # Update progress
419
- if progress_callback:
420
- progress = frame_count / total_frames
421
- progress_callback(progress, f"Processing frame {frame_count}/{total_frames}")
422
-
423
- # Release resources
424
- cap.release()
425
- out.release()
426
-
427
- return output_path
428
-
429
- except Exception as e:
430
- logger.error(f"Custom background video processing failed: {e}")
431
- return None
432
-
433
- # PRESERVED: Streamlit UI with all features
434
- def main():
435
- st.set_page_config(
436
- page_title="BackgroundFX - Professional",
437
- page_icon="🎬",
438
- layout="wide",
439
- initial_sidebar_state="expanded"
440
- )
441
-
442
- st.title("🎬 BackgroundFX - Professional Video Background Replacement")
443
- st.markdown("**Advanced AI-powered background replacement with green screen workflow**")
444
-
445
- # Show system status
446
- col1, col2, col3, col4 = st.columns(4)
447
-
448
- with col1:
449
- if CUDA_AVAILABLE:
450
- st.success(f"✅ GPU: {GPU_NAME}")
451
- else:
452
- st.warning("⚠️ CPU Mode")
453
-
454
- with col2:
455
- if SAM2_AVAILABLE:
456
- st.success("✅ SAM2 Ready")
457
- elif REMBG_AVAILABLE:
458
- st.success("✅ Rembg Ready")
459
- else:
460
- st.info("ℹ️ Loading...")
461
 
462
- with col3:
463
- if MATANYONE_AVAILABLE:
464
- st.success("✅ MatAnyone")
465
- else:
466
- st.info("ℹ️ MatAnyone Loading...")
467
-
468
- with col4:
469
- if PYMATTING_AVAILABLE:
470
- st.success("✅ PyMatting")
471
- else:
472
- st.info("ℹ️ Basic Matting")
473
-
474
- # Sidebar with method selection
475
- with st.sidebar:
476
- st.markdown("### 🛠️ Available Methods")
477
- methods = []
478
-
479
- if SAM2_AVAILABLE:
480
- methods.append("✅ SAM2 (AI Segmentation)")
481
- if REMBG_AVAILABLE:
482
- methods.append("✅ Rembg (High Quality)")
483
- if MATANYONE_AVAILABLE:
484
- methods.append("✅ MatAnyone (Virtual Try-On)")
485
- if PYMATTING_AVAILABLE:
486
- methods.append("✅ PyMatting (Advanced Edges)")
487
-
488
- methods.append("✅ Green Screen Workflow")
489
- methods.append("✅ Color-based (Fallback)")
490
-
491
- for method in methods:
492
- st.markdown(method)
493
-
494
- st.markdown("---")
495
- st.markdown("### 📊 Processing Stats")
496
- if 'frames_processed' not in st.session_state:
497
- st.session_state.frames_processed = 0
498
- st.metric("Frames Processed", st.session_state.frames_processed)
499
-
500
- # Main content
501
- col1, col2 = st.columns(2)
502
-
503
- # Initialize session state for video persistence
504
- if 'video_path' not in st.session_state:
505
- st.session_state.video_path = None
506
- if 'video_bytes' not in st.session_state:
507
- st.session_state.video_bytes = None
508
- if 'video_name' not in st.session_state:
509
- st.session_state.video_name = None
510
-
511
- with col1:
512
- st.markdown("### 📹 Upload Video")
513
- uploaded_video = st.file_uploader(
514
- "Choose a video file",
515
- type=['mp4', 'avi', 'mov', 'mkv'],
516
- help="Upload the video you want to process"
517
- )
518
-
519
- if uploaded_video:
520
- # Check if this is a new video upload
521
- if st.session_state.video_name != uploaded_video.name:
522
- # Display video info
523
- st.success(f"✅ Video uploaded: {uploaded_video.name}")
524
-
525
- # Read video data once and store it
526
- video_bytes = uploaded_video.read()
527
-
528
- # Save uploaded video to persistent temp file
529
- with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as tmp_file:
530
- tmp_file.write(video_bytes)
531
- video_path = tmp_file.name
532
-
533
- # Store in session state for persistence
534
- st.session_state.video_path = video_path
535
- st.session_state.video_bytes = video_bytes
536
- st.session_state.video_name = uploaded_video.name
537
-
538
- # Show video preview using stored bytes
539
- if st.session_state.video_bytes is not None:
540
- st.video(st.session_state.video_bytes)
541
-
542
- elif st.session_state.video_path:
543
- # Show previously uploaded video info
544
- st.success(f"✅ Video ready: {st.session_state.video_name}")
545
- st.video(st.session_state.video_bytes)
546
-
547
- with col2:
548
- st.markdown("### 🖼️ Background Image")
549
-
550
- # Background selection method
551
- background_method = st.radio(
552
- "Choose background method:",
553
- ["📋 Preset Backgrounds", "📁 Upload Custom Image"],
554
- index=0
555
- )
556
-
557
- background_url = None
558
- custom_background = None
559
-
560
- if background_method == "📋 Preset Backgrounds":
561
- # Check premium access and get appropriate backgrounds
562
- is_premium = check_premium_access()
563
-
564
- if is_premium:
565
- background_options = get_professional_backgrounds()
566
- st.info("🎨 **Professional Backgrounds** - Premium collection available!")
567
- else:
568
- background_options = get_basic_backgrounds()
569
- st.info("🆓 **Basic Backgrounds** - Upgrade for professional collection!")
570
-
571
- selected_background = st.selectbox(
572
- "Choose background",
573
- options=list(background_options.keys()),
574
- index=0
575
- )
576
-
577
- background_url = background_options[selected_background]
578
-
579
- # Show background preview
580
- try:
581
- background_image = load_background_image(background_url)
582
- st.image(background_image, caption=f"Background: {selected_background}", use_container_width=True)
583
- except:
584
- st.error("Failed to load background image")
585
-
586
- else: # Upload Custom Image
587
- uploaded_background = st.file_uploader(
588
- "Upload your background image",
589
- type=['jpg', 'jpeg', 'png', 'bmp'],
590
- help="Upload a custom background image (JPG, PNG, BMP)"
591
- )
592
-
593
- if uploaded_background:
594
- # Load and display custom background
595
- try:
596
- custom_background = np.array(Image.open(uploaded_background).convert('RGB'))
597
- st.image(custom_background, caption="Custom Background", use_container_width=True)
598
- st.success(f"✅ Custom background uploaded: {uploaded_background.name}")
599
- except Exception as e:
600
- st.error(f"Failed to load custom background: {e}")
601
- custom_background = None
602
  else:
603
- st.info("Please upload a background image")
 
 
 
 
 
 
 
 
 
 
 
604
 
605
- # Process button
606
- if (uploaded_video or st.session_state.video_path) and st.button("🎬 Process Video", type="primary"):
607
-
608
- # Check if background is selected
609
- if background_method == "📋 Preset Backgrounds" and not background_url:
610
- st.error("Please select a background first!")
611
- return
612
- elif background_method == "📁 Upload Custom Image" and custom_background is None:
613
- st.error("Please upload a background image first!")
614
- return
615
-
616
- # Get video path
617
- video_path = st.session_state.video_path
618
-
619
- if video_path and os.path.exists(video_path):
620
- # Create progress tracking
621
- progress_bar = st.progress(0)
622
- status_text = st.empty()
623
-
624
- def update_progress(progress, message):
625
- progress_bar.progress(progress)
626
- status_text.text(message)
627
-
628
- try:
629
- # Use the selected background
630
- if background_method == "📋 Preset Backgrounds":
631
- result_path = process_video_with_green_screen(
632
- video_path,
633
- background_url,
634
- update_progress
635
- )
636
- else:
637
- # Process with custom background
638
- result_path = process_video_with_custom_background(
639
- video_path,
640
- custom_background,
641
- update_progress
642
- )
643
-
644
- if result_path and os.path.exists(result_path):
645
- status_text.text("✅ Processing complete!")
646
-
647
- # Read the processed video
648
- with open(result_path, 'rb') as f:
649
- result_video = f.read()
650
-
651
- # Display result
652
- st.video(result_video)
653
-
654
- # Download button
655
- st.download_button(
656
- "💾 Download Processed Video",
657
- data=result_video,
658
- file_name="backgroundfx_result.mp4",
659
- mime="video/mp4"
660
- )
661
-
662
- # Update stats
663
- st.session_state.frames_processed += 100 # Approximate
664
-
665
- # Clean up
666
- os.unlink(result_path)
667
- else:
668
- st.error("❌ Processing failed!")
669
-
670
- except Exception as e:
671
- st.error(f"❌ Error during processing: {str(e)}")
672
- logger.error(f"Processing error: {e}")
673
- else:
674
- st.error("Video file not found. Please upload again.")
675
-
676
- if __name__ == "__main__":
677
- main()
 
1
+ # TILFØJ DISSE ÆNDRINGER TIL DIN app.py:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ # 1. RETTELSE: Rembg GPU providers (omkring linje 65)
4
+ # ERSTAT din rembg initialization med:
 
 
 
 
 
 
 
5
  try:
6
  from rembg import remove, new_session
7
+ import onnxruntime as ort
8
+
9
  REMBG_AVAILABLE = True
10
  logger.info("✅ Rembg loaded")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # FORCE GPU providers for ONNX
13
+ if CUDA_AVAILABLE:
14
+ providers = [
15
+ ('CUDAExecutionProvider', {
16
+ 'device_id': 0,
17
+ 'arena_extend_strategy': 'kSameAsRequested',
18
+ 'gpu_mem_limit': 20 * 1024 * 1024 * 1024, # 20GB for L4
19
+ 'cudnn_conv_algo_search': 'HEURISTIC',
20
+ }),
21
+ 'CPUExecutionProvider'
22
+ ]
23
+
24
+ # Create session with explicit GPU providers
25
+ rembg_session = new_session('u2net_human_seg', providers=providers)
26
+
27
+ # VIGTIGT: Warm up the model on GPU
28
+ dummy_img = Image.new('RGB', (512, 512), color='white')
29
+ with torch.cuda.amp.autocast(): # Use mixed precision
30
+ _ = remove(dummy_img, session=rembg_session)
31
+
32
+ logger.info(f" Rembg GPU session initialized with providers: {providers}")
33
+ else:
34
+ rembg_session = new_session('u2net_human_seg')
35
+ logger.info(" Rembg CPU session initialized")
 
 
 
 
 
 
 
 
 
 
36
 
37
+ except ImportError as e:
38
+ REMBG_AVAILABLE = False
39
+ rembg_session = None
40
+ logger.warning(f"⚠️ Rembg not available: {e}")
41
+
42
+ # 2. TILFØJ: Mixed precision for bedre GPU performance
43
+ # Tilføj efter torch imports:
44
+ if CUDA_AVAILABLE:
45
+ # Enable TF32 for better performance on L4
46
+ torch.backends.cuda.matmul.allow_tf32 = True
47
+ torch.backends.cudnn.allow_tf32 = True
48
+ torch.backends.cudnn.benchmark = True
49
+ torch.backends.cudnn.deterministic = False
50
+
51
+ # 3. FORBEDRET: segment_person_rembg_optimized funktion
52
+ def segment_person_rembg_optimized(frame):
53
+ """Optimized rembg segmentation with GPU acceleration"""
54
  try:
55
  if REMBG_AVAILABLE and rembg_session:
56
  # Convert frame to PIL Image
57
  pil_image = Image.fromarray(frame)
58
 
59
+ # Use GPU memory efficiently
60
+ if CUDA_AVAILABLE:
61
+ # Process with mixed precision for L4
62
+ with torch.cuda.amp.autocast():
63
+ output = remove(
64
+ pil_image,
65
+ session=rembg_session,
66
+ alpha_matting=True,
67
+ alpha_matting_foreground_threshold=240,
68
+ alpha_matting_background_threshold=10,
69
+ alpha_matting_erode_size=10
70
+ )
71
+ else:
72
+ output = remove(pil_image, session=rembg_session, alpha_matting=True)
73
 
74
  # Extract alpha channel as mask
75
  output_array = np.array(output)
76
  if output_array.shape[2] == 4:
77
+ mask = output_array[:, :, 3].astype(np.float32) / 255.0 # Use float32
78
  else:
79
+ mask = np.ones((frame.shape[0], frame.shape[1]), dtype=np.float32)
 
80
 
81
  return mask
82
  return None
 
84
  logger.error(f"Rembg segmentation failed: {e}")
85
  return None
86
 
87
+ # 4. TILFØJ: Debug info i sidebar for at verificere GPU usage
88
+ # Tilføj i main() funktionen efter GPU status dashboard:
89
+ with st.sidebar:
90
+ st.markdown("### 🔍 GPU Debug Info")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
+ if CUDA_AVAILABLE:
93
+ # Check ONNX providers
94
+ try:
95
+ import onnxruntime as ort
96
+ providers = ort.get_available_providers()
97
+ gpu_providers = [p for p in providers if 'CUDA' in p or 'Tensorrt' in p]
98
+ if gpu_providers:
99
+ st.success(f"✅ ONNX GPU: {', '.join(gpu_providers)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  else:
101
+ st.error(" No ONNX GPU providers!")
102
+ st.info(f"All providers: {providers}")
103
+ except:
104
+ st.warning("ONNX Runtime not available")
105
+
106
+ # PyTorch info
107
+ st.code(f"""
108
+ PyTorch: {torch.__version__}
109
+ CUDA: {torch.version.cuda}
110
+ cuDNN: {torch.backends.cudnn.version()}
111
+ TF32: {torch.backends.cuda.matmul.allow_tf32}
112
+ """)
113
 
114
+ # Tilføj knap til at teste GPU allocation
115
+ if st.button("🧪 Test GPU Allocation"):
116
+ try:
117
+ test_size = 2 # GB
118
+ test_tensor = torch.zeros(
119
+ (test_size * 256, 1024, 1024),
120
+ device='cuda',
121
+ dtype=torch.float32
122
+ )
123
+ allocated = torch.cuda.memory_allocated() / 1024**3
124
+ st.success(f"✅ Allocated {allocated:.2f}GB on GPU!")
125
+ del test_tensor
126
+ torch.cuda.empty_cache()
127
+ except Exception as e:
128
+ st.error(f"❌ GPU allocation failed: {e}")