Spaces:
Running
Running
Commit ·
d5e1b6d
1
Parent(s): a068458
Quality: increase output resolution to 2048px, Lanczos upscale, adaptive unsharp mask
Browse files- processors/face_swap.py +26 -10
- utils/image_utils.py +2 -2
processors/face_swap.py
CHANGED
|
@@ -141,13 +141,15 @@ class FaceSwapper:
|
|
| 141 |
|
| 142 |
roi = result[y1:y2, x1:x2].copy()
|
| 143 |
|
| 144 |
-
# 1. Unsharp mask
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
| 147 |
|
| 148 |
# 2. CLAHE on L channel
|
| 149 |
lab = cv2.cvtColor(sharp, cv2.COLOR_BGR2LAB)
|
| 150 |
-
clahe = cv2.createCLAHE(clipLimit=
|
| 151 |
lab[:, :, 0] = clahe.apply(lab[:, :, 0])
|
| 152 |
enhanced_roi = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
|
| 153 |
|
|
@@ -181,12 +183,18 @@ class FaceSwapper:
|
|
| 181 |
self._init()
|
| 182 |
|
| 183 |
try:
|
| 184 |
-
#
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
source_faces = self._app.get(source_bgr)
|
| 192 |
target_faces = self._app.get(target_bgr)
|
|
@@ -208,6 +216,14 @@ class FaceSwapper:
|
|
| 208 |
if enhance:
|
| 209 |
result = self._enhance_opencv(result, target_faces)
|
| 210 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
return result, f"Swapped {len(target_faces)} face(s) successfully."
|
| 212 |
|
| 213 |
except Exception as exc:
|
|
|
|
| 141 |
|
| 142 |
roi = result[y1:y2, x1:x2].copy()
|
| 143 |
|
| 144 |
+
# 1. Unsharp mask — scale radius with face size for consistent sharpness
|
| 145 |
+
face_short = min(x2 - x1, y2 - y1)
|
| 146 |
+
sigma = max(1.5, face_short / 80) # larger face → larger radius
|
| 147 |
+
blurred = cv2.GaussianBlur(roi, (0, 0), sigma)
|
| 148 |
+
sharp = cv2.addWeighted(roi, 1.8, blurred, -0.8, 0)
|
| 149 |
|
| 150 |
# 2. CLAHE on L channel
|
| 151 |
lab = cv2.cvtColor(sharp, cv2.COLOR_BGR2LAB)
|
| 152 |
+
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
|
| 153 |
lab[:, :, 0] = clahe.apply(lab[:, :, 0])
|
| 154 |
enhanced_roi = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
|
| 155 |
|
|
|
|
| 183 |
self._init()
|
| 184 |
|
| 185 |
try:
|
| 186 |
+
# Preserve full resolution up to 2048px on the longest side.
|
| 187 |
+
# Going beyond that adds little visible quality on CPU but multiplies time.
|
| 188 |
+
MAX_DIM = 2048
|
| 189 |
+
orig_h, orig_w = target_bgr.shape[:2]
|
| 190 |
+
scale_down = 1.0
|
| 191 |
+
if max(orig_h, orig_w) > MAX_DIM:
|
| 192 |
+
scale_down = MAX_DIM / max(orig_h, orig_w)
|
| 193 |
+
target_bgr = cv2.resize(
|
| 194 |
+
target_bgr,
|
| 195 |
+
(int(orig_w * scale_down), int(orig_h * scale_down)),
|
| 196 |
+
interpolation=cv2.INTER_LANCZOS4,
|
| 197 |
+
)
|
| 198 |
|
| 199 |
source_faces = self._app.get(source_bgr)
|
| 200 |
target_faces = self._app.get(target_bgr)
|
|
|
|
| 216 |
if enhance:
|
| 217 |
result = self._enhance_opencv(result, target_faces)
|
| 218 |
|
| 219 |
+
# If we downscaled, upscale back to original resolution with Lanczos
|
| 220 |
+
if scale_down < 1.0:
|
| 221 |
+
result = cv2.resize(
|
| 222 |
+
result,
|
| 223 |
+
(orig_w, orig_h),
|
| 224 |
+
interpolation=cv2.INTER_LANCZOS4,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
return result, f"Swapped {len(target_faces)} face(s) successfully."
|
| 228 |
|
| 229 |
except Exception as exc:
|
utils/image_utils.py
CHANGED
|
@@ -14,13 +14,13 @@ def bgr_to_pil(bgr_img: np.ndarray) -> Image.Image:
|
|
| 14 |
return Image.fromarray(cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB))
|
| 15 |
|
| 16 |
|
| 17 |
-
def resize_to_max(image: np.ndarray, max_size: int =
|
| 18 |
"""Downscale image so its longest side does not exceed max_size."""
|
| 19 |
h, w = image.shape[:2]
|
| 20 |
if max(h, w) <= max_size:
|
| 21 |
return image
|
| 22 |
scale = max_size / max(h, w)
|
| 23 |
-
return cv2.resize(image, (int(w * scale), int(h * scale)), interpolation=cv2.
|
| 24 |
|
| 25 |
|
| 26 |
def apply_color_correction(source: np.ndarray, target: np.ndarray, mask: np.ndarray) -> np.ndarray:
|
|
|
|
| 14 |
return Image.fromarray(cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB))
|
| 15 |
|
| 16 |
|
| 17 |
+
def resize_to_max(image: np.ndarray, max_size: int = 2048) -> np.ndarray:
|
| 18 |
"""Downscale image so its longest side does not exceed max_size."""
|
| 19 |
h, w = image.shape[:2]
|
| 20 |
if max(h, w) <= max_size:
|
| 21 |
return image
|
| 22 |
scale = max_size / max(h, w)
|
| 23 |
+
return cv2.resize(image, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_LANCZOS4)
|
| 24 |
|
| 25 |
|
| 26 |
def apply_color_correction(source: np.ndarray, target: np.ndarray, mask: np.ndarray) -> np.ndarray:
|