包含: - 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密钥已脱敏为环境变量,原文件备份在本地
302 lines
14 KiB
Markdown
302 lines
14 KiB
Markdown
# 区域生发功能 - 技术调研报告
|
||
|
||
> **功能定义**:输入一张头发稀少/发际线偏高的人头照片,在照片上指定一个区域或遮罩,输出在该区域内"生发"(补全/填充头发)的结果图。
|
||
>
|
||
> 调研日期: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.5(v1-5-pruned)生成细密发丝的真实感有限,发丝可能偏糊
|
||
- ⚠️ 同一个人多次生发结果可能不一致(生成模型随机性)
|
||
|
||
**预估工作量**:2-3 天(mask 构造逻辑 + 接口封装 + 调参)
|
||
|
||
---
|
||
|
||
### 方案 B:ControlNet 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 调试)
|
||
|
||
---
|
||
|
||
### 方案 C:SAM 自动分割 + 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) — 商业产品效果参考(稀疏加密/发际线调整)
|