DegMaTsu commited on
Commit
d218c4d
·
verified ·
1 Parent(s): bf99de4

Upload reactor_swapper.py

Browse files
custom_nodes/comfyui-reactor-node/scripts/reactor_swapper.py CHANGED
@@ -200,71 +200,71 @@ def create_gradient_mask(crop_size=256):
200
  #### 1. Используйте `cv2.BORDER_TRANSPARENT` (OpenCV ≥ 4.5)
201
  # Этот флаг позволяет **не заполнять** области за пределами маски никаким цветом (пиксели остаются `0` или "прозрачные").
202
  def paste_back(target_img, swapped_face, M, crop_size=256):
 
203
 
204
- # Улучшенная функция paste_back с идеальной овальной маской
205
- # Стандартная функция InsightFace для вставки лица обратно с использованием WARP_INVERSE_MAP.
206
- # target_img: Исходное изображение (BGR, numpy)
207
- # swapped_face: Результат работы модели (256x256, BGR, numpy)
208
- # M: Матрица аффинного преобразования (Target -> Crop)
209
- # crop_size: Размер кропа (для HyperSwap это 256)
210
 
211
  # 1. Создание мягкой маски (Эрозия + Размытие)
212
  mask = create_gradient_mask(crop_size)
213
-
214
- # Сохраняем для отладки
215
- cv2.imwrite("debug_original_mask.png", (mask * 255).astype(np.uint8))
216
-
217
  # Преобразуем в трехканальную маску
218
  mask_3c = np.stack([mask] * 3, axis=2)
219
-
220
  # 2. Получаем размеры целевого изображения
221
  h, w = target_img.shape[:2]
222
-
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,
230
- borderMode=cv2.BORDER_TRANSPARENT # <<< ИЗМЕНЕНИЕ: TRANSPARENT вместо CONSTANT
 
231
  )
232
-
233
- # Для маски (INTER_CUBIC — плавные границы)
234
- inv_mask = cv2.warpAffine(
235
- mask_3c,
236
- M,
237
  (w, h),
238
  flags=cv2.INTER_CUBIC | cv2.WARP_INVERSE_MAP,
239
- borderMode=cv2.BORDER_TRANSPARENT # <<< ИЗМЕНЕНИЕ: TRANSPARENT вместо CONSTANT
 
240
  )
241
-
242
- # 4. Ограничение значений маски [0, 1]
243
- inv_mask = np.clip(inv_mask, 0, 1)
244
-
245
- # 5. Дополнительное размытие для устранения артефактов
246
- inv_mask = cv2.GaussianBlur(inv_mask, (3, 3), 0)
247
-
248
- # Отладка
249
- logger.debug("Warped face shape: %s | Min: %s | Max: %s", inv_face.shape, inv_face.min(), inv_face.max())
250
- logger.debug("Warped mask shape: %s | Min: %s | Max: %s", inv_mask.shape, inv_mask.min(), inv_mask.max())
251
-
252
- # Визуализация
253
- # cv2.imshow("Warped Face", inv_face.astype(np.uint8))
254
- # cv2.imshow("Warped Mask", (inv_mask * 255).astype(np.uint8))
255
- # cv2.waitKey(1)
256
-
257
- # 6. Плавное наложение
258
- target_img_float = target_img.astype(np.float32)
259
- inv_face_float = inv_face.astype(np.float32)
260
- result = target_img_float * (1.0 - inv_mask) + inv_face_float * inv_mask
261
-
262
- # 7. Ограничение результата [0, 255]
263
- result = np.clip(result, 0, 255).astype(np.uint8)
264
-
265
- # Сохраняем результат
266
- cv2.imwrite("debug_result.png", result)
267
-
268
  return result
269
 
270
  #### Что проверить:
@@ -338,7 +338,6 @@ def run_hyperswap(session, source_face, target_face, target_img):
338
 
339
  # --- CPU FLOAT NORMALIZATION FIX ---
340
  # предотвращает появление "синей кожи" и "шума" при работе на CPU
341
- # (адаптировано из патча patch_cpu_fix.diff)
342
  if isinstance(output, np.ndarray):
343
  # устранение NaN и бесконечностей
344
  output = np.nan_to_num(output, nan=0.0, posinf=255.0, neginf=0.0)
@@ -360,7 +359,7 @@ def run_hyperswap(session, source_face, target_face, target_img):
360
  # (ваш код без изменений, но без старой денормализации)
361
  output = output.transpose(1, 2, 0) # CHW -> HWC
362
  output = output[:, :, ::-1] # RGB -> BGR (Убедитесь, что это BGR, если вход был BGR)
363
- logger.debug("Output after denormalization: Min: %s | Max: %s", output.min(), output.max())
364
 
365
  # Визуализация после денормализации
366
  #### Что проверить:
 
200
  #### 1. Используйте `cv2.BORDER_TRANSPARENT` (OpenCV ≥ 4.5)
201
  # Этот флаг позволяет **не заполнять** области за пределами маски никаким цветом (пиксели остаются `0` или "прозрачные").
202
  def paste_back(target_img, swapped_face, M, crop_size=256):
203
+ # Улучшенная функция paste_back с идеальной овальной маской и исправлениями артефактов
204
 
205
+ # target_img: Исходное изображение (BGR, numpy, uint8)
206
+ # swapped_face: Результат работы модели (256x256, BGR, uint8)
207
+ # M: Матрица аффинного преобразования (Target -> Crop), но здесь используется M_inv из run_hyperswap
208
+ # crop_size: Размер кропа (для HyperSwap это 256)
 
 
209
 
210
  # 1. Создание мягкой маски (Эрозия + Размытие)
211
  mask = create_gradient_mask(crop_size)
212
+
 
 
 
213
  # Преобразуем в трехканальную маску
214
  mask_3c = np.stack([mask] * 3, axis=2)
215
+
216
  # 2. Получаем размеры целевого изображения
217
  h, w = target_img.shape[:2]
218
+
219
+ # 3. Нормализация swapped_face к float32 [0,1] для warp
220
+ swapped_face_norm = swapped_face.astype(np.float32) / 255.0
221
+ mask_norm = mask_3c.astype(np.float32) # Маска уже [0,1]
222
+
223
+ # 4. Обратное преобразование (WARP_INVERSE_MAP) для лица И маски
224
+ # Используем BORDER_CONSTANT с borderValue=0.5 (серый, чтобы избежать синих/зеленых артефактов)
225
+ warped_face = cv2.warpAffine(
226
+ swapped_face_norm,
227
+ M, # Это M_inv из run_hyperswap
228
  (w, h),
229
  flags=cv2.INTER_LANCZOS4 | cv2.WARP_INVERSE_MAP,
230
+ borderMode=cv2.BORDER_CONSTANT,
231
+ borderValue=0.5 # Серый фон вместо черного/белого
232
  )
233
+
234
+ warped_mask = cv2.warpAffine(
235
+ mask_norm,
236
+ M, # Это M_inv из run_hyperswap
 
237
  (w, h),
238
  flags=cv2.INTER_CUBIC | cv2.WARP_INVERSE_MAP,
239
+ borderMode=cv2.BORDER_CONSTANT,
240
+ borderValue=0.0 # Маска: 0 за пределами
241
  )
242
+
243
+ # 5. Обработка после warp: Clip, NaN fix
244
+ warped_face = np.clip(warped_face, 0, 1) # Убираем отрицательные
245
+ warped_face = np.nan_to_num(warped_face, nan=0.5) # NaN -> серый
246
+
247
+ warped_mask = np.clip(warped_mask, 0, 1)
248
+ warped_mask = np.nan_to_num(warped_mask, nan=0.0)
249
+
250
+ # 6. Дополнительное размытие для устранения артефактов (опционально, но помогает)
251
+ warped_mask = cv2.GaussianBlur(warped_mask, (3, 3), 0)
252
+
253
+ # Отладочные логи (добавьте после warp)
254
+ logger.debug("Warped face shape: %s | Min: %s | Max: %s | NaN count: %s",
255
+ warped_face.shape, warped_face.min(), warped_face.max(), np.isnan(warped_face).sum())
256
+ logger.debug("Warped mask shape: %s | Min: %s | Max: %s | NaN count: %s",
257
+ warped_mask.shape, warped_mask.min(), warped_mask.max(), np.isnan(warped_mask).sum())
258
+
259
+ # 7. Плавное наложение в float32
260
+ target_float = target_img.astype(np.float32) / 255.0
261
+ result_float = target_float * (1.0 - warped_mask) + warped_face * warped_mask
262
+
263
+ # 8. Обратная нормализация к uint8
264
+ result = (result_float * 255).clip(0, 255).astype(np.uint8)
265
+
266
+ logger.debug("Final result: shape %s | Min: %s | Max: %s", result.shape, result.min(), result.max())
267
+
 
268
  return result
269
 
270
  #### Что проверить:
 
338
 
339
  # --- CPU FLOAT NORMALIZATION FIX ---
340
  # предотвращает появление "синей кожи" и "шума" при работе на CPU
 
341
  if isinstance(output, np.ndarray):
342
  # устранение NaN и бесконечностей
343
  output = np.nan_to_num(output, nan=0.0, posinf=255.0, neginf=0.0)
 
359
  # (ваш код без изменений, но без старой денормализации)
360
  output = output.transpose(1, 2, 0) # CHW -> HWC
361
  output = output[:, :, ::-1] # RGB -> BGR (Убедитесь, что это BGR, если вход был BGR)
362
+ logger.debug("Output after denormalization: Min: %s | Max: %s | NaN count: %s", output.min(), output.max(), np.isnan(output).sum())
363
 
364
  # Визуализация после денормализации
365
  #### Что проверить: