Update utilities.py
Browse files- utilities.py +24 -15
utilities.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
| 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
|
|
@@ -255,10 +256,10 @@ def create_green_screen_background(frame):
|
|
| 255 |
return green_screen
|
| 256 |
|
| 257 |
# ============================================================================ #
|
| 258 |
-
#
|
| 259 |
# ============================================================================ #
|
| 260 |
def replace_background_hq(frame, mask, background):
|
| 261 |
-
"""High-quality background replacement with
|
| 262 |
try:
|
| 263 |
# Resize background to match frame
|
| 264 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
|
@@ -267,30 +268,36 @@ def replace_background_hq(frame, mask, background):
|
|
| 267 |
if len(mask.shape) == 3:
|
| 268 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 269 |
|
| 270 |
-
#
|
| 271 |
-
|
| 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 |
-
#
|
| 279 |
-
|
| 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
|
| 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
|
| 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
|
| 294 |
result = frame * mask_3channel + background * (1 - mask_3channel)
|
| 295 |
result = np.clip(result, 0, 255).astype(np.uint8)
|
| 296 |
|
|
@@ -298,13 +305,15 @@ def replace_background_hq(frame, mask, background):
|
|
| 298 |
|
| 299 |
except Exception as e:
|
| 300 |
logger.error(f"Background replacement error: {e}")
|
| 301 |
-
# Fallback
|
| 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 |
-
|
| 307 |
-
|
|
|
|
|
|
|
| 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)
|
|
|
|
| 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 |
+
EDGE IMPROVEMENT: Added morphological operations and adjusted threshold
|
| 8 |
"""
|
| 9 |
|
| 10 |
import os
|
|
|
|
| 256 |
return green_screen
|
| 257 |
|
| 258 |
# ============================================================================ #
|
| 259 |
+
# IMPROVED EDGE HANDLING: Enhanced background replacement function
|
| 260 |
# ============================================================================ #
|
| 261 |
def replace_background_hq(frame, mask, background):
|
| 262 |
+
"""High-quality background replacement with improved edge handling"""
|
| 263 |
try:
|
| 264 |
# Resize background to match frame
|
| 265 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
|
|
|
| 268 |
if len(mask.shape) == 3:
|
| 269 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 270 |
|
| 271 |
+
# IMPROVED EDGE HANDLING: Adjusted threshold and morphological operations
|
| 272 |
+
threshold = 140 # Increased from 128 for tighter mask
|
|
|
|
| 273 |
_, mask_binary = cv2.threshold(mask, threshold, 255, cv2.THRESH_BINARY)
|
| 274 |
|
| 275 |
+
# Clean up edges with morphological operations
|
| 276 |
+
kernel = np.ones((3,3), np.uint8)
|
| 277 |
+
mask_binary = cv2.morphologyEx(mask_binary, cv2.MORPH_CLOSE, kernel) # Fill small holes
|
| 278 |
+
mask_binary = cv2.morphologyEx(mask_binary, cv2.MORPH_OPEN, kernel) # Remove small noise
|
| 279 |
+
|
| 280 |
+
# Optional: Apply erosion then dilation for cleaner edges
|
| 281 |
+
# mask_binary = cv2.erode(mask_binary, kernel, iterations=1)
|
| 282 |
+
# mask_binary = cv2.dilate(mask_binary, kernel, iterations=1)
|
| 283 |
+
|
| 284 |
# Convert to float and normalize
|
| 285 |
mask_float = mask_binary.astype(np.float32) / 255.0
|
| 286 |
|
| 287 |
+
# Minimal edge feathering for smooth but sharp edges
|
| 288 |
+
feather_radius = 1 # Reduced from 2 for sharper edges
|
|
|
|
| 289 |
kernel_size = feather_radius * 2 + 1
|
| 290 |
mask_feathered = cv2.GaussianBlur(mask_float, (kernel_size, kernel_size), feather_radius/3)
|
| 291 |
|
| 292 |
+
# Ensure strong opacity in the center
|
|
|
|
| 293 |
mask_feathered = np.where(mask_feathered > 0.5,
|
| 294 |
+
np.minimum(mask_feathered * 1.2, 1.0), # Boost high values
|
| 295 |
mask_feathered * 0.8) # Reduce low values
|
| 296 |
|
| 297 |
# Create 3-channel mask
|
| 298 |
mask_3channel = np.stack([mask_feathered] * 3, axis=2)
|
| 299 |
|
| 300 |
+
# Simple compositing
|
| 301 |
result = frame * mask_3channel + background * (1 - mask_3channel)
|
| 302 |
result = np.clip(result, 0, 255).astype(np.uint8)
|
| 303 |
|
|
|
|
| 305 |
|
| 306 |
except Exception as e:
|
| 307 |
logger.error(f"Background replacement error: {e}")
|
| 308 |
+
# Fallback with same improvements
|
| 309 |
try:
|
| 310 |
if len(mask.shape) == 3:
|
| 311 |
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
| 312 |
background = cv2.resize(background, (frame.shape[1], frame.shape[0]))
|
| 313 |
+
_, mask_binary = cv2.threshold(mask, 140, 255, cv2.THRESH_BINARY)
|
| 314 |
+
kernel = np.ones((3,3), np.uint8)
|
| 315 |
+
mask_binary = cv2.morphologyEx(mask_binary, cv2.MORPH_CLOSE, kernel)
|
| 316 |
+
mask_binary = cv2.morphologyEx(mask_binary, cv2.MORPH_OPEN, kernel)
|
| 317 |
mask_normalized = mask_binary.astype(np.float32) / 255.0
|
| 318 |
mask_3channel = np.stack([mask_normalized] * 3, axis=2)
|
| 319 |
result = frame * mask_3channel + background * (1 - mask_3channel)
|