包含: - 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密钥已脱敏为环境变量,原文件备份在本地
换发型服务 - 本地部署文档
本文档说明换发型服务(hair_service_sd)在本机的部署架构、接口用法、训练功能与日常运维操作。 该服务从
szlc@192.168.101.63:/home/szlc/project克隆部署至本机/home/xsl/change_hair。
一、总体架构
服务由三个独立进程组成,存在调用依赖关系:
┌─────────────────────────────┐
客户端 ──HTTP──▶ │ hair_service_sd (:8801) │ 主服务,对外提供所有接口
│ conda env: my_hair │
└──────────┬──────────────────┘
│ 换发型/训练时内部调用
┌──────────────┴───────────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ photo_service (:32678)│ │ webui (:57860) │
│ conda env: py310 │ │ conda env: sdwebui │
│ LoRA加载/训练调度 │────▶ │ SD模型 + ControlNet │
│ 训练: conda env:kohya│ 推理 │ LoRA权重 │
└──────────────────────┘ └──────────────────────┘
| 进程 | 端口 | conda 环境 | 职责 |
|---|---|---|---|
| hair_service_sd | 8801 | my_hair | 主服务,对外提供换发色/换发型/上传发型/训练回调接口 |
| photo_service | 32678 | py310 (推理) / kohya (训练) | LoRA 权重加载、webui 推理调度、LoRA 训练执行 |
| webui | 57860 | sdwebui | Stable Diffusion img2img 推理(SD模型 + ControlNet + LoRA) |
启动顺序(存在依赖):webui → photo_service → hair_service_sd
二、目录结构
所有数据部署在 /home/xsl/change_hair(对应原服务器 /home/szlc/project + /data):
/home/xsl/change_hair/
├── README.md # 本文档
├── start_all.sh # 统一启动/停止/状态脚本
├── start_photo.sh # photo_service 启动包装脚本
├── start_hair.sh # hair_service_sd 启动包装脚本
├── adapt_paths.sh # 路径适配脚本(部署时一次性执行)
├── fix_conda_paths.py # conda 环境路径修复脚本(部署时一次性执行)
├── test_haircolor.py # 换发色接口测试脚本
├── test_swaphair.py # 换发型接口测试脚本
├── test_train_api.py # 训练接口测试脚本
│
├── project/ # ← 原服务器 /home/szlc/project
│ ├── hair_service_sd/ # 主服务(9.9G,含 weights 模型权重)
│ ├── onediff/ # webui(49G,含SD模型+228个LoRA+ControlNet)
│ │ └── stable-diffusion-webui/
│ ├── photo_service/ # LoRA调度+训练服务(4.1M)
│ ├── kohya_ss_home/ # kohya LoRA训练框架(13G)
│ │ └── kohya_ss/ # train_network.py 所在目录
│ ├── data/ # 发型素材(4.9G)
│ │ ├── ref_hairstyle/ # 换发型发型材质(183个发型)
│ │ ├── ref_haircolor/ # 换发色参考
│ │ ├── hair_template_material/ # 发型模板(matting+pkl)
│ │ ├── upload_train_imgs/ # 上传发型训练图
│ │ ├── ref_user_imgs/ # ⚠️ 训练素材生成用的参考图(当前为空,见下文说明)
│ │ └── ...
│ └── logs/ # 所有服务日志
│
└── data/ # ← 原服务器 /data
└── train_material/ # 338个发型的LoRA训练素材(121G)
└── <hair_id>/
├── images/<N>_hairstyle/*.png # 训练图片
└── model/hairstyle_hd_lora.safetensors # 训练产出的LoRA权重
conda 环境位于 /home/xsl/miniconda3/envs/:
| 环境 | Python | 用途 |
|---|---|---|
| my_hair | 3.10.13 | hair_service_sd 主服务 |
| sdwebui | 3.10.19 | webui SD 推理 |
| py310 | 3.10.14 | photo_service LoRA调度 |
| kohya | 3.10.20 | LoRA 训练(本次部署新建,原服务用 Docker) |
三、日常运维操作
启动 / 停止 / 查看状态
cd /home/xsl/change_hair
bash start_all.sh start # 启动全部服务(按依赖顺序)
bash start_all.sh stop # 停止全部服务
bash start_all.sh restart # 重启全部服务
bash start_all.sh status # 查看运行状态与端口
启动后 webui 加载 SD 模型需约 1-2 分钟,status 显示端口就绪即表示可用。
查看日志
tail -f project/logs/webui.log # webui 日志
tail -f project/logs/photo_service.log # photo_service 日志
tail -f project/logs/hair_service.log # 主服务日志
运行接口测试
# 换发色测试(生成红色头发)
/home/xsl/miniconda3/envs/my_hair/bin/python test_haircolor.py
# 换发型测试(用已有发型)
/home/xsl/miniconda3/envs/my_hair/bin/python test_swaphair.py
测试结果图保存在 project/logs/test_*_result.jpg。
四、接口文档
主服务地址:http://127.0.0.1:8801
1. 换发色 POST /hairColor/v2
把照片中头发的颜色改为指定 RGB。
请求参数(JSON):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| img | string | 是 | 图片 base64,需带 data:image/jpeg;base64, 前缀 |
| userId | string | 是 | 用户标识 |
| rgb | [int,int,int] | 是 | 目标颜色,长度3的数组,如 [255,0,0] 红色 |
| output_format | string | 否 | base64(返回base64图片)或 url(上传OSS返回链接,默认) |
请求示例:
import base64, requests
with open("photo.jpg", "rb") as f:
img_b64 = "data:image/jpeg;base64," + base64.b64encode(f.read()).decode()
resp = requests.post("http://127.0.0.1:8801/hairColor/v2", json={
"img": img_b64,
"userId": "user001",
"rgb": [255, 0, 0], # 红色
"output_format": "base64"
})
result = resp.json()
# {"msg": "success", "state": 0, "result": "<base64图片>", "umd": ""}
2. 换发型 POST /api/swapHair/v1
把照片中人物的发型换成指定发型(需该发型已训练好 LoRA)。
请求参数(JSON):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| hair_id | string | 是 | 发型ID(对应 train_material 和 ref_hairstyle 中的目录名) |
| task_id | string | 是 | 任务ID(任意唯一字符串) |
| is_hr | string | 是 | "true" 高清版 / "false" 普通版 |
| user_img_path | string | 是 | 用户照片 base64,需带 data:image/jpeg;base64, 前缀 |
| output_format | string | 否 | base64 或 url(默认) |
请求示例:
resp = requests.post("http://127.0.0.1:8801/api/swapHair/v1", json={
"hair_id": "1907641420518580226",
"task_id": "task_001",
"is_hr": "true",
"user_img_path": img_b64, # data:image/jpeg;base64,...
"output_format": "base64"
})
result = resp.json()
# {"state": 0, "msg": "success", "data": "<base64图片>", "task_id": "task_001"}
可用发型:发型需同时具备 ref_hairstyle/<hair_id> 材质 + train_material/<hair_id>/model/hairstyle_hd_lora.safetensors 权重。查看可用发型:
# 列出材质+权重都齐全的发型
for hid in $(ls project/data/ref_hairstyle); do
[ -f data/train_material/$hid/model/hairstyle_hd_lora.safetensors ] && echo "$hid"
done
3. 新增发型训练 POST /api/uploadHair/v1
上传新发型图片,异步训练 LoRA。训练完成后发型即可用于换发型。
请求参数(JSON):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| hair_id | string | 是 | 新发型ID(自定义唯一ID) |
| img_lists | [string] | 是 | 发型图片 URL 列表(至少1张,需 HTTP 可访问的URL) |
注意:此接口 img_lists 必须是 HTTP URL(内部用 requests 下载),不支持 base64。如用本地图片,需先启动一个 HTTP 服务托管。
响应:立即返回 {"msg": "OK", "state": 0},训练在后台异步进行(约 30-40 分钟)。
4. 训练回调 POST /api/hair/trainCallBack
photo_service 训练完成后自动调用此接口通知主服务,一般不由外部直接调用。
五、新增发型训练(LoRA Training)
工作原理
原服务通过 Docker 容器(chinatszrn/ubuntu:kohya_ss)运行 kohya 的 train_network.py 训练 LoRA。本机部署已改为本地 conda 环境直接执行,无需 Docker。
完整链路:
uploadHair → 下载图片 → 生成训练素材(matting/pkl/训练图)
→ photo_service 入队
→ 后台 train_thread 执行:
accelerate launch train_network.py (1500步, 约30-40分钟)
→ 产出 hairstyle_hd_lora.safetensors
→ 回调 trainCallBack
→ 该发型即可用于换发型
训练配置(已适配本机)
photo_service 的训练命令(lora_train_service_1.py 的 train_thread 函数)关键参数:
| 配置项 | 值 | 说明 |
|---|---|---|
| 训练脚本 | kohya_ss_home/kohya_ss/train_network.py |
kohya LoRA 训练 |
| conda 环境 | /home/xsl/miniconda3/envs/kohya |
torch 2.0.1+cu118 |
| 基础模型 | v1-5-pruned-emaonly.safetensors |
SD底模(原为 majicmix,本机不存在,已替换) |
| 优化器 | AdamW |
原为 AdamW8bit(依赖 bitsandbytes,有兼容问题,已改) |
| 训练步数 | 1500 | |
| LoRA维度 | 128 (network_dim) | |
| GPU | device 0 | 单卡 |
手动触发训练(不经过 uploadHair)
如果已有训练素材(train_material/<hair_id>/images/<N>_hairstyle/*.png),可直接调用 photo_service 训练:
curl -X POST http://127.0.0.1:32678/api/hair/train \
-H "Content-Type: application/json" \
-d '{
"task_id": "manual_train_001",
"hair_id": "1905785164224868354",
"hair_material_dir": "/home/xsl/change_hair/data/train_material/1905785164224868354",
"is_tj": "1",
"device_id": "0",
"webui_addr": "http://0.0.0.0:32678/"
}'
训练进度查看:tail -f project/logs/photo_service.log(搜索 steps:)
⚠️ 训练素材生成依赖说明
uploadHair 的完整流程中,生成训练素材这一步依赖 project/data/ref_user_imgs/ 参考图目录(用于人脸对齐生成训练对)。该目录在原服务器上即为空(这是原项目状态)。
- 若
ref_user_imgs为空,uploadHair 能下载图片并触发 LoRA 训练,但生成的新训练素材质量会受限 - 要完整使用 uploadHair 上传全新发型训练,需往
ref_user_imgs/补充参考人像图 + 对应的.pkl关键点文件
六、部署改动记录
以下是对原服务代码/配置的改动,供维护参考:
1. 路径适配(所有配置和代码)
原服务器路径 → 本机路径的映射:
| 原路径 | 本机路径 |
|---|---|
/home/szlc/project/... |
/home/xsl/change_hair/project/... |
/data/train_material |
/home/xsl/change_hair/data/train_material |
/home/szlc/miniconda3 |
/home/xsl/miniconda3 |
涉及文件:hair_service_sd/config/configure.ini、hair_service_sd/*.py、photo_service/*.py、onediff/.../config.json 等。
执行脚本:adapt_paths.sh(业务代码)+ fix_conda_paths.py(conda环境)。
2. webui 配置改动
| 改动 | 说明 |
|---|---|
config.json 的 onediff compiler 路径 |
指向本机 onediff 目录 |
启动加 HF_HUB_OFFLINE=1 |
本机无法访问 huggingface.co,用本地缓存离线加载 CLIP |
| SD 模型 | 实际加载 v1-5-pruned-emaonly(config 里写的 majicmix 不存在,webui 自动 fallback) |
3. 换发型推理改动(hair_service_sd/gen_super_image.py)
refiner_checkpoint 从不存在的 majicmixRealistic_v7.safetensors 改为 v1-5-pruned-emaonly.safetensors(第185、213行)。否则 webui 报 Could not find checkpoint 错误。
4. photo_service 训练改动(photo_service/lora_train_service_1.py)
| 改动 | 原值 | 新值 |
|---|---|---|
| LoRA 输出目录 | /gz-fs/Lora |
webui 的 models/Lora 目录(实际路径) |
| kohya 训练目录 | /root/project/kohya_ss_home |
本机 kohya_ss_home |
| 训练方式 | sudo docker run chinatszrn/ubuntu:kohya_ss accelerate launch ... |
本地 accelerate launch ...(conda kohya 环境) |
| 基础模型 | /mnt/nas_hdd/.../majicmixRealistic_v7.safetensors |
本机 v1-5-pruned-emaonly.safetensors |
| 优化器 | AdamW8bit(bitsandbytes) | AdamW(torch 原生) |
| tokenizer | --tokenizer_cache_dir=/home/chinatszrn/.cache/clip |
删除(用 HF 缓存 + 离线模式) |
七、已知限制与注意事项
-
OSS 上传:换发色/换发型默认
output_format=url会把结果图上传到生产阿里云 OSS(密钥硬编码在代码中)。仅本地测试时建议用output_format=base64避免污染生产环境。 -
网络限制:本机无法访问
huggingface.co。webui 和训练都依赖本地 HF 缓存(~/.cache/huggingface),已配置离线模式。如需新增 HF 模型需手动准备缓存。 -
ref_user_imgs 为空:影响 uploadHair 生成训练素材的完整性(见第五节说明)。
-
单卡部署:GPU 固定使用 device 0。
CUDA_VISIBLE_DEVICES=0已写入启动脚本。如有多卡需求需调整配置。 -
服务进程保活:服务通过 nohup + wrapper 脚本启动。切勿直接
python xxx.py &启动,会被 shell 会话退出时清理。请始终用start_all.sh start或start_*.sh脚本。 -
webui 启动较慢:加载 SD 模型需 1-2 分钟,启动后需等待
start_all.sh status显示端口就绪才能正常服务。
八、快速排障
| 现象 | 可能原因 | 解决 |
|---|---|---|
换发型报 推理发型失败 |
webui 未就绪或 LoRA 缺失 | start_all.sh status 检查 webui 端口;确认发型 LoRA 存在 |
webui 报 Could not find checkpoint |
refiner 模型名不对 | 确认 gen_super_image.py 的 refiner_checkpoint 为 v1-5-pruned-emaonly.safetensors |
| 训练报 bitsandbytes 错误 | bnb 版本/优化器问题 | 确认用 AdamW(非 AdamW8bit) |
| webui 卡在 huggingface 重试 | HF 离线模式未启用 | 启动脚本已设 HF_HUB_OFFLINE=1,确认未丢失 |
| 服务启动后立即退出 | 未用 wrapper 脚本启动 | 用 start_all.sh start 启动 |
| conda 环境 python 段错误 | 环境二进制损坏 | 重新 rsync 环境并用 fix_conda_paths.py 修复(勿用 sed) |