Files
change_hair/docs/HAIR_GROWTH_FEASIBILITY.md
xsl 443cfa298f 初始化:换发型/换发色/训练发型服务
包含:
- 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密钥已脱敏为环境变量,原文件备份在本地
2026-07-07 13:53:52 +08:00

302 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 区域生发功能 - 技术调研报告
> **功能定义**:输入一张头发稀少/发际线偏高的人头照片,在照片上指定一个区域或遮罩,输出在该区域内"生发"(补全/填充头发)的结果图。
>
> 调研日期: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) — 商业产品效果参考(稀疏加密/发际线调整)