Files
hair/CLAUDE.md
T
xsl 8b7bfe2023 feat(接口99): 网关改本机实现(纯CPU+单路排队锁),文档同步
- 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 通过
2026-09-06 23:37:01 +08:00

104 lines
8.0 KiB
Markdown
Raw 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.
# 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` 的 FastAPI `description` 和 `docs/接口文档.md` 部分示例仍写「Mock 第一版」字样,实际全部接口已是真实算法实现。**架构以 `docs/实现说明.md` 为准,契约以 `docs/接口文档.md` 为准。**
## Commands
```bash
# ---- 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_hair `chang_*` 映射在 `_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` — 离线模型权重/字体清单