GPU Accelerated OpenCV
This commit is contained in:
@@ -2,6 +2,7 @@ import cv2
|
||||
import numpy as np
|
||||
from modules.typing import Face, Frame
|
||||
import modules.globals
|
||||
from modules.gpu_processing import gpu_gaussian_blur, gpu_resize, gpu_cvt_color
|
||||
|
||||
def apply_color_transfer(source, target):
|
||||
"""
|
||||
@@ -61,8 +62,8 @@ def create_face_mask(face: Face, frame: Frame) -> np.ndarray:
|
||||
# Fill the padded convex hull
|
||||
cv2.fillConvexPoly(mask, hull_padded, 255)
|
||||
|
||||
# Smooth the mask edges
|
||||
mask = cv2.GaussianBlur(mask, (5, 5), 3)
|
||||
# Smooth the mask edges (GPU-accelerated when available)
|
||||
mask = gpu_gaussian_blur(mask, (5, 5), 3)
|
||||
|
||||
return mask
|
||||
|
||||
@@ -123,8 +124,8 @@ def create_lower_mouth_mask(
|
||||
polygon_relative_to_roi = expanded_landmarks - [min_x, min_y]
|
||||
cv2.fillPoly(mask_roi, [polygon_relative_to_roi], 255)
|
||||
|
||||
# Apply Gaussian blur to soften the mask edges
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (15, 15), 5)
|
||||
# Apply Gaussian blur to soften the mask edges (GPU-accelerated when available)
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (15, 15), 5)
|
||||
|
||||
# Place the mask ROI in the full-sized mask
|
||||
mask[min_y:max_y, min_x:max_x] = mask_roi
|
||||
@@ -192,8 +193,8 @@ def create_eyes_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, tuple
|
||||
cv2.ellipse(mask_roi, left_center, left_axes, 0, 0, 360, 255, -1)
|
||||
cv2.ellipse(mask_roi, right_center, right_axes, 0, 0, 360, 255, -1)
|
||||
|
||||
# Apply Gaussian blur to soften mask edges
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (15, 15), 5)
|
||||
# Apply Gaussian blur to soften mask edges (GPU-accelerated when available)
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (15, 15), 5)
|
||||
|
||||
# Place the mask ROI in the full-sized mask
|
||||
mask[min_y:max_y, min_x:max_x] = mask_roi
|
||||
@@ -374,15 +375,15 @@ def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, t
|
||||
left_shape = create_curved_eyebrow(left_local)
|
||||
right_shape = create_curved_eyebrow(right_local)
|
||||
|
||||
# Apply multi-stage blurring for natural feathering
|
||||
# Apply multi-stage blurring for natural feathering (GPU-accelerated when available)
|
||||
# First, strong Gaussian blur for initial softening
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (21, 21), 7)
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (21, 21), 7)
|
||||
|
||||
# Second, medium blur for transition areas
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (11, 11), 3)
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (11, 11), 3)
|
||||
|
||||
# Finally, light blur for fine details
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (5, 5), 1)
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (5, 5), 1)
|
||||
|
||||
# Normalize mask values
|
||||
mask_roi = cv2.normalize(mask_roi, None, 0, 255, cv2.NORM_MINMAX)
|
||||
@@ -405,7 +406,7 @@ def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, t
|
||||
right_local = right_eyebrow - [min_x, min_y]
|
||||
cv2.fillPoly(mask_roi, [left_local.astype(np.int32)], 255)
|
||||
cv2.fillPoly(mask_roi, [right_local.astype(np.int32)], 255)
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (21, 21), 7)
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (21, 21), 7)
|
||||
mask[min_y:max_y, min_x:max_x] = mask_roi
|
||||
eyebrows_cutout = frame[min_y:max_y, min_x:max_x].copy()
|
||||
eyebrows_polygon = np.vstack([left_eyebrow, right_eyebrow]).astype(np.int32)
|
||||
@@ -433,11 +434,11 @@ def apply_mask_area(
|
||||
return frame
|
||||
|
||||
try:
|
||||
resized_cutout = cv2.resize(cutout, (box_width, box_height))
|
||||
resized_cutout = gpu_resize(cutout, (box_width, box_height))
|
||||
roi = frame[min_y:max_y, min_x:max_x]
|
||||
|
||||
if roi.shape != resized_cutout.shape:
|
||||
resized_cutout = cv2.resize(
|
||||
resized_cutout = gpu_resize(
|
||||
resized_cutout, (roi.shape[1], roi.shape[0])
|
||||
)
|
||||
|
||||
@@ -457,8 +458,8 @@ def apply_mask_area(
|
||||
adjusted_polygon = polygon - [min_x, min_y]
|
||||
cv2.fillPoly(polygon_mask, [adjusted_polygon], 255)
|
||||
|
||||
# Apply strong initial feathering
|
||||
polygon_mask = cv2.GaussianBlur(polygon_mask, (21, 21), 7)
|
||||
# Apply strong initial feathering (GPU-accelerated when available)
|
||||
polygon_mask = gpu_gaussian_blur(polygon_mask, (21, 21), 7)
|
||||
|
||||
# Apply additional feathering
|
||||
feather_amount = min(
|
||||
|
||||
@@ -15,6 +15,7 @@ from modules.utilities import (
|
||||
is_video,
|
||||
)
|
||||
from modules.cluster_analysis import find_closest_centroid
|
||||
from modules.gpu_processing import gpu_gaussian_blur, gpu_sharpen, gpu_add_weighted, gpu_resize, gpu_cvt_color
|
||||
import os
|
||||
from collections import deque
|
||||
import time
|
||||
@@ -158,7 +159,7 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
|
||||
# print(f"Warning: Swapped frame shape {swapped_frame_raw.shape} differs from input {temp_frame.shape}.") # Debug
|
||||
# Attempt resize (might distort if aspect ratio changed, but better than crashing)
|
||||
try:
|
||||
swapped_frame_raw = cv2.resize(swapped_frame_raw, (temp_frame.shape[1], temp_frame.shape[0]))
|
||||
swapped_frame_raw = gpu_resize(swapped_frame_raw, (temp_frame.shape[1], temp_frame.shape[0]))
|
||||
except Exception as resize_e:
|
||||
# print(f"Error resizing swapped frame: {resize_e}") # Debug
|
||||
return original_frame
|
||||
@@ -236,7 +237,7 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
|
||||
|
||||
# Blend the original_frame with the (potentially mouth-masked) swapped_frame
|
||||
# Ensure both frames are uint8 before blending
|
||||
final_swapped_frame = cv2.addWeighted(original_frame.astype(np.uint8), 1 - opacity, swapped_frame.astype(np.uint8), opacity, 0)
|
||||
final_swapped_frame = gpu_add_weighted(original_frame.astype(np.uint8), 1 - opacity, swapped_frame.astype(np.uint8), opacity, 0)
|
||||
|
||||
# Ensure final frame is uint8 after blending (addWeighted should preserve it, but belt-and-suspenders)
|
||||
final_swapped_frame = final_swapped_frame.astype(np.uint8)
|
||||
@@ -312,17 +313,10 @@ def apply_post_processing(current_frame: Frame, swapped_face_bboxes: List[np.nda
|
||||
face_region = processed_frame[y1:y2, x1:x2]
|
||||
if face_region.size == 0: continue
|
||||
|
||||
# Apply sharpening with optimized parameters for Apple Silicon
|
||||
# Apply sharpening (GPU-accelerated when CUDA OpenCV is available)
|
||||
try:
|
||||
# Use smaller sigma for faster processing on Apple Silicon
|
||||
sigma = 2 if IS_APPLE_SILICON else 3
|
||||
blurred = cv2.GaussianBlur(face_region, (0, 0), sigma)
|
||||
sharpened_region = cv2.addWeighted(
|
||||
face_region, 1.0 + sharpness_value,
|
||||
blurred, -sharpness_value,
|
||||
0
|
||||
)
|
||||
sharpened_region = np.clip(sharpened_region, 0, 255).astype(np.uint8)
|
||||
sharpened_region = gpu_sharpen(face_region, strength=sharpness_value, sigma=sigma)
|
||||
processed_frame[y1:y2, x1:x2] = sharpened_region
|
||||
except cv2.error:
|
||||
pass
|
||||
@@ -338,7 +332,7 @@ def apply_post_processing(current_frame: Frame, swapped_face_bboxes: List[np.nda
|
||||
if PREVIOUS_FRAME_RESULT is not None and PREVIOUS_FRAME_RESULT.shape == processed_frame.shape and PREVIOUS_FRAME_RESULT.dtype == processed_frame.dtype:
|
||||
# Perform interpolation
|
||||
try:
|
||||
final_frame = cv2.addWeighted(
|
||||
final_frame = gpu_add_weighted(
|
||||
PREVIOUS_FRAME_RESULT, 1.0 - interpolation_weight,
|
||||
processed_frame, interpolation_weight,
|
||||
0
|
||||
@@ -813,10 +807,10 @@ def create_lower_mouth_mask(
|
||||
# Draw polygon on the ROI mask
|
||||
cv2.fillPoly(mask_roi, [polygon_relative_to_roi], 255)
|
||||
|
||||
# Apply Gaussian blur (ensure kernel size is odd and positive)
|
||||
# Apply Gaussian blur (GPU-accelerated when available)
|
||||
blur_k_size = getattr(modules.globals, "mask_blur_kernel", 15) # Default 15
|
||||
blur_k_size = max(1, blur_k_size // 2 * 2 + 1) # Ensure odd
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (blur_k_size, blur_k_size), 0) # Sigma=0 calculates from kernel
|
||||
mask_roi = gpu_gaussian_blur(mask_roi, (blur_k_size, blur_k_size), 0)
|
||||
|
||||
# Place the mask ROI in the full-sized mask
|
||||
mask[min_y:max_y, min_x:max_x] = mask_roi
|
||||
@@ -952,7 +946,7 @@ def apply_mouth_area(
|
||||
if roi.shape[:2] != mouth_cutout.shape[:2]:
|
||||
# Check if mouth_cutout has valid dimensions before resizing
|
||||
if mouth_cutout.shape[0] > 0 and mouth_cutout.shape[1] > 0:
|
||||
resized_mouth_cutout = cv2.resize(mouth_cutout, (box_width, box_height), interpolation=cv2.INTER_LINEAR)
|
||||
resized_mouth_cutout = gpu_resize(mouth_cutout, (box_width, box_height), interpolation=cv2.INTER_LINEAR)
|
||||
else:
|
||||
# print("Warning: mouth_cutout has invalid dimensions, cannot resize.")
|
||||
return frame # Cannot proceed without valid cutout
|
||||
@@ -1125,14 +1119,10 @@ def create_face_mask(face: Face, frame: Frame) -> np.ndarray:
|
||||
return mask # Return empty mask on error
|
||||
|
||||
|
||||
# Apply Gaussian blur to feather the mask edges
|
||||
# Kernel size should be reasonably large, odd, and positive
|
||||
# Apply Gaussian blur to feather the mask edges (GPU-accelerated when available)
|
||||
blur_k_size = getattr(modules.globals, "face_mask_blur", 31) # Default 31
|
||||
blur_k_size = max(1, blur_k_size // 2 * 2 + 1) # Ensure odd and positive
|
||||
|
||||
# Use sigma=0 to let OpenCV calculate from kernel size
|
||||
# Apply blur to the uint8 mask directly
|
||||
mask = cv2.GaussianBlur(mask, (blur_k_size, blur_k_size), 0)
|
||||
mask = gpu_gaussian_blur(mask, (blur_k_size, blur_k_size), 0)
|
||||
|
||||
# --- Optional: Return float mask for apply_mouth_area ---
|
||||
# mask = mask.astype(float) / 255.0
|
||||
|
||||
Reference in New Issue
Block a user