feat(接口99): 人脸特殊点检测 + 网关转发路由;文档同步至当前代码

- worker: POST /api/v1/face/special-points,60 个固定编号关键点(6组)
  + 5 个发际线特殊点(沿指定关键点方向射线步进,与 BiSeNet 头发 mask 首次相交;
  双射线取中点,left_left=mid(333→332, 298→284) 与右侧镜像)
- 网关: 同路径盲转发路由(调用频率低,维持转发不占网关依赖;
  响应无 *_base64 字段,改写逻辑天然跳过)
- 测试页 static/test_interface99.html:分组着色小点 + 点ID标注(可开关)
  + 发际线点大圆标签 + JSON 展示
- 文档: CLAUDE.md 重写至当前架构;实现说明/接口文档补接口 9~12/99 契约;
  网关待改动补 §7(9/10已加) §8(11/12待加) §9(接口99部署步骤)
This commit is contained in:
xsl
2026-09-06 23:32:24 +08:00
parent 59893788a1
commit 0b02ad1693
8 changed files with 900 additions and 83 deletions
+77 -1
View File
@@ -70,7 +70,7 @@ async def hair_grow_v2(request: Request):
|------|------|------|------|
| image_file / image_url / image_base64 | — | **三选一** | 用户正面照 |
| gender | string | **是** | `male` / `female` |
| hair_style | string | **是** | 发型序号,**逗号分隔多选**(如 `1,2,3`)。female: 1–5,male: 1–4 |
| hair_style | string | **是** | 发型序号,**逗号分隔多选**(如 `1,2,3`)。female / male 均为 1–7 |
| beauty_enabled | bool | 否 | 美颜开关(本期不生效) |
| use_mask | bool | 否 | 默认 `true`,`false` 跳过遮罩 |
| prompt | string | 否 | ComfyUI 提示词 |
@@ -135,6 +135,82 @@ async def face_measure_v2(request: Request):
---
## 7. ✅【已完成】接口9/10 头部遮罩(`/api/v1/head/mask`、`/api/v1/head/band`)
`gateway/app.py` 已新增两条盲转发路由(同接口6 的写法):
```python
@app.post("/api/v1/head/mask", tags=["人脸分析"])
@app.post("/api/v1/head/band", tags=["人脸分析"])
```
- 入参:图片三选一 + `erode_cm`(接口10 另有 `dilate_cm`)。
- 出参:`steps_common.*_base64` 与 `bisenet`/`segformer` 组内的 `*_base64` 字段——现有递归
`rewrite_base64_to_url` 已覆盖(嵌套 dict/数组),无需额外改动。
- 两个接口响应较慢(双分割模型跑两遍),注意 `request_timeout_seconds`。
---
## 8. 🔲【新增】接口11/12 发际线生发/带重绘(`/api/v1/hairline/grow*`)
worker 侧已上线 4 条路由,**网关尚无对应转发路由**(当前仅测试页直连 worker)。如需对外提供,参考接口9 加法:
```python
@app.post("/api/v1/hairline/grow", tags=["生发"]) # 接口11
@app.post("/api/v1/hairline/grow_v2", tags=["生发"]) # 接口12 全参数
@app.post("/api/v1/hairline/grow_v2_final", tags=["生发"]) # 接口12 final 精简版
@app.post("/api/v1/hairline/grow_v2_final_v2", tags=["生发"])
```
- 入参:图片三选一 + `hairline_id`(接口12 final 两变体仅需这两个)。接口11/12 全参数版还有
20+ 个调优 Form 参数,网关盲转发即可,无需逐个声明。
- 出参:`data.steps.*_base64`(input/final/redraw_full/redraw_band 等各步图)→ 现有递归改写覆盖。
- ⚠️ 依赖 worker 机的 change_hair(:8801) 与 ComfyUI(:8188) 都在跑;单请求 10s+,超时建议 ≥120s。
- 契约细节见 [`接口文档.md`](接口文档.md) 接口11/12 章节。
---
## 9. ✅【新增】接口99 人脸特殊点(`/api/v1/face/special-points`)
**背景**:worker 侧新增接口99——固定编号 MediaPipe 关键点(6 组 60 点)+ 5 个发际线特殊点,
返回原图像素坐标 JSON(无图片字段)。
**网关代码已在本仓库 `gateway/app.py` 改好**(同接口1 的盲转发写法,pull 后重启即生效):
```python
@app.post("/api/v1/face/special-points", tags=["人脸分析"])
async def face_special_points(request: Request):
"""接口99:人脸特殊点检测(固定关键点 + 发际线特殊点像素坐标)"""
return await _proxy(request, "/api/v1/face/special-points")
```
**网关机部署/调试步骤**:
```bash
cd /path/to/hair && git pull # 拉取本分支
sudo systemctl restart hair-gateway # 或项目实际的网关服务名(uvicorn gateway.app:app)
# 验证路由已注册(应能在输出里看到 /api/v1/face/special-points)
curl -s http://127.0.0.1:8080/openapi.json | python3 -m json.tool | grep special-points
# 冒烟:经网关调一次(multipart 传图,无需 X-Internal-Token,网关会注入)
curl -s -X POST http://127.0.0.1:8080/api/v1/face/special-points \
-F "image_file=@/tmp/face.jpg" | python3 -m json.tool | head -30
```
**无需额外改动**:
- 请求:multipart/form-data,仅图片三选一参数,盲转发即可。
- 响应:`data.points` 全是坐标(`{"x":..,"y":..}`),**没有任何 `*_base64` 字段**,
现有 `rewrite_base64_to_url` 递归改写天然跳过,原样返回。
- 性能:纯本地推理(MediaPipe + BiSeNet,秒级),不依赖 ComfyUI/change_hair,
`request_timeout_seconds` 无需调大。
**注意**:
- worker 侧测试页 `static/test_interface99.html` 是 worker 本机直连的(带 token 头),
经网关调试请用 curl 或网关 `/docs`(网关只盲转发 `/api/*`,不代理 worker 的 `/static`)。
- 契约详见 [`接口文档.md`](接口文档.md) 接口99 章节;worker 错误码 1001(无人脸)/1003(非正面)
/1007/1008 原样透传。
---
## 已经做好、无需再动的
- **接口4 在网关本机实现**(调豆包,不转发 worker)——已完成;config 里配 `ark`。