- gateway/app.py: 接口99 从盲转发改为本机实现(同接口4 模式): 显式 Form 三选一 + 惰性导入(缺依赖仅本接口报1007)+ run_in_threadpool - 单路排队: _if99_lock 全局互斥——串行推理限制 CPU 峰值 + 规避 MediaPipe 单例线程安全问题;实测 req2≈2×req1 确认串行 - 网关待改动 §9 重写: 本机实现部署步骤 + 依赖清单(mediapipe/opencv/ numpy<2,torch CPU 版可选——不装则发际线5点null)+ 权重已入git说明 - 实现说明/接口文档/CLAUDE.md 同步: 接口4/99 网关本机实现不转发 - 实测: BiSeNet 13.3M参数/51MB;CPU单核 0.46-0.65s/张;网关路径 70/70点;worker 路径保留(测试页直连);pytest 15 通过
8.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
旷视接口服务 — FastAPI 人像分析/生发服务,一个仓库、两种部署角色:
- worker(
app.py+face_analysis/+hairline/):跑真实算法(本地模型 + ComfyUI + change_hair),监听0.0.0.0:8187。本机(GPU 机)即 worker 角色,不跑网关。 - 网关(
gateway/):外网机器上的薄反向代理,负责鉴权/健康检查/派发,并把 worker 返回的*_base64落盘改写为*_url。接口4(豆包)和接口99(人脸特殊点,纯 CPU)在网关本机实现,不转发 worker。
历史包袱:
app.py的 FastAPIdescription和docs/接口文档.md部分示例仍写「Mock 第一版」字样,实际全部接口已是真实算法实现。架构以docs/实现说明.md为准,契约以docs/接口文档.md为准。
Commands
# ---- worker(本机)----
./run_worker.sh # 后台启动(0.0.0.0:8187,日志 worker.log)
FOREGROUND=1 ./run_worker.sh # 前台调试
./stop_worker.sh # 停止
sudo systemctl restart hair-worker # systemd 方式(生产)
journalctl -u hair-worker -f
# ---- 依赖服务(worker 之前要先起)----
sudo systemctl start comfyui # ComfyUI 127.0.0.1:8188(Flux-2 生图)
cd /home/ubuntu/change_hair && bash start_all.sh start # 换发型三件套 8801/32678/57860
# ---- 测试 ----
./venv/bin/pytest tests/ # 单元测试(conftest 会准备 fixtures)
./venv/bin/python -m hairline.comfyui tests/output/comfy_input.png # 手动跑一次生发工作流
依赖装在 ./venv(Python 3.10,torch 2.7.1+cu128,RTX 4090 CUDA 可用)。requirements.txt 里的 torch 2.2.2/5090 注释是旧机器的记录,本机 venv 实际是 cu128 版。
Architecture
接口一览(worker 侧路由,app.py)
| 接口 | 路径 | 实现位置 | 说明 |
|---|---|---|---|
| 1 | POST /api/v1/face/measure |
face_analysis/ |
四庭七眼测量 + 标注 PNG |
| 6 | POST /api/v1/face/measure-v2 |
同上(variant="v6") |
接口1 去顶庭变体 |
| 2 | POST /api/v1/hair/grow |
hairline/service.py |
C端生发。female 1..5 走 change_hair swapHair + Flux-2 整帧重绘(接口12 final 管线);female 6/7 与 male 全部走 ComfyUI add_hair.json inpaint |
| 7 | POST /api/v1/hair/grow-v2(hidden/deprecated) |
同上 | 同接口2,用 add_hair2.json 工作流 |
| 3 | POST /api/v1/hair/grow-b |
hairline/marker_detect.py |
B端马克笔划线生发(黑帽响应 + Dijkstra) |
| 4 | POST /api/v1/face/features |
worker 有实现;生产走网关本机(豆包) | 用户特征 |
| 5 | POST /api/v1/hairline/generate |
hairline/service.py |
发际线 PNG 三档(middle/high/low)+ 生发 |
| 9 | POST /api/v1/head/mask |
face_analysis/head_mask.py |
头发遮罩 + 分步可视化(BiSeNet/SegFormer 双路) |
| 10 | POST /api/v1/head/band |
face_analysis/head_band.py |
头部外缘膨胀带遮罩 |
| 11 | POST /api/v1/hairline/grow |
face_analysis/hairline_grow.py |
发际线生发(接口9 遮罩 + change_hair + 接缝融合),不含重绘 |
| 12 | POST /api/v1/hairline/grow_v2 |
同上 | 接口11 final + 发际线重绘带 → Flux-2 保色重绘 |
| 12f | POST /api/v1/hairline/grow_v2_final |
同上(_V2_FINAL_DEFAULTS) |
精简版:仅图片 + hairline_id,取整帧重绘 |
| 12f2 | POST /api/v1/hairline/grow_v2_final_v2 |
同上 | 同上,取 B 局部加发+全脸美颜 |
| 99 | POST /api/v1/face/special-points |
face_analysis/special_points.py(网关直接 import 本机实现 + _if99_lock 单路排队;worker 保留同路径备份) |
固定编号关键点 + 发际线特殊点(射线求交头发 mask)像素坐标 |
| 代理 | POST /api/swapHair/v1、/hairColor/v2 |
httpx 转发 127.0.0.1:8801 | 解决测试页 CORS |
| 工具 | POST /api/v1/redraw |
hairline/redraw.py |
直接 ComfyUI 重绘(图+遮罩+prompt) |
| 调试 | POST /api/v1/debug/grow-timing、GET /api/v1/debug/hairline_log |
— | 免鉴权,分步计时/下载日志 |
配套测试页在 static/test_interface{1..12}*.html,经 /static 挂载。
关键模式
- 统一信封:
ok(data)/err(code, message)构造{code, message, request_id, data};业务错误 HTTP 一律 200。错误码:1001 无人像 / 1002 分辨率低 / 1003 非正面 / 1004 gender 非法 / 1005 多人 / 1006 >1MB / 1007 参数或处理失败 / 1008 格式不支持。 - 图片三选一:每个接口
image_file(multipart) /image_url/image_base64(带data:前缀)严格互斥,由resolve_image_bytes()统一处理(0 个或多个 → 1007)。接口3 是marked_image_*。 - 阻塞算法进线程池:所有推理 handler 用
run_in_threadpool(...),避免卡事件循环。 - 内网鉴权:HTTP 中间件校验
/api/*的X-Internal-Token头,密码来自worker_config.json的accept_passwords(或环境变量WORKER_ACCEPT_PASSWORDS)。豁免前缀见_AUTH_EXEMPT(health/docs/static/debug/redraw/swapHair/hairColor)。 - 发型序号映射:
hairline/service.py的_HAIRSTYLE_ORDER显式定义 1..7 顺序(不靠文件名字母序);female 1..5 → change_hairchang_*映射在_FEMALE_KEY_TO_CHANG。 - 接口12 固化参数:
_V2_FINAL_DEFAULTS(app.py)是生产默认值(push=0.8cm / two_stage / color_match=关 等),接口2 female 重绘也复用它。
外部依赖(worker 视角)
| 依赖 | 地址 | 用途 | 配置 |
|---|---|---|---|
| ComfyUI | http://127.0.0.1:8188(COMFYUI_URL) |
Flux-2 生图/重绘,走 HTTP API(hairline/comfyui.py) |
Basic Auth admin,密码:COMFYUI_PASSWORD → worker_config.json.comfyui_password → password.txt |
| change_hair | http://127.0.0.1:8801(SWAP_HAIR_URL/HAIR_GROW_URL/CHANGE_HAIR_BASE) |
换发型(SD WebUI + LoRA)、hairGrow | 服务目录 /home/ubuntu/change_hair,start_all.sh 启停 |
ComfyUI 工作流 JSON 在仓库根:add_hair.json(接口2/3/5 生发)、add_hair2.json(接口7)、0716add-hair-api.json(接口12/2女 重绘)、hair_repaint.json。节点约定:26 输入图 / 6 seed / 60 提示词 / 16 unet(.gguf 自动切 UnetLoaderGGUF)/ 61 CLIP(4b↔9b 自动配套)。
性能/显存相关的坑
- 三个生发接口共用同一提示词字符串以命中 ComfyUI 的 CLIP 文本条件缓存——prompt 变了会触发 Flux(7.7G)+qwen CLIP(3.9G) 互相挤显存重载(每次 +4s)。
- 接口2 女性重绘管线默认把输入长边压到
REDRAW_MAX_SIDE(env,默认 1024)再放大回原尺寸;redraw_max_side=0表示不缩图。 comfyui.run(front=True)可插队(接口2 对时延敏感);超时COMFY_TIMEOUT(默认 600s)。
Deployment(本机 = gpu_worker)
systemd 单元(仓库根有副本,实装在 /etc/systemd/system/):
| 服务 | 端口 | 说明 |
|---|---|---|
comfyui.service |
127.0.0.1:8188 | ComfyUI/venv 的 main.py --cache-classic --fast |
hair-worker.service |
0.0.0.0:8187 | 本仓库 app:app(Wants comfyui + change_hair-hair) |
change_hair-{webui,photo,hair}.service |
57860 / 32678 / 8801 | conda envs sdwebui/py310/my_hair,依赖顺序 webui→photo→hair |
网关机另行部署 gateway/(config:workers 列表 + shared_password + ark key),见 gateway/config.example.json 与 docs/网关待改动.md。
Documentation
docs/接口文档.md— 对外 API 契约(唯一权威)docs/实现说明.md— 架构总览 + 各接口实现简述 + 部署要点docs/发际线增强算法.md、docs/发际线生发遮罩算法_pushed模式.md— 接口11/12 算法推导docs/gpu-worker改动说明.md、docs/网关待改动.md— worker↔网关 协同变更记录OFFLINE_ASSETS.md— 离线模型权重/字体清单