跳转至

人脸保护 — YOLO + 软椭圆混合

文件face_protector.py(147 行)

为什么需要人脸保护

扩散再生(img2img)在低 strength 下虽然对整体图像影响很小,但对人脸细节非常敏感。眼睛不对称、牙齿变形、皮肤纹理异常等问题在人脸区域尤其明显。

解决方案:在扩散再生前提取人脸,再生后将原始人脸"粘回去"。

三步流程

graph LR
    A[输入图像] --> B[YOLO 检测人脸]
    B --> C[裁剪保存面部区域]
    C --> D[扩散再生]
    D --> E[椭圆 alpha 混合恢复人脸]
    E --> F[输出图像]

Step 1:人脸检测(detect_face_bboxes()

两种检测器可选:

YOLO 模式(默认)

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model(image, classes=[0])  # class 0 = person
bboxes = results[0].boxes.xyxy.cpu().numpy()  # (x1, y1, x2, y2)

使用 YOLOv8 nano(~6MB),检测 person class。

Haar Cascade 回退

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
faces = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
# 增加 20% 边距

YOLO 不可用时(如未安装 ultralytics)自动回退。

Step 2:面部提取(extract_faces()

def extract_faces(self, image):
    bboxes = self.detect_face_bboxes(image)
    faces = []
    for (x1, y1, x2, y2) in bboxes:
        crop = image[y1:y2, x1:x2].copy()
        faces.append(((x1, y1, x2, y2), crop))
    return faces

返回 (bbox, face_crop) 列表,供后续恢复使用。

Step 3:面部恢复(restore_faces()

扩散再生完成后,将原始面部混合回去:

def restore_faces(self, target_image, original_faces):
    for (x1, y1, x2, y2), face_crop in original_faces:
        w, h = x2 - x1, y2 - y1

        # 创建椭圆 alpha mask
        mask = np.zeros((h, w), dtype=np.float32)
        center = (w // 2, h // 2)
        axes = (int(w * 0.4), int(h * 0.4))  # 中心 40% 为纯白
        cv2.ellipse(mask, center, axes, 0, 0, 360, 1.0, -1)

        # 重度高斯模糊实现软边缘
        blur_size = max(w, h) // 4
        if blur_size % 2 == 0:
            blur_size += 1
        mask = cv2.GaussianBlur(mask, (blur_size, blur_size), 0)

        # Alpha 混合
        mask_3d = mask[:, :, np.newaxis]
        target_roi = target_image[y1:y2, x1:x2].astype(np.float64)
        source_roi = face_crop.astype(np.float64)
        blended = source_roi * mask_3d + target_roi * (1.0 - mask_3d)
        target_image[y1:y2, x1:x2] = blended.astype(np.uint8)

椭圆 Mask 的设计

┌─────────────────────┐
│                     │
│    ╭───────────╮    │
│    │  α = 1.0  │    │ ← 中心 40%:纯白,完全使用原始人脸
│    │  (原始)   │    │
│    ╰───────────╯    │
│  渐变过渡带          │ ← 高斯模糊实现软边缘
│                     │
└─────────────────────┘
    α = 0.0(再生)     ← 外围:完全使用再生结果
  • 中心 40%:α = 1.0,100% 使用原始面部
  • 边缘:高斯模糊产生的渐变过渡
  • 外围:α = 0.0,100% 使用再生结果

blur_size = max(w, h) // 4 确保过渡带足够宽,不会产生明显的接缝。

与扩散再生的集成

InvisibleEngine.remove_watermark() 中:

# Phase 1: 提取
protector = FaceProtector(use_yolo=True)
original_faces = protector.extract_faces(cv_img)

# 执行扩散再生(会扭曲人脸)
watermark_remover.remove_watermark(...)

# Phase 2: 恢复
protector.restore_faces(output_cv, original_faces)

局限性

场景 效果
正面清晰人脸 ✅ 效果好
侧脸 / 遮挡 ⚠️ YOLO 可能检测不到
多人合影 ✅ 逐个处理
卡通 / 动漫人脸 ❌ YOLO person class 不保证检测
人脸占图像比例很小 ⚠️ 裁剪后分辨率低,混合可能有接缝