初始化:换发型/换发色/训练发型服务

包含:
- hair_service_sd: 主服务(换发型/换发色/生发,端口8801)
- photo_service: LoRA调度+训练(端口32678)
- hair_grow_service: 调试测试页(端口8888,含4个测试页)
- 批量训练脚本(batch_train_hairstyles.py)
- 发际线mask自动识别(hairline_mask.py,4种方案)
- 手绘mask换发型(hair_swap_manual.py)
- 文档:README.md + LARGE_FILES.md + docs/

大文件(模型权重200G、训练数据123G)已排除,见 LARGE_FILES.md
OSS/COS密钥已脱敏为环境变量,原文件备份在本地
This commit is contained in:
xsl
2026-07-07 13:53:52 +08:00
commit 443cfa298f
312 changed files with 67065 additions and 0 deletions
+301
View File
@@ -0,0 +1,301 @@
# 区域生发功能 - 技术调研报告
> **功能定义**:输入一张头发稀少/发际线偏高的人头照片,在照片上指定一个区域或遮罩,输出在该区域内"生发"(补全/填充头发)的结果图。
>
> 调研日期:2026-06-21
---
## 一、需求分析与技术挑战
### 1.1 需求拆解
本功能本质是一个 **"受限区域头发补全"**constrained hair inpainting)任务,可拆为三个子问题:
| 子问题 | 描述 | 难度 |
|--------|------|------|
| ① 在哪生发 | 确定缺发区域(发际线后退区、头顶稀疏区),即 mask | 中(可人工画,也可自动检测) |
| ② 生成什么 | 在 mask 区域内生成与本人发色、发质、走向自然衔接的头发 | 高(核心难点) |
| ③ 如何融合 | 把生成结果无缝贴回原图,消除边缘拼接痕迹 | 低(已有成熟工具) |
### 1.2 关键挑战
1. **生成真实感**:新生头发必须符合该人的发色/卷曲度/走向,不能突兀。SD1.5 对细密发丝的生成能力有限,容易出现"塑料感"或糊。
2. **边缘自然衔接**:新生头发与原有头发、额头皮质的交界处不能有硬边或色差。
3. **发丝纹理一致性**:新头发应延续原头发的纹理方向(从发旋/发根向外)。
4. **不破坏五官**:生发区域靠近额头/眉毛,生成时不能改变五官。
5. **mask 边界合理性**:mask 区域应只覆盖"该长头发但目前没长"的地方,不能覆盖到脸上或背景。
---
## 二、技术方案全景
综合代码库已有能力和业界方案,归纳出 **4 种实现路径**,按推荐度排序。
### 方案 A:复用现有 webui Inpainting(★★★★★ 强烈推荐)
**核心思路**:把"生发"当作一次 SD inpainting——mask 区域重绘为头发,非 mask 区域保持不变。这正是换发型服务里 `webui_img2img` 已经在做的事,几乎可原样复用。
```
[原图] → [人检+1k关键点] → [构造生发mask] → [webui_img2img(mask内重绘为头发)] → [泊松融合] → [结果]
```
**可复用的现成组件**(均在代码库内,已验证可用):
| 环节 | 复用组件 | 文件:行号 | 说明 |
|------|---------|-----------|------|
| 人脸检测 | `RetinaFaceDetector.forward` | models/detector.py:148 | 定位人脸框 |
| 1k关键点 | `MomocvFaceAlignment1K.stable_forward` | utils/MomocvFaceAlignment1K.py:404 | 发际线定位的关键 |
| 头发mask | `Generator_Matte.matte_inference` | hair_matting/Generator_Matte.py:81 | 现有头发alpha |
| **发际线/头皮分割** | `Generator_BaldSeg_5c.forward` | models/Generator_BaldSeg.py:45 | **自动定位缺发区,权重存在** |
| 额头凸包mask | `draw_bigger_hull_mask` | utils/landmark_processor.py:1079 | 关键点→额头区mask |
| **SD生成** | `webui_img2img(img, mask_img, ...)` | gen_super_image.py:416 | **核心生成,已验证可用** |
| 泊松融合 | `cv2.seamlessClone` | core/hairstyle_model.py:1380 | 消除拼接缝 |
**mask 构造方法(两种交互模式)**
- **手动模式**(用户画遮罩):前端提供画笔工具,用户在发际线/稀疏区涂抹 → 直接作为 inpainting mask。最简单可靠。
- **自动模式**(自动检测缺发区):`BaldSeg` 的 5 类分割直接区分"有发区/头皮区",或用 `额头凸包mask - 现有头发mask = 缺发带`。无需用户画,但需调参。
**生成参数建议**(基于 `build_body_v2`gen_super_image.py:86):
- `denoising_strength = 0.5~0.7`(重绘强度,过低生不出新发,过高破坏原有头发)
- `mask_blur = 8~12`(边缘羽化,避免硬边)
- `prompt = "thick natural hair, detailed hair strands, realistic hairline, high detail"`
- `negative_prompt = "bald, receding hairline, thinning hair, bald spot, skin, low quality"`
- `inpainting_fill = 1`mask 区域填噪声重绘)
- `steps = 20~30`
**优势**
- ✅ 90% 代码已存在且验证过,开发量最小(主要是拼装 + mask 构造逻辑)
- ✅ 与现有服务架构完全一致,可作为一个新接口 `/api/hairGrow/v1` 接入
- ✅ 无需新增任何模型权重
**劣势**
- ⚠️ SD1.5v1-5-pruned)生成细密发丝的真实感有限,发丝可能偏糊
- ⚠️ 同一个人多次生发结果可能不一致(生成模型随机性)
**预估工作量**:2-3 天(mask 构造逻辑 + 接口封装 + 调参)
---
### 方案 BControlNet Canny 约束发丝走向(★★★★ 推荐)
**核心思路**:在方案 A 基础上,引入 **ControlNet Canny** 约束新生头发的走向,使其与已有头发的纹理方向一致,提升真实感。
```
[原图] → 提取已有头发的 canny 边缘 → [mask内: SD inpainting + ControlNet canny约束] → [结果]
```
**关键改动**(相对方案A):
- 启用 webui 的 ControlNet(代码库已装 `control_v11p_sd15_canny` 模型,可用)
- 参照 `gen_super_image.py:244 build_body``alwayson_scripts` 结构,把 openpose 改成 canny
- canny 输入图:对原图做 `cv2.Canny`,或在 mask 外保留原头发边缘作为约束
**ControlNet 配置示例**(需新增到 build_body):
```python
"alwayson_scripts": {
"controlnet": {
"args": [{
"model": "control_v11p_sd15_canny",
"module": "canny",
"weight": 0.5, # 约束强度,0.3-0.7
"input_image": <原图base64>, # webui自动做canny
"resize_mode": "just_resize"
}]
}
}
```
**优势**
- ✅ 发丝走向更自然、与原发衔接更好
- ✅ ControlNet 模型和扩展都已在 webui 里,无需额外部署
**劣势**
- ⚠️ canny 约束在"完全无发的光头区"无法提供有效边缘(那里本来就没头发),主要对"稀疏→加密"有效
- ⚠️ 需要调 `weight`,过强会让新生头发僵硬
**预估工作量**3-4 天(方案A + ControlNet 调试)
---
### 方案 CSAM 自动分割 + SD Inpainting(★★★ 可选)
**核心思路**:用 **Segment Anything (SAM)** 替代手动画 mask——用户点击缺发区域,SAM 自动分割出精确边界,再做 inpainting。
```
[原图] → [用户点击发际线区域] → [SAM分割出该区域mask] → [SD inpainting] → [结果]
```
**业界实践**:这是目前社区主流的"点击式 inpainting"范式,已有成熟工具:
- **sd-webui-inpaint-anything** 扩展([GitHub](https://github.com/Uminosachi/sd-webui-inpaint-anything)):webui 集成 SAM + inpainting
- 学术:[Segment Anything Meets Image Inpainting (arXiv:2304.06790)](https://arxiv.org/abs/2304.06790) 提出 click-and-fill 范式
**是否需要 SAM**
- 若用户**手动画 mask**(方案A)→ 不需要 SAM
- 若要**点击式自动分割** → 需要 SAM(代码库没有,需新增 ~2.5G 模型)
**优势**
- ✅ 交互体验好(点一下即可),mask 边界比手画精确
**劣势**
- ⚠️ 需新增 SAM 模型(vit_h 约 2.5G),增加部署复杂度
- ⚠️ SAM 对"发际线缺发带"这种渐变区域的分割不一定准确(头发与皮肤对比度低时)
- ⚠️ 代码库已有 `Generator_BaldSeg`(头皮分割)+ 1k关键点(发际线定位),**可达到类似自动 mask 效果,不一定非要用 SAM**
**预估工作量**5-7 天(含 SAM 集成)
**结论**:**不优先推荐**。代码库的 BaldSeg + 关键点方案已能实现"自动定位缺发区",SAM 的边际价值不大,除非未来需要通用点击分割。
---
### 方案 D:专项 LoRA 微调 / 训练生发模型(★★ 备选)
**核心思路**:收集"稀疏发/高发际线 → 浓密发"的配对数据,训练一个生发专用 LoRA 或微调模型,专门优化生发效果。
**业界相关研究**
- **HairDiffusion (NeurIPS 2024)**:把发型/发色编辑重新定义为 latent diffusion inpainting 任务
- **AnyBald (WACV 2026)**diffusion inpainting + 可学习文本提示做头发去除(反向任务,架构可借鉴)
- **Stable-Hair (arXiv 2024)**diffusion 真实世界发型迁移
**适用场景**:当方案 A/B 用通用 SD 的生发效果不够好(发丝太糊/不真实),且业务对质量要求高时。
**优势**
- ✅ 可针对性优化生发质量,效果上限最高
**劣势**
-**需要大量配对训练数据**(稀疏发→浓密发),数据收集是最大瓶颈
- ❌ 训练+调优周期长(2-4 周)
- ❌ 代码库的 LoRA 训练流程(kohya)是针对"发型模板"的,生发是不同任务,需重新组织数据
**预估工作量**:3-4 周(含数据收集、训练、调优)
**结论**:**作为后续优化手段备选**,不作为首期方案。先用方案 A/B 跑通,效果不达标再考虑。
---
## 三、推荐方案与实施路径
### 3.1 推荐:方案 A 为主,方案 B 为增强
**首期实现方案 A**(复用 webui inpainting),最快验证可行性;**若效果不足,叠加方案 B**ControlNet canny)。方案 C/D 暂不引入。
理由:
1. 方案 A 的 90% 组件已存在且验证过,风险最低、见效最快
2. 代码库的 `Generator_BaldSeg` 能自动定位缺发区,免去了 SAM 的额外部署
3. 与现有服务架构一致,可作为 hair_service_sd 的新接口无缝接入
### 3.2 推荐的功能管线(方案A)
```
输入: 人头图 + (可选)手绘mask
├─ 若无手绘mask(自动模式):
│ 1. RetinaFace 检测人脸 → 1k关键点
│ 2. Generator_BaldSeg 分割 → 提取"头皮/发际线"区
│ 3. 或: 额头凸包mask(关键点) - 现有头发mask(matting) = 缺发带
│ 4. mask 形态学处理(dilate/erode 羽化)
├─ 若有手绘mask(手动模式):
│ 直接使用,轻微 dilate 羽化
mask_blur=10 的 inpainting:
webui_img2img(
img = 原图,
mask_img = 生发mask,
tag = "thick natural hair, detailed hair strands, hairline",
denoising_strength = 0.6,
is_hr = True
)
泊松融合贴回(cv2.seamlessClone + GaussianBlur)
输出: 生发结果图
```
### 3.3 接口设计建议
```
POST /api/hairGrow/v1
{
"img": "data:image/jpeg;base64,...", # 人头图
"mask": "data:image/png;base64,...", # 可选: 手绘mask(白色=生发区)
"auto_mask": true, # 可选: 无mask时自动检测缺发区
"userId": "xxx",
"output_format": "base64"
}
```
返回(同换发型):
```json
{"state": 0, "msg": "success", "data": "<base64结果图>", "task_id": "..."}
```
---
## 四、代码库可复用能力清单(详细)
以下能力**全部已在本机部署中验证可用**,无需新增模型:
| 能力 | 模块 | 文件:行号 | 权重 | 对生发的作用 |
|------|------|-----------|------|-------------|
| 人脸检测 | RetinaFace | models/detector.py:148 | Resnet50_Final.pth (109M) | 入口,定位人脸 |
| 1k关键点 | MomocvFaceAlignment1K | utils/MomocvFaceAlignment1K.py:404 | face_alignment_1k.pth (49M) | **发际线定位** |
| 头发抠图 | Generator_Matte | hair_matting/Generator_Matte.py:81 | deeplabv3+gca (535M) | 现有头发边界 |
| **头皮分割** | **Generator_BaldSeg_5c** | **models/Generator_BaldSeg.py:45** | **ori_hair_checkpoint (134M)** | **自动定位缺发区** |
| 人脸分割 | FaceSeg | core/faceseg/face_seg.py:32 | faceseg_20210927 (229M) | 排除五官区 |
| SD inpainting | webui_img2img | gen_super_image.py:416 | v1-5 SD模型 (4.2G) | **核心生成** |
| 额头mask构造 | draw_bigger_hull_mask | utils/landmark_processor.py:1079 | - | 关键点→额头区 |
| 泊松融合 | cv2.seamlessClone | core/hairstyle_model.py:1380 | - | 边缘融合 |
| 形态学处理 | cv2.dilate/erode | utils/landmark_processor.py 多处 | - | mask羽化 |
| ControlNet canny | sd-webui-controlnet | webui扩展(已装) | control_v11p_sd15_canny | 发丝走向约束(方案B) |
---
## 五、风险与质量评估
| 风险点 | 严重度 | 应对 |
|--------|--------|------|
| SD1.5生发发丝偏糊 | 中 | 方案B加canny约束;或后续换SDXL inpainting模型(需升级webui) |
| 自动mask不准(误判缺发区) | 中 | 首期优先支持手绘mask;自动模式作为可选增强 |
| 生发破坏五官 | 高 | mask构造时必须减去FaceSeg的人脸区;inpainting的mask严格限定 |
| 多次结果不一致 | 低 | 固定seed(代码已用seed=123456789);可返回多张供选 |
| 边缘拼接痕迹 | 低 | 泊松融合+GaussianBlur已有成熟方案 |
**质量预期**:方案A对手绘mask场景(用户明确指定生发区)效果应较好;对稀疏加密(发际线整体下移)效果中等;对完全光头大区域生发效果有限(建议用方案D专项优化)。
---
## 六、实施建议(分阶段)
**阶段一(2-3天):MVP 手动 mask 生发**
- 实现 `/api/hairGrow/v1` 接口,接收手绘 mask
- 复用 `webui_img2img`prompt 写生发描述
- 加泊松融合后处理
- 用几张发际线高的测试图调参(denoising/mask_blur/prompt
**阶段二(2-3天):自动 mask + ControlNet**
- 接入 `Generator_BaldSeg` 自动检测缺发区
- 叠加 ControlNet canny 约束发丝走向(方案B)
- 对比有无 canny 的效果差异
**阶段三(按需):质量优化**
- 若效果不足:收集生发配对数据,训练专项 LoRA(方案D)
- 或升级到 SDXL inpainting 模型提升发丝真实感
---
## 七、参考资料
**学术论文**
- [HairDiffusion: Vivid Multi-Colored Hair Editing via Latent Diffusion (NeurIPS 2024)](https://openreview.net/forum?id=UQflshLbZv) — 发型编辑的 inpainting 范式
- [Stable-Hair: Real-World Hair Transfer via Diffusion Model (arXiv 2024)](https://arxiv.org/html/2407.14078v1) — 扩散发型迁移
- [Segment Anything Meets Image Inpainting (arXiv:2304.06790)](https://arxiv.org/abs/2304.06790) — SAM click-and-fill 范式
**实践工具/社区**
- [sd-webui-inpaint-anything (GitHub)](https://github.com/Uminosachi/sd-webui-inpaint-anything) — SAM + inpainting webui 扩展
- [Stable Diffusion Inpainting with SAM (HuggingFace Space)](https://huggingface.co/spaces/Sanshruth/Stable-Diffusion-Inpainting_with_SAM) — 在线 demo
- [Reddit: 用 inpainting 添加头发的技巧](https://www.reddit.com/r/StableDiffusion/comments/15x9yvs/) — 社区经验:先预填底色再 inpainting
- [Evoto Hair Editor](https://www.evoto.ai/features/hair-part) — 商业产品效果参考(稀疏加密/发际线调整)