feat: 新增mask区域重绘功能 + 羽化贴回优化 + 发丝增强

新增功能:
- inpaint_mask.py: mask区域重绘服务(enhance/pure_inpaint两种模式)
  严格只在mask区做SD inpainting,发丝自然化
- /inpaint 页面 + /api/inpaint 接口:画板手绘mask + 提示词编辑 + denoising可调
- enhance_hair.webui_img2img: 新增denoising_strength参数(原硬编码0.35)

羽化贴回优化(hair_swap_manual.py 步骤⑥):
- feather_px(羽化范围)+ feather_alpha(羽化强度)拆分为两个独立参数
- 改为只羽化边缘(mask内部保持硬切,仅边缘带渐变)
- 新增黑底羽化对比图(纯黑背景凸显边缘过渡)

其他改动:
- OSS/COS密钥脱敏:改为可选import,未设环境变量时不崩溃
- manual页面:发型列表改为本次训练的8个(含hair_flow)
- enhance二次增强:重绘区改为「原图头发∪手绘mask」并外扩10%+减发际线带3cm
- 发型清单文档更新:179→180个可用发型(+hair_flow)

涉及文件:
- 新增: inpaint_mask.py, inpaint.html
- 修改: app.py, manual.html, hair_swap_manual.py, enhance_hair.py
- 修改: oss_module.py, upload_oss.py, cos_module.py(密钥脱敏)
- 文档: HAIRSTYLES_AVAILABLE.md, hairstyles_available.csv
This commit is contained in:
xsl
2026-07-08 20:38:31 +08:00
parent aa38085867
commit 215663177a
10 changed files with 751 additions and 229 deletions
+64
View File
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""mask 区域重绘(inpainting)服务
两种模式(底层都是 webui img2img inpainting,严格只在mask区重绘):
1. enhance: 用 enhance_hair.webui_img2img(默认提示词=发丝增强,denoising默认0.35
2. pure_inpaint: 纯SD inpainting(默认提示词=自然头发,denoising默认0.5
严格只重绘 mask 区域:inpainting_fill=1mask外保留原图)
"""
import cv2
import numpy as np
from utils import enhance_hair
def inpaint_mask(img, mask, prompt="", denoising_strength=0.5, mode="enhance"):
"""在 mask 区域做 SD inpainting 重绘。
参数:
img: 输入图 BGR
mask: 灰度mask,白色(255)=重绘区,与img同尺寸
prompt: 提示词(空则用模式默认值)
denoising_strength: 重绘强度 0~1
mode: "enhance""pure_inpaint"
返回:
result: 重绘后的图(mask区已重绘,其余保留原图)
info: 本次参数说明
"""
h, w = img.shape[:2]
# 归一化 mask(保证 0/255,尺寸一致)
if mask.shape[:2] != (h, w):
mask = cv2.resize(mask, (w, h), interpolation=cv2.INTER_NEAREST)
if mask.ndim == 3:
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
_, mask_bin = cv2.threshold(mask, 30, 255, cv2.THRESH_BINARY)
# 模式默认值
if not prompt:
if mode == "enhance":
prompt = "high quality, detailed natural hair strands, realistic hair texture, sharp focus, photorealistic"
else:
prompt = "natural realistic hair, soft hair strands, photorealistic, high quality, detailed"
# 统计 mask 区域占比
mask_ratio = float((mask_bin > 0).sum()) / mask_bin.size * 100
# 调用 enhance_hair.webui_img2img(底层是 inpaintinginpainting_fill=1 严格只在mask区重绘)
result = enhance_hair.webui_img2img(
img, mask_bin, prompt=prompt,
denoising_strength=denoising_strength
)
# webui 可能调整尺寸(调整到8的倍数),resize 回原图尺寸
if result.shape[:2] != (h, w):
result = cv2.resize(result, (w, h), interpolation=cv2.INTER_LANCZOS4)
# 双重保险:强制 mask 外区域用原图(虽然 inpainting_fill=1 已保证,再加一层确保严格)
mask_3c = (mask_bin > 0)[..., None]
result = np.where(mask_3c, result, img).astype(np.uint8)
info = (f"模式={mode},提示词=\"{prompt[:50]}...\"denoising={denoising_strength}"
f"mask区域={mask_ratio:.1f}%。严格只重绘mask区,其余保留原图。")
return result, mask_bin, info