人脸保护 — 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 不保证检测 |
| 人脸占图像比例很小 | ⚠️ 裁剪后分辨率低,混合可能有接缝 |