feat: 新增接口22生发控制与测试页
This commit is contained in:
+7
-7
@@ -17,16 +17,16 @@
|
||||
```
|
||||
|
||||
- **worker**(`app.py` + `face_analysis/` + `hairline/`):跑真正的算法,除 ComfyUI/change_hair 走本机
|
||||
回环外无外网依赖。对 `/api/*` 校验 `X-Internal-Token`(密码在 `worker_config.json` 的 `accept_passwords`);
|
||||
回环外无外网依赖。动态路由检查直连来源 IP;
|
||||
`/health` 模型就绪才返回 200。监听 **8187**(systemd `hair-worker.service`,也可 `./run_worker.sh` 手动起)。
|
||||
另有 worker 本机的工具路由:`/api/swapHair/v1`、`/hairColor/v2`(CORS 代理转发 change_hair:8801)、
|
||||
`/api/v1/redraw`(直接 ComfyUI 重绘)、`/api/v1/debug/*`(免Token调试,仍受IP限制)。
|
||||
`/api/v1/redraw`(直接 ComfyUI 重绘)、`/api/v1/debug/*`(调试,受 IP 白名单限制)。
|
||||
所有动态路径先校验直连来源IP:允许本机与网关`49.232.51.171`,其余返回HTTP403/code1010;`/static/`除外。配置见[`接口IP白名单.md`](接口IP白名单.md),网关侧不增加IP限制。
|
||||
- **网关**(`gateway/`):薄反向代理,健康检查/派发/鉴权/把 worker 的 `*_base64` 落盘改成 `*_url`。
|
||||
网关本机实现的接口(不转发 worker):**接口4**(调外网豆包)与**接口99**(纯 CPU 特殊点检测,
|
||||
直接 import 同仓库 `face_analysis/special_points`,`_if99_lock` 单路排队)。
|
||||
- **图片三选一**:所有接口图片入参 `image_file`/`image_url`/`image_base64` 严格三选一(接口3 是 `marked_image_*`)。
|
||||
- **响应信封**:`{code, message, request_id, data}`;业务错误用 `code`(HTTP 200);IP拒绝为HTTP403/code1010,Token错误为HTTP401/code1009,依赖健康异常为HTTP503。
|
||||
- **响应信封**使用 `{code, message, request_id, data}`。业务错误用 `code`(HTTP 200),Worker 拒绝来源 IP 时返回 HTTP 403 / code 1010,依赖健康异常时返回 HTTP 503。
|
||||
|
||||
### base64 → URL 映射(网关落盘改写,递归进数组)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|------|--------------------|----------|
|
||||
| 1 | `annotated_image_base64` | `annotated_image_url` |
|
||||
| 6 | `annotated_image_base64` | `annotated_image_url` |
|
||||
| 2 | `results[].image_base64` / `results[].grown_image_base64`(可空) | `results[].image_url` / `results[].grown_image_url` |
|
||||
| 2、22 | `results[].image_base64` / `results[].grown_image_base64`(可空) | `results[].image_url` / `results[].grown_image_url` |
|
||||
| 3 | `hair_growth_image_base64`(可空) | `hair_growth_image_url` |
|
||||
| 5 | `hairline_images[].image_{middle,high,low}_base64` / `grown_image_base64`(可空) | `hairline_images[].image_{middle,high,low}_url` / `grown_image_url` |
|
||||
| 52 | `hairline_images[].image_{middle,high,low}_base64` / `grown_image_base64`、`input_image_base64` / `original_image_base64` | 仅GPU worker,本次不新增网关路由;直接返回`*_base64` |
|
||||
@@ -52,7 +52,7 @@
|
||||
### 错误码
|
||||
|
||||
`1001` 无法识别人像 | `1002` 分辨率过低 | `1003` 非正面 | `1004` gender 必填/非法(接口2/5/7)|
|
||||
`1006` >1MB | `1007` 图片参数错误(0或多个)/未预期异常 | `1008` 格式不支持 | `1009` Token鉴权失败 | `1010` worker来源IP不允许或白名单配置无效。
|
||||
`1006` >1MB | `1007` 图片参数错误(0或多个)/未预期异常 | `1008` 格式不支持 | `1010` worker来源IP不允许或白名单配置无效。
|
||||
|
||||
---
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
### 接口52 发际线PNG生成 `/api/v1/hairline/generate-52`(worker)
|
||||
- **预处理**:`hairline/framing.py`先扶正9→152,再按高(3÷0.9)L、宽2.5L(3:4)做原像素裁切,人物相对原3L规则缩小约10%。L为9→152距离;眉心位置沿用已扶正参考图比例。长图的标准窗口不足时,求原图内最大的3:4窗口,通过可行区域约束保留全部人脸关键点,并尽量贴近目标眉心位置;无法保留人脸时回退仅扶正。其余图片空间不足时仍仅扶正、去除旋转空角。参考图与参数由`scripts/calibrate_hairline_framing.py`生成。公开接口为兼容旧调用默认关闭预处理,`normalize_image=true`开启;规则页显式开启,对比调试页默认开启。返回当前底图、原图及双向坐标矩阵。
|
||||
- **目标高度**:`上庭=(中庭+下庭)/2`,用当前底图测量的未取整像素坐标(眉心9、鼻翼下缘94、下巴152)算目标中心。测量失败时用发际线管线同编号点计算并标注来源。
|
||||
- **目标高度**:`上庭=(中庭+下庭)×0.45`,用当前底图测量的未取整像素坐标(眉心9、鼻翼下缘94、下巴152)算目标中心。测量失败时用发际线管线同编号点计算并标注来源。
|
||||
- **模板定位**:原有502点/贴图生成形状,`hairline/height_rule.py` 将每种 middle 曲线沿面部向上方向平移、校准中心 Y,保留曲线形状与倾斜。此规则仅接口52使用,接口2保持原逻辑。
|
||||
- **生发**:移动后的预览曲线直接生成黑线和重绘遮罩,再送ComfyUI,避免预览与黑模板位置不一致。
|
||||
- **裁切叠加预览**:测试页新增“裁切发际线叠加”。`static/hairline_reference_box.js`共享同一个162/389/9/152参考框几何,按原图像素裁切middle透明图层,再叠加到处理后人像;四栏顺序为原图、middle、裁切发际线叠加、生发图。
|
||||
@@ -153,7 +153,7 @@
|
||||
photo_service:32678(py310/kohya) → hair_service_sd:8801(my_hair),`start_all.sh start|stop|status`,
|
||||
也有 systemd `change_hair-{webui,photo,hair}.service`)。不跑时接口2女1..5、接口11/12、
|
||||
swapHair/hairColor 代理不可用。
|
||||
- `worker_config.json`(不入 git):`accept_passwords`(鉴权) + `comfyui_password`;鉴权头 `X-Internal-Token`。
|
||||
- `worker_config.json`(不入 git):`comfyui_password` 用于连接本机 ComfyUI。
|
||||
- systemd 单元:`hair-worker.service`(8187) / `comfyui.service`(8188),仓库根有副本;手动启停用
|
||||
`./run_worker.sh` / `./stop_worker.sh`。
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# GPU worker 接口 IP白名单
|
||||
|
||||
仅GPU worker使用项目根目录的`ip_access.json`,不存在时仅允许本机。网关不启用IP限制。配置每次请求重新读取,修改后立即生效。现有`X-Internal-Token`鉴权继续有效。
|
||||
仅GPU worker使用项目根目录的`ip_access.json`,不存在时仅允许本机。网关不启用IP限制。配置每次请求重新读取,修改后立即生效。Worker 动态接口以直连来源 IP 为准。
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -24,6 +24,6 @@ worker的所有动态路径统一保护,包括5号、52号、其他业务接
|
||||
|
||||
修改`allowed_ips`后保存为有效JSON即可生效,无需重启;建议写入同目录临时文件再原子替换,避免编辑过程中出现半份JSON。文件不存在时仅允许本机,内容损坏时动态接口一律拒绝。更新程序时保留本机配置文件。
|
||||
|
||||
IP检查先于Token检查。白名单内请求仍需原来的`X-Internal-Token`;`/health`和`/api/v1/debug/*`等原本免Token的路径仍需通过IP检查。静态页可打开不代表该来源可以调用API。
|
||||
Worker 动态路由通过来源 IP 白名单授权,包括 `/health` 和 `/api/v1/debug/*`。静态文件公开读取,调用 API 时以直连 IP 的检查结果为准。
|
||||
|
||||
验证命令:`./venv/bin/pytest tests/test_ip_access.py -q`,覆盖本机、精确白名单、CIDR、IPv6、转发头伪造、热更新、全接口拦截与网关不受限制。
|
||||
|
||||
+24
-9
@@ -17,6 +17,7 @@
|
||||
| 1 四庭七眼测量 | POST | `/api/v1/face/measure` |
|
||||
| 6 四庭七眼测量 v2 | POST | `/api/v1/face/measure-v2` |
|
||||
| 2 C 端生发 | POST | `/api/v1/hair/grow` |
|
||||
| 22 生发密度与发际线高度 | POST | `/api/v1/hair/grow-22` |
|
||||
| 3 B 端生发 | POST | `/api/v1/hair/grow-b` |
|
||||
| 72 B 端五个关键点 | POST | `/api/v1/face/b-end-points` |
|
||||
| 4 用户特征 | POST | `/api/v1/face/features` |
|
||||
@@ -91,7 +92,6 @@ GPU worker的所有动态接口均启用IP限制:允许本机及`ip_access.jso
|
||||
| 1006 | 文件超出大小限制 | 单文件超过 1 MB |
|
||||
| 1007 | 图片参数错误 | file / url / base64 未传,或同时传了多个(三者严格互斥) |
|
||||
| 1008 | 图片格式不支持 | 非 JPG / PNG |
|
||||
| 1009 | 未授权 | 缺少或错误的 `X-Internal-Token`(`/api/*` 路径鉴权) |
|
||||
| 1010 | 来源IP不允许 | 仅GPU worker:直连IP不在本机/白名单内,或来源/白名单配置无效;HTTP403 |
|
||||
|
||||
---
|
||||
@@ -363,6 +363,21 @@ GPU worker的所有动态接口均启用IP限制:允许本机及`ip_access.jso
|
||||
|
||||
---
|
||||
|
||||
## 接口 22 生发密度与发际线高度
|
||||
|
||||
`POST /api/v1/hair/grow-22` 使用接口 2 的图片输入、`gender`、`hair_style` 和返回结构。
|
||||
|
||||
| 表单字段 | 默认值 | 取值和效果 |
|
||||
| --- | --- | --- |
|
||||
| `hair_density` | 必填 | `sparse` 稀疏、`normal` 正常、`dense` 浓密。至少提交一档,多选时重复提交同名字段。提示词为「填充遮罩区域的头发,发量{描述},人物增加一点磨皮」,三档描述依次为「稀疏」「正常」「适中偏多」。 |
|
||||
| `hairline_height` | `0` | `[-1, 1]`,`-1` 最高,`0` 正常,`1` 最低。端点相对正常位置移动眉心到下巴垂直像素距离的 10%。非零时须启用 `use_mask`。 |
|
||||
|
||||
每个发型用一张正常高度贴图渲染预览,按请求高度移动预览线。女性 1–5 号在 WebUI 出图后移动贴回区域和重绘带。其他发型用移动后的预览曲线生成生发遮罩。
|
||||
|
||||
例如选择两档时,表单传 `hair_density=sparse` 和 `hair_density=dense`。每个「密度 × 发型」组合返回一项,结果的 `hair_density` 字段标明对应档位。
|
||||
|
||||
生发图与透明发际线 PNG 对齐到输入照片尺寸。测试页运行在本机 Worker 的 `/static/test_interface22.html`,扶正裁切预览依赖接口 52。
|
||||
|
||||
## 接口 3:B 端生发接口
|
||||
|
||||
**说明**:医生/操作端在用户照片上用马克笔标注目标发际线后,**只需上传这一张划线图**。系统检测划线 →
|
||||
@@ -476,7 +491,7 @@ GPU worker的所有动态接口均启用IP限制:允许本机及`ip_access.jso
|
||||
|
||||
Worker 直连时图片字段使用 `_base64` 后缀。检测不到人脸返回 `1001`,非正面或标注所需点位无法取得时返回 `1003`,图片无法解码返回 `1008`。
|
||||
|
||||
对外 42、52、72 的客户端登录、令牌验证和刷新均由 `main` 分支的网关处理。网关将 52 和 72 转发至本分支的 worker,worker 仍校验来源 IP 和网关注入的 `X-Internal-Token`。对外测试请使用网关同源的测试页。
|
||||
对外 42、52、72 的客户端登录、令牌验证和刷新均由 `main` 分支的网关处理。网关将 52 和 72 转发至本分支的 Worker,Worker 通过直连来源 IP 白名单授权。对外测试请使用网关同源的测试页。
|
||||
|
||||
---
|
||||
|
||||
@@ -659,7 +674,7 @@ Ft 两端保持横坐标并取平均高度,Zy 为 234 到 454 的距离。只
|
||||
|
||||
## 接口 52:发际线 PNG 生成接口
|
||||
|
||||
**说明**:入参同接口2(先选性别、再多选发型)。对每个选中发型返回一张 `middle` 设计叠图与生发图。**目标上庭 =(中庭 + 下庭)÷2**,从眉心向上定位;所有形状的中点对齐同一目标。`high` / `low` 已停用,旧字段保留为 `null`。
|
||||
**说明**:入参同接口2(先选性别、再多选发型)。对每个选中发型返回一张 `middle` 设计叠图与生发图。**目标上庭 =(中庭 + 下庭)×0.45**,从眉心向上定位;所有形状的中点对齐同一目标。`high` / `low` 已停用,旧字段保留为 `null`。
|
||||
|
||||
**请求**:`POST /api/v1/hairline/generate-52`(仅GPU worker,端口8187;本次不在网关新增路由)
|
||||
|
||||
@@ -769,12 +784,12 @@ Ft 两端保持横坐标并取平均高度,Zy 为 234 到 454 的距离。只
|
||||
"order": 1,
|
||||
"height_placement": {
|
||||
"old_center": {"x": 540.0, "y": 450.0},
|
||||
"new_center": {"x": 540.0, "y": 430.0},
|
||||
"shift_x_px": 0.0, "shift_y_px": -20.0, "target_error_px": 0.0
|
||||
"new_center": {"x": 540.0, "y": 461.0},
|
||||
"shift_x_px": 0.0, "shift_y_px": 11.0, "target_error_px": 0.0
|
||||
}
|
||||
}
|
||||
],
|
||||
"best_hairline_center_point": {"x": 540, "y": 430},
|
||||
"best_hairline_center_point": {"x": 540, "y": 461},
|
||||
"high_hairline_center_point": null,
|
||||
"low_hairline_center_point": null,
|
||||
"input_image_base64": "/9j/4AAQ...",
|
||||
@@ -783,14 +798,14 @@ Ft 两端保持横坐标并取平均高度,Zy 为 234 到 454 的距离。只
|
||||
"coordinate_space": "original_image",
|
||||
"framing": null,
|
||||
"height_rule": {
|
||||
"name": "court_average", "formula": "上庭 = (中庭 + 下庭) / 2",
|
||||
"name": "court_average", "formula": "上庭 = (中庭 + 下庭) × 0.45",
|
||||
"measurement_source": "hairline_landmarks",
|
||||
"brow_center": {"x": 540.0, "y": 740.0},
|
||||
"nose_bottom": {"x": 540.0, "y": 1050.0},
|
||||
"chin_tip": {"x": 540.0, "y": 1360.0},
|
||||
"middle_height_px": 310.0, "lower_height_px": 310.0,
|
||||
"target_upper_height_px": 310.0, "target_upper_height_cm": null,
|
||||
"target_center": {"x": 540.0, "y": 430.0}
|
||||
"target_upper_height_px": 279.0, "target_upper_height_cm": null,
|
||||
"target_center": {"x": 540.0, "y": 461.0}
|
||||
},
|
||||
"guide_landmarks": {
|
||||
"9": {"x": 540.0, "y": 740.0}, "150": {"x": 470.0, "y": 1340.0},
|
||||
|
||||
Reference in New Issue
Block a user