Update utilities.py
Browse files- utilities.py +27 -16
utilities.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
utilities.py - Helper functions and utilities for Video Background Replacement
|
| 4 |
Contains all the utility functions, background creation functions
|
| 5 |
UPDATED: Models passed as parameters instead of globals
|
|
|
|
| 6 |
"""
|
| 7 |
|
| 8 |
import os
|
|
@@ -253,8 +254,11 @@ def create_green_screen_background(frame):
|
|
| 253 |
green_screen = np.full((h, w, 3), (0, 177, 64), dtype=np.uint8)
|
| 254 |
return green_screen
|
| 255 |
|
|
|
|
|
|
|
|
|
|
| 256 |
def replace_background_hq(frame, mask, background):
|
| 257 |
-
"""High-quality background replacement with
|
| 258 |
try:
|
| 259 |
# Resize background to match frame
|
| 260 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
|
@@ -263,38 +267,45 @@ def replace_background_hq(frame, mask, background):
|
|
| 263 |
if len(mask.shape) == 3:
|
| 264 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 265 |
|
| 266 |
-
#
|
| 267 |
-
|
|
|
|
|
|
|
| 268 |
|
| 269 |
-
#
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
kernel_size = feather_radius * 2 + 1
|
| 272 |
mask_feathered = cv2.GaussianBlur(mask_float, (kernel_size, kernel_size), feather_radius/3)
|
| 273 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
# Create 3-channel mask
|
| 275 |
mask_3channel = np.stack([mask_feathered] * 3, axis=2)
|
| 276 |
|
| 277 |
-
#
|
| 278 |
-
|
| 279 |
-
background_linear = np.power(background.astype(np.float32) / 255.0, 2.2)
|
| 280 |
-
|
| 281 |
-
# Composite in linear space
|
| 282 |
-
result_linear = frame_linear * mask_3channel + background_linear * (1 - mask_3channel)
|
| 283 |
-
|
| 284 |
-
# Convert back to gamma space
|
| 285 |
-
result = np.power(result_linear, 1/2.2) * 255.0
|
| 286 |
result = np.clip(result, 0, 255).astype(np.uint8)
|
| 287 |
|
| 288 |
return result
|
| 289 |
|
| 290 |
except Exception as e:
|
| 291 |
logger.error(f"Background replacement error: {e}")
|
| 292 |
-
# Fallback to simple replacement
|
| 293 |
try:
|
| 294 |
if len(mask.shape) == 3:
|
| 295 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 296 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]))
|
| 297 |
-
|
|
|
|
|
|
|
| 298 |
mask_3channel = np.stack([mask_normalized] * 3, axis=2)
|
| 299 |
result = frame * mask_3channel + background * (1 - mask_3channel)
|
| 300 |
return result.astype(np.uint8)
|
|
|
|
| 3 |
utilities.py - Helper functions and utilities for Video Background Replacement
|
| 4 |
Contains all the utility functions, background creation functions
|
| 5 |
UPDATED: Models passed as parameters instead of globals
|
| 6 |
+
CRITICAL FIX: Fixed transparency issue in replace_background_hq function
|
| 7 |
"""
|
| 8 |
|
| 9 |
import os
|
|
|
|
| 254 |
green_screen = np.full((h, w, 3), (0, 177, 64), dtype=np.uint8)
|
| 255 |
return green_screen
|
| 256 |
|
| 257 |
+
# ============================================================================ #
|
| 258 |
+
# CRITICAL FIX: REPLACE THIS ENTIRE FUNCTION TO FIX TRANSPARENCY ISSUE
|
| 259 |
+
# ============================================================================ #
|
| 260 |
def replace_background_hq(frame, mask, background):
|
| 261 |
+
"""High-quality background replacement with FIXED binary masking - NO TRANSPARENCY"""
|
| 262 |
try:
|
| 263 |
# Resize background to match frame
|
| 264 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
|
|
|
| 267 |
if len(mask.shape) == 3:
|
| 268 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 269 |
|
| 270 |
+
# ===== CRITICAL FIX: BINARIZE THE MASK TO PREVENT TRANSPARENCY =====
|
| 271 |
+
# This is the key change - force mask to be either 0 or 1, no in-between values
|
| 272 |
+
threshold = 128 # You can adjust this value between 100-200 if needed
|
| 273 |
+
_, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
|
| 274 |
|
| 275 |
+
# Convert to float and normalize
|
| 276 |
+
mask_float = mask_binary.astype(np.float32) / 255.0
|
| 277 |
+
|
| 278 |
+
# Optional: Apply MINIMAL edge feathering AFTER binarization
|
| 279 |
+
# This gives smooth edges without making the whole person transparent
|
| 280 |
+
feather_radius = 2 # Reduced from 3 to minimize transparency
|
| 281 |
kernel_size = feather_radius * 2 + 1
|
| 282 |
mask_feathered = cv2.GaussianBlur(mask_float, (kernel_size, kernel_size), feather_radius/3)
|
| 283 |
|
| 284 |
+
# Ensure strong opacity in the center (prevent ghosting)
|
| 285 |
+
# Boost high values and reduce low values to maintain solid appearance
|
| 286 |
+
mask_feathered = np.where(mask_feathered > 0.5,
|
| 287 |
+
np.minimum(mask_feathered * 1.2, 1.0), # Boost high values (but cap at 1.0)
|
| 288 |
+
mask_feathered * 0.8) # Reduce low values
|
| 289 |
+
|
| 290 |
# Create 3-channel mask
|
| 291 |
mask_3channel = np.stack([mask_feathered] * 3, axis=2)
|
| 292 |
|
| 293 |
+
# Simple compositing (skip gamma correction for now to ensure solidity)
|
| 294 |
+
result = frame * mask_3channel + background * (1 - mask_3channel)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
result = np.clip(result, 0, 255).astype(np.uint8)
|
| 296 |
|
| 297 |
return result
|
| 298 |
|
| 299 |
except Exception as e:
|
| 300 |
logger.error(f"Background replacement error: {e}")
|
| 301 |
+
# Fallback to simple replacement - ALSO FIXED WITH BINARY MASK
|
| 302 |
try:
|
| 303 |
if len(mask.shape) == 3:
|
| 304 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 305 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]))
|
| 306 |
+
# Also fix the fallback with binary threshold
|
| 307 |
+
_, mask_binary = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY)
|
| 308 |
+
mask_normalized = mask_binary.astype(np.float32) / 255.0
|
| 309 |
mask_3channel = np.stack([mask_normalized] * 3, axis=2)
|
| 310 |
result = frame * mask_3channel + background * (1 - mask_3channel)
|
| 311 |
return result.astype(np.uint8)
|