Upload reactor_swapper.py
Browse files
custom_nodes/comfyui-reactor-node/scripts/reactor_swapper.py
CHANGED
|
@@ -14,13 +14,14 @@ from insightface.app.common import Face
|
|
| 14 |
# except:
|
| 15 |
# cuda = None
|
| 16 |
import torch
|
|
|
|
| 17 |
|
| 18 |
import folder_paths
|
| 19 |
import comfy.model_management as model_management
|
| 20 |
from modules.shared import state
|
| 21 |
|
| 22 |
# 1. Добавьте импорт logging наверху файла, если его там нет:
|
| 23 |
-
import logging
|
| 24 |
from scripts.reactor_logger import logger
|
| 25 |
from reactor_utils import (
|
| 26 |
move_path,
|
|
@@ -126,9 +127,21 @@ def getFaceSwapModel(model_path: str):
|
|
| 126 |
if FS_MODEL is None or CURRENT_FS_MODEL_PATH is None or CURRENT_FS_MODEL_PATH != model_path:
|
| 127 |
CURRENT_FS_MODEL_PATH = model_path
|
| 128 |
FS_MODEL = unload_model(FS_MODEL)
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
else:
|
|
|
|
| 132 |
FS_MODEL = insightface.model_zoo.get_model(model_path, providers=providers)
|
| 133 |
return FS_MODEL
|
| 134 |
|
|
@@ -137,10 +150,10 @@ def get_landmarks_5(face):
|
|
| 137 |
# face.landmark_5: np.ndarray shape (5,2)
|
| 138 |
# Если нет, попробуй face.kps или face.landmark
|
| 139 |
if hasattr(face, 'landmark_5') and face.landmark_5 is not None:
|
| 140 |
-
|
| 141 |
return face.landmark_5
|
| 142 |
elif hasattr(face, 'kps') and face.kps is not None:
|
| 143 |
-
|
| 144 |
return face.kps
|
| 145 |
elif hasattr(face, 'landmark') and face.landmark is not None:
|
| 146 |
# 68-точечная разметка, берём нужные индексы
|
|
@@ -148,9 +161,9 @@ def get_landmarks_5(face):
|
|
| 148 |
# Пример: [36, 45, 30, 48, 54] — левый/правый глаз, нос, левый/правый рот
|
| 149 |
if face.landmark.shape[0] >= 68:
|
| 150 |
idxs = [36, 45, 30, 48, 54]
|
| 151 |
-
|
| 152 |
return face.landmark[idxs]
|
| 153 |
-
|
| 154 |
return None
|
| 155 |
|
| 156 |
#### Что проверить:
|
|
@@ -178,14 +191,14 @@ def create_gradient_mask(crop_size=256):
|
|
| 178 |
|
| 179 |
# 3. Рисуем эллипс (заполняем белым цветом, значение=1.0)
|
| 180 |
cv2.ellipse(
|
| 181 |
-
mask,
|
| 182 |
-
center,
|
| 183 |
-
axes,
|
| 184 |
-
angle=0,
|
| 185 |
-
startAngle=0,
|
| 186 |
-
endAngle=360,
|
| 187 |
-
color=1.0,
|
| 188 |
-
thickness=-1
|
| 189 |
)
|
| 190 |
|
| 191 |
# 4. Применяем размытие для плавных краёв
|
|
@@ -212,7 +225,7 @@ def paste_back(target_img, swapped_face, M, crop_size=256):
|
|
| 212 |
mask = create_gradient_mask(crop_size)
|
| 213 |
|
| 214 |
# Сохраняем для отладки
|
| 215 |
-
|
| 216 |
|
| 217 |
# Преобразуем в трехканальную маску
|
| 218 |
mask_3c = np.stack([mask] * 3, axis=2)
|
|
@@ -223,7 +236,7 @@ def paste_back(target_img, swapped_face, M, crop_size=256):
|
|
| 223 |
# 3. Обратное преобразование (WARP_INVERSE_MAP) для лица И маски
|
| 224 |
# Для лица (INTER_LANCZOS4 — высококачественная интерполяция)
|
| 225 |
inv_face = cv2.warpAffine(
|
| 226 |
-
swapped_face.astype(np.float32),
|
| 227 |
M,
|
| 228 |
(w, h),
|
| 229 |
flags=cv2.INTER_LANCZOS4 | cv2.WARP_INVERSE_MAP,
|
|
@@ -246,13 +259,13 @@ def paste_back(target_img, swapped_face, M, crop_size=256):
|
|
| 246 |
inv_mask = cv2.GaussianBlur(inv_mask, (3, 3), 0)
|
| 247 |
|
| 248 |
# Отладка
|
| 249 |
-
|
| 250 |
-
|
| 251 |
|
| 252 |
# Визуализация
|
| 253 |
-
#
|
| 254 |
-
#
|
| 255 |
-
#
|
| 256 |
|
| 257 |
# 6. Плавное наложение
|
| 258 |
target_img_float = target_img.astype(np.float32)
|
|
@@ -263,7 +276,7 @@ def paste_back(target_img, swapped_face, M, crop_size=256):
|
|
| 263 |
result = np.clip(result, 0, 255).astype(np.uint8)
|
| 264 |
|
| 265 |
# Сохраняем результат
|
| 266 |
-
|
| 267 |
|
| 268 |
return result
|
| 269 |
|
|
@@ -276,8 +289,8 @@ def visualize_points(img, points, color=(0, 255, 0)):
|
|
| 276 |
img = img.copy()
|
| 277 |
for p in points:
|
| 278 |
cv2.circle(img, tuple(p.astype(int)), 3, color, -1)
|
| 279 |
-
#
|
| 280 |
-
#
|
| 281 |
|
| 282 |
# Итоговая функция run_hyperswap с аффинным преобразованием
|
| 283 |
def run_hyperswap(session, source_face, target_face, target_img):
|
|
@@ -289,7 +302,7 @@ def run_hyperswap(session, source_face, target_face, target_img):
|
|
| 289 |
visualize_points(target_img, target_landmarks_5, (0, 255, 0)) # Зеленые точки
|
| 290 |
|
| 291 |
if target_landmarks_5 is None:
|
| 292 |
-
|
| 293 |
# Важно: Если ошибка, возвращаем None и исходную матрицу (или обрабатываем ошибку иначе)
|
| 294 |
return None, None
|
| 295 |
|
|
@@ -304,7 +317,7 @@ def run_hyperswap(session, source_face, target_face, target_img):
|
|
| 304 |
|
| 305 |
# Вычисляем аффинную матрицу
|
| 306 |
M = get_affine_transform(target_landmarks_5.astype(np.float32), std_landmarks_256)
|
| 307 |
-
|
| 308 |
|
| 309 |
#### Что проверить:
|
| 310 |
# Матрица `M` не должна содержать `NaN` или бесконечности.
|
|
@@ -317,9 +330,9 @@ def run_hyperswap(session, source_face, target_face, target_img):
|
|
| 317 |
#### Что проверить:
|
| 318 |
# Окно `"Crop Before Inference"` должно показывать лицо, вырезанное по аффинному преобразованию.
|
| 319 |
# Если изображение черное — проблема в `M` или `target_landmarks_5`.
|
| 320 |
-
|
| 321 |
-
#
|
| 322 |
-
#
|
| 323 |
|
| 324 |
# 4. Преобразуем crop для модели
|
| 325 |
# crop_input = crop[:, :, ::-1] / 255.0
|
|
@@ -333,9 +346,9 @@ def run_hyperswap(session, source_face, target_face, target_img):
|
|
| 333 |
# 5. Инференс
|
| 334 |
try:
|
| 335 |
output = session.run(None, {'source': source_embedding, 'target': crop_input})[0][0]
|
| 336 |
-
|
| 337 |
except Exception as e:
|
| 338 |
-
|
| 339 |
return target_img
|
| 340 |
|
| 341 |
# 6. Обратная нормализация
|
|
@@ -353,15 +366,21 @@ def run_hyperswap(session, source_face, target_face, target_img):
|
|
| 353 |
#### Что проверить:
|
| 354 |
# `output` должен быть в диапазоне `[0..255]` и содержать лицо.
|
| 355 |
# Если `output` черный — проблема в нормализации/денормализации или в самой модели.
|
| 356 |
-
|
| 357 |
-
#
|
| 358 |
-
#
|
| 359 |
|
| 360 |
# 7. ИЗМЕНЕНИЕ: Возвращаем результат 256x256 И матрицу M
|
| 361 |
# Мы больше не вызываем warp_and_paste_face отсюда.
|
| 362 |
# return result
|
| 363 |
|
| 364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
|
| 366 |
def sort_by_order(face, order: str):
|
| 367 |
if order == "left-right":
|
|
@@ -456,13 +475,13 @@ def get_face_single(img_data: np.ndarray, face, face_index=0, det_size=(640, 640
|
|
| 456 |
try:
|
| 457 |
faces_sorted = sort_by_order(face, order)
|
| 458 |
selected_face = faces_sorted[face_index]
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
return selected_face, 0
|
| 465 |
-
return faces_sorted[face_index], 0
|
| 466 |
# return sorted(face, key=lambda x: x.bbox[0])[face_index], 0
|
| 467 |
except IndexError:
|
| 468 |
return None, 0
|
|
@@ -484,14 +503,14 @@ def swap_face(
|
|
| 484 |
codeformer_weight: float = 0.5,
|
| 485 |
interpolation: str = "Bicubic",
|
| 486 |
):
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
|
| 496 |
global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH
|
| 497 |
result_image = target_img
|
|
@@ -607,13 +626,19 @@ def swap_face(
|
|
| 607 |
if len(source_faces_index) > 1 and source_face_idx > 0:
|
| 608 |
source_face, src_wrong_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[source_face_idx], gender_source=gender_source, order=faces_order[1])
|
| 609 |
source_face_idx += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 610 |
|
| 611 |
if source_face is not None and src_wrong_gender == 0:
|
| 612 |
target_face, wrong_gender = get_face_single(target_img, target_faces, face_index=face_num, gender_target=gender_target, order=faces_order[0])
|
| 613 |
if target_face is not None and wrong_gender == 0:
|
| 614 |
logger.status(f"Swapping...")
|
| 615 |
if "hyperswap" in model:
|
| 616 |
-
|
| 617 |
swapped_face_256, M = run_hyperswap(face_swapper, source_face, target_face, result)
|
| 618 |
if swapped_face_256 is not None:
|
| 619 |
result = paste_back(result, swapped_face_256, M, crop_size=256)
|
|
@@ -799,7 +824,17 @@ def swap_face_many(
|
|
| 799 |
logger.status(f'Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.')
|
| 800 |
elif source_face is not None:
|
| 801 |
results = target_imgs
|
| 802 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 803 |
face_swapper = getFaceSwapModel(model_path)
|
| 804 |
|
| 805 |
source_face_idx = 0
|
|
@@ -823,16 +858,23 @@ def swap_face_many(
|
|
| 823 |
target_face_single, wrong_gender = get_face_single(target_img, target_face, face_index=face_num, gender_target=gender_target, order=faces_order[0])
|
| 824 |
if target_face_single is not None and wrong_gender == 0:
|
| 825 |
result = target_img
|
| 826 |
-
|
| 827 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 828 |
logger.status(f"Face Boost is enabled")
|
| 829 |
bgr_fake, M = face_swapper.get(target_img, target_face_single, source_face, paste_back=False)
|
| 830 |
bgr_fake, scale = restorer.get_restored_face(bgr_fake, face_restore_model, face_restore_visibility, codeformer_weight, interpolation)
|
| 831 |
M *= scale
|
| 832 |
result = swapper.in_swap(target_img, bgr_fake, M)
|
| 833 |
else:
|
| 834 |
-
#
|
| 835 |
result = face_swapper.get(target_img, target_face_single, source_face)
|
|
|
|
| 836 |
results[i] = result
|
| 837 |
pbar.update(1)
|
| 838 |
elif wrong_gender == 1:
|
|
|
|
| 14 |
# except:
|
| 15 |
# cuda = None
|
| 16 |
import torch
|
| 17 |
+
import gc # для очистки кэша
|
| 18 |
|
| 19 |
import folder_paths
|
| 20 |
import comfy.model_management as model_management
|
| 21 |
from modules.shared import state
|
| 22 |
|
| 23 |
# 1. Добавьте импорт logging наверху файла, если его там нет:
|
| 24 |
+
# import logging - закомментирован
|
| 25 |
from scripts.reactor_logger import logger
|
| 26 |
from reactor_utils import (
|
| 27 |
move_path,
|
|
|
|
| 127 |
if FS_MODEL is None or CURRENT_FS_MODEL_PATH is None or CURRENT_FS_MODEL_PATH != model_path:
|
| 128 |
CURRENT_FS_MODEL_PATH = model_path
|
| 129 |
FS_MODEL = unload_model(FS_MODEL)
|
| 130 |
+
|
| 131 |
+
# Извлекаем имя файла модели из пути
|
| 132 |
+
model_filename = os.path.basename(model_path)
|
| 133 |
+
|
| 134 |
+
# Определяем правильный путь в зависимости от типа модели
|
| 135 |
+
if "hyperswap" in model_filename.lower():
|
| 136 |
+
# Ищем в директории hyperswap
|
| 137 |
+
correct_path = os.path.join(folder_paths.models_dir, "hyperswap", model_filename)
|
| 138 |
+
FS_MODEL = ort.InferenceSession(correct_path, providers=providers)
|
| 139 |
+
elif "reswapper" in model_filename.lower():
|
| 140 |
+
# Ищем в директории reswapper
|
| 141 |
+
correct_path = os.path.join(folder_paths.models_dir, "reswapper", model_filename)
|
| 142 |
+
FS_MODEL = insightface.model_zoo.get_model(correct_path, providers=providers)
|
| 143 |
else:
|
| 144 |
+
# Для моделей insightface используем оригинальный путь
|
| 145 |
FS_MODEL = insightface.model_zoo.get_model(model_path, providers=providers)
|
| 146 |
return FS_MODEL
|
| 147 |
|
|
|
|
| 150 |
# face.landmark_5: np.ndarray shape (5,2)
|
| 151 |
# Если нет, попробуй face.kps или face.landmark
|
| 152 |
if hasattr(face, 'landmark_5') and face.landmark_5 is not None:
|
| 153 |
+
# logger.debug("landmark_5: %s", face.landmark_5)
|
| 154 |
return face.landmark_5
|
| 155 |
elif hasattr(face, 'kps') and face.kps is not None:
|
| 156 |
+
# logger.debug("kps: %s", face.kps)
|
| 157 |
return face.kps
|
| 158 |
elif hasattr(face, 'landmark') and face.landmark is not None:
|
| 159 |
# 68-точечная разметка, берём нужные индексы
|
|
|
|
| 161 |
# Пример: [36, 45, 30, 48, 54] — левый/правый глаз, нос, левый/правый рот
|
| 162 |
if face.landmark.shape[0] >= 68:
|
| 163 |
idxs = [36, 45, 30, 48, 54]
|
| 164 |
+
# logger.debug("landmark (68 точек): %s", face.landmark[idxs])
|
| 165 |
return face.landmark[idxs]
|
| 166 |
+
# logger.warning("Нет подходящих точек в объекте Face. Доступные атрибуты: %s", dir(face))
|
| 167 |
return None
|
| 168 |
|
| 169 |
#### Что проверить:
|
|
|
|
| 191 |
|
| 192 |
# 3. Рисуем эллипс (заполняем белым цветом, значение=1.0)
|
| 193 |
cv2.ellipse(
|
| 194 |
+
mask, # Массив для рисования
|
| 195 |
+
center, # Центр эллипса
|
| 196 |
+
axes, # Полуоси (ширина, высота)
|
| 197 |
+
angle=0, # Угол поворота
|
| 198 |
+
startAngle=0, # Начальный угол дуги
|
| 199 |
+
endAngle=360, # Конечный угол дуги (360 = полный эллипс)
|
| 200 |
+
color=1.0, # Значение для заполнения (белый = 1.0)
|
| 201 |
+
thickness=-1 # -1 = заполнить всю область эллипса
|
| 202 |
)
|
| 203 |
|
| 204 |
# 4. Применяем размытие для плавных краёв
|
|
|
|
| 225 |
mask = create_gradient_mask(crop_size)
|
| 226 |
|
| 227 |
# Сохраняем для отладки
|
| 228 |
+
# cv2.imwrite("debug_original_mask.png", (mask * 255).astype(np.uint8))
|
| 229 |
|
| 230 |
# Преобразуем в трехканальную маску
|
| 231 |
mask_3c = np.stack([mask] * 3, axis=2)
|
|
|
|
| 236 |
# 3. Обратное преобразование (WARP_INVERSE_MAP) для лица И маски
|
| 237 |
# Для лица (INTER_LANCZOS4 — высококачественная интерполяция)
|
| 238 |
inv_face = cv2.warpAffine(
|
| 239 |
+
swapped_face.astype(np.float32).copy(),
|
| 240 |
M,
|
| 241 |
(w, h),
|
| 242 |
flags=cv2.INTER_LANCZOS4 | cv2.WARP_INVERSE_MAP,
|
|
|
|
| 259 |
inv_mask = cv2.GaussianBlur(inv_mask, (3, 3), 0)
|
| 260 |
|
| 261 |
# Отладка
|
| 262 |
+
# logger.debug("Warped face shape: %s | Min: %s | Max: %s", inv_face.shape, inv_face.min(), inv_face.max())
|
| 263 |
+
# logger.debug("Warped mask shape: %s | Min: %s | Max: %s", inv_mask.shape, inv_mask.min(), inv_mask.max())
|
| 264 |
|
| 265 |
# Визуализация
|
| 266 |
+
# cv2.imshow("Warped Face", inv_face.astype(np.uint8))
|
| 267 |
+
# cv2.imshow("Warped Mask", (inv_mask * 255).astype(np.uint8))
|
| 268 |
+
# cv2.waitKey(1)
|
| 269 |
|
| 270 |
# 6. Плавное наложение
|
| 271 |
target_img_float = target_img.astype(np.float32)
|
|
|
|
| 276 |
result = np.clip(result, 0, 255).astype(np.uint8)
|
| 277 |
|
| 278 |
# Сохраняем результат
|
| 279 |
+
# cv2.imwrite("debug_result.png", result)
|
| 280 |
|
| 281 |
return result
|
| 282 |
|
|
|
|
| 289 |
img = img.copy()
|
| 290 |
for p in points:
|
| 291 |
cv2.circle(img, tuple(p.astype(int)), 3, color, -1)
|
| 292 |
+
# cv2.imshow("Face Points", img)
|
| 293 |
+
# cv2.waitKey(1)
|
| 294 |
|
| 295 |
# Итоговая функция run_hyperswap с аффинным преобразованием
|
| 296 |
def run_hyperswap(session, source_face, target_face, target_img):
|
|
|
|
| 302 |
visualize_points(target_img, target_landmarks_5, (0, 255, 0)) # Зеленые точки
|
| 303 |
|
| 304 |
if target_landmarks_5 is None:
|
| 305 |
+
# logger.error("Не удалось получить 5 точек для целевого лица")
|
| 306 |
# Важно: Если ошибка, возвращаем None и исходную матрицу (или обрабатываем ошибку иначе)
|
| 307 |
return None, None
|
| 308 |
|
|
|
|
| 317 |
|
| 318 |
# Вычисляем аффинную матрицу
|
| 319 |
M = get_affine_transform(target_landmarks_5.astype(np.float32), std_landmarks_256)
|
| 320 |
+
# logger.debug("Affine Matrix M (used for cropping):\n%s", M)
|
| 321 |
|
| 322 |
#### Что проверить:
|
| 323 |
# Матрица `M` не должна содержать `NaN` или бесконечности.
|
|
|
|
| 330 |
#### Что проверить:
|
| 331 |
# Окно `"Crop Before Inference"` должно показывать лицо, вырезанное по аффинному преобразованию.
|
| 332 |
# Если изображение черное — проблема в `M` или `target_landmarks_5`.
|
| 333 |
+
# logger.debug("Crop shape: %s | Min: %s | Max: %s", crop.shape, crop.min(), crop.max())
|
| 334 |
+
# cv2.imshow("Crop Before Inference", crop)
|
| 335 |
+
# cv2.waitKey(1) # Отображает изображение
|
| 336 |
|
| 337 |
# 4. Преобразуем crop для модели
|
| 338 |
# crop_input = crop[:, :, ::-1] / 255.0
|
|
|
|
| 346 |
# 5. Инференс
|
| 347 |
try:
|
| 348 |
output = session.run(None, {'source': source_embedding, 'target': crop_input})[0][0]
|
| 349 |
+
# logger.debug("Model output shape: %s | Min: %s | Max: %s", output.shape, output.min(), output.max())
|
| 350 |
except Exception as e:
|
| 351 |
+
# logger.error("Ошибка выполнения модели: %s", e)
|
| 352 |
return target_img
|
| 353 |
|
| 354 |
# 6. Обратная нормализация
|
|
|
|
| 366 |
#### Что проверить:
|
| 367 |
# `output` должен быть в диапазоне `[0..255]` и содержать лицо.
|
| 368 |
# Если `output` черный — проблема в нормализации/денормализации или в самой модели.
|
| 369 |
+
# logger.debug("Output after denormalization: Min: %s | Max: %s", output.min(), output.max())
|
| 370 |
+
# cv2.imshow("Output After Denormalization", output)
|
| 371 |
+
# cv2.waitKey(1)
|
| 372 |
|
| 373 |
# 7. ИЗМЕНЕНИЕ: Возвращаем результат 256x256 И матрицу M
|
| 374 |
# Мы больше не вызываем warp_and_paste_face отсюда.
|
| 375 |
# return result
|
| 376 |
|
| 377 |
+
# --- Безопасное преобразование перед возвратом ---
|
| 378 |
+
# Убедимся, что результат всегда float32 в диапазоне [0..255]
|
| 379 |
+
output = output.astype(np.float32)
|
| 380 |
+
output = np.clip(output, 0, 255)
|
| 381 |
+
torch.cuda.empty_cache()
|
| 382 |
+
gc.collect()
|
| 383 |
+
return output, M # Возвращаем лицо (256x256) и матрицу M
|
| 384 |
|
| 385 |
def sort_by_order(face, order: str):
|
| 386 |
if order == "left-right":
|
|
|
|
| 475 |
try:
|
| 476 |
faces_sorted = sort_by_order(face, order)
|
| 477 |
selected_face = faces_sorted[face_index]
|
| 478 |
+
# logger.debug("Выбрано лицо: bbox=%s, landmark_5=%s, kps=%s, landmark=%s",
|
| 479 |
+
# selected_face.bbox,
|
| 480 |
+
# hasattr(selected_face, "landmark_5"),
|
| 481 |
+
# hasattr(selected_face, "kps"),
|
| 482 |
+
# hasattr(selected_face, "landmark"))
|
| 483 |
return selected_face, 0
|
| 484 |
+
# return faces_sorted[face_index], 0
|
| 485 |
# return sorted(face, key=lambda x: x.bbox[0])[face_index], 0
|
| 486 |
except IndexError:
|
| 487 |
return None, 0
|
|
|
|
| 503 |
codeformer_weight: float = 0.5,
|
| 504 |
interpolation: str = "Bicubic",
|
| 505 |
):
|
| 506 |
+
# >>>>> РЕШЕНИЕ: Принудительная установка уровня DEBUG <<<<<
|
| 507 |
+
# if logger.getEffectiveLevel() != logging.DEBUG:
|
| 508 |
+
# print("\n--- [ReActor Debug] Принудительная установка уровня логирования на DEBUG (10) в swap_face ---")
|
| 509 |
+
# logger.setLevel(logging.DEBUG)
|
| 510 |
|
| 511 |
+
# Проверочное сообщение (теперь оно должно появиться)
|
| 512 |
+
# logger.debug("--- ТЕСТ: swap_face запущена, уровень логирования DEBUG активен. ---")
|
| 513 |
+
# >>>>> КОНЕЦ РЕШЕНИЯ <<<<<
|
| 514 |
|
| 515 |
global SOURCE_FACES, SOURCE_IMAGE_HASH, TARGET_FACES, TARGET_IMAGE_HASH
|
| 516 |
result_image = target_img
|
|
|
|
| 626 |
if len(source_faces_index) > 1 and source_face_idx > 0:
|
| 627 |
source_face, src_wrong_gender = get_face_single(source_img, source_faces, face_index=source_faces_index[source_face_idx], gender_source=gender_source, order=faces_order[1])
|
| 628 |
source_face_idx += 1
|
| 629 |
+
|
| 630 |
+
# очистка кэша и памяти перед каждым Hyperswap вызовом
|
| 631 |
+
torch.cuda.empty_cache()
|
| 632 |
+
gc.collect()
|
| 633 |
+
SOURCE_FACES = None
|
| 634 |
+
TARGET_FACES = None
|
| 635 |
|
| 636 |
if source_face is not None and src_wrong_gender == 0:
|
| 637 |
target_face, wrong_gender = get_face_single(target_img, target_faces, face_index=face_num, gender_target=gender_target, order=faces_order[0])
|
| 638 |
if target_face is not None and wrong_gender == 0:
|
| 639 |
logger.status(f"Swapping...")
|
| 640 |
if "hyperswap" in model:
|
| 641 |
+
# logger.status(f"Swapping with Hyperswap...")
|
| 642 |
swapped_face_256, M = run_hyperswap(face_swapper, source_face, target_face, result)
|
| 643 |
if swapped_face_256 is not None:
|
| 644 |
result = paste_back(result, swapped_face_256, M, crop_size=256)
|
|
|
|
| 824 |
logger.status(f'Source Faces must have no entries (default=0), one entry, or same number of entries as target faces.')
|
| 825 |
elif source_face is not None:
|
| 826 |
results = target_imgs
|
| 827 |
+
|
| 828 |
+
# Определяем путь к модели в зависимости от типа
|
| 829 |
+
if "inswapper" in model:
|
| 830 |
+
model_path = os.path.join(insightface_path, model)
|
| 831 |
+
elif "reswapper" in model:
|
| 832 |
+
model_path = os.path.join(reswapper_path, model)
|
| 833 |
+
elif "hyperswap" in model:
|
| 834 |
+
model_path = os.path.join(hyperswap_path, model)
|
| 835 |
+
else:
|
| 836 |
+
model_path = os.path.join(insightface_path, model)
|
| 837 |
+
|
| 838 |
face_swapper = getFaceSwapModel(model_path)
|
| 839 |
|
| 840 |
source_face_idx = 0
|
|
|
|
| 858 |
target_face_single, wrong_gender = get_face_single(target_img, target_face, face_index=face_num, gender_target=gender_target, order=faces_order[0])
|
| 859 |
if target_face_single is not None and wrong_gender == 0:
|
| 860 |
result = target_img
|
| 861 |
+
|
| 862 |
+
# Обработка в зависимости от типа модели
|
| 863 |
+
if "hyperswap" in model:
|
| 864 |
+
# Для Hyperswap используем специальную функцию
|
| 865 |
+
swapped_face_256, M = run_hyperswap(face_swapper, source_face, target_face_single, result)
|
| 866 |
+
if swapped_face_256 is not None:
|
| 867 |
+
result = paste_back(result, swapped_face_256, M, crop_size=256)
|
| 868 |
+
elif face_boost_enabled:
|
| 869 |
logger.status(f"Face Boost is enabled")
|
| 870 |
bgr_fake, M = face_swapper.get(target_img, target_face_single, source_face, paste_back=False)
|
| 871 |
bgr_fake, scale = restorer.get_restored_face(bgr_fake, face_restore_model, face_restore_visibility, codeformer_weight, interpolation)
|
| 872 |
M *= scale
|
| 873 |
result = swapper.in_swap(target_img, bgr_fake, M)
|
| 874 |
else:
|
| 875 |
+
# Для остальных моделей используем метод get()
|
| 876 |
result = face_swapper.get(target_img, target_face_single, source_face)
|
| 877 |
+
|
| 878 |
results[i] = result
|
| 879 |
pbar.update(1)
|
| 880 |
elif wrong_gender == 1:
|