From 5f569caa5d402f1771d5dec1d0c5add5614cc682 Mon Sep 17 00:00:00 2001 From: xsl Date: Thu, 5 Nov 2026 22:25:55 +0800 Subject: [PATCH] =?UTF-8?q?docs(gateway):=20=E8=A1=A5=E5=85=85=E6=8E=A5?= =?UTF-8?q?=E5=8F=A31/6=20=E7=AB=96=E7=BA=BF=E5=9D=90=E6=A0=87=20vertical?= =?UTF-8?q?=5Flines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 网关继续盲转发,该字段是坐标 JSON,不参与 base64 改写。 Co-authored-by: Cursor --- docs/实现说明.md | 6 +++--- docs/接口文档.md | 51 +++++++++++++++++++++++++++++++++++++++++++++- docs/网关待改动.md | 11 ++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/docs/实现说明.md b/docs/实现说明.md index 04e0813..15da1f2 100644 --- a/docs/实现说明.md +++ b/docs/实现说明.md @@ -56,7 +56,7 @@ ## 2. 各接口实现简述 ### 接口1 四庭七眼测量 `/api/v1/face/measure`(worker) -- **做什么**:正面照 → 四庭(顶/上/中/下庭) + 七眼(眼宽/脸宽/间距) 的 cm 与占比、5 个关键点坐标、一张透明底标注 PNG。 +- **做什么**:正面照 → 四庭(顶/上/中/下庭) + 七眼(眼宽/脸宽/间距) 的 cm 与占比、5 个关键点坐标、竖线坐标 `vertical_lines`、一张透明底标注 PNG。 - **怎么实现**(`face_analysis/`):MediaPipe Face Mesh 468+虹膜点 → solvePnP 姿态校验(非正面 1003) → 虹膜直径法定标(px→cm) → **眉心以下实测**;**眉心以上**用 BiSeNet 头发分割取真实发际线/头顶(方案B), 失败回退比例推算(方案A,`hairline_source` 透出)。标注图 numpy 向量化渐变线 + 思源黑体。返回 `annotated_image_base64`。 @@ -65,8 +65,8 @@ ### 接口6 四庭七眼测量 v2 `/api/v1/face/measure-v2`(worker)—— 接口1 的去顶庭变体 - **做什么**:基于接口 1,**去顶庭**:不画头顶横线、不返回顶庭数据(`four_courts` 仅上/中/下庭,`landmarks` 无 `hair_top`,`face_total_height_cm` 为三庭之和)。 - **与接口1 的标注差异**(`create_annotated_image(variant="v6")`):①竖线范围改为发际线→下巴尖;②不画人头最左/最右端线(仅七眼 6 点 5 段,接口1 为 8 线 7 段);③左侧只标上/中/下庭。箭头/虚线/字体等与接口1 一致。 -- **怎么实现**:与接口 1 共用 `_face_measure_impl(variant="v6")`;v6 时标注走变体分支、数据由 app.py 边界删顶庭字段并重算三庭比例。 -- **网关改动**:新增路由 `POST /api/v1/face/measure-v2`,转发到 worker 同路径;base64→URL 改写无需改动。 +- **怎么实现**:与接口 1 共用 `_face_measure_impl(variant="v6")`;v6 时标注走变体分支、数据由 app.py 边界删顶庭字段并重算三庭比例。`vertical_lines` 与标注图共用 `vertical_line_geometry`:接口 6 的 y 范围是发际线到下巴尖,`lines` 只有 6 条内线。 +- **网关改动**:路由已有,盲转发。`vertical_lines` 是普通 JSON,不经过 base64→URL 改写。 ### 接口2 C端生发 `/api/v1/hair/grow`(worker)—— 预览 + 生发图 - **做什么**:正面照 + `gender`(必填) + `hair_style`(必填,逗号分隔多选,如 `1,2,3`) → 指定发际线类型 **N 组**:**预览图**(发际线叠在照片上) + **生发后图**(植发3个月效果)。 diff --git a/docs/接口文档.md b/docs/接口文档.md index a5ba891..2379d10 100644 --- a/docs/接口文档.md +++ b/docs/接口文档.md @@ -110,6 +110,7 @@ | four_courts | object | 四庭数据,见下表 | | seven_eyes | object | 七眼数据,见下表 | | landmarks | object | 关键分界点坐标(头顶 / 发际线 / 眉心 / 鼻翼下缘 / 下巴尖),原图像素坐标 | +| vertical_lines | object | 标注图竖线坐标,见下表。与标注 PNG 上的竖线同一套几何 | | left_position | object | MediaPipe 21 号关键点坐标(左脸定位点),原图像素:`{ "x": int, "y": int }` | | right_position | object | MediaPipe 251 号关键点坐标(右脸定位点,与 21 号镜像),原图像素:`{ "x": int, "y": int }` | @@ -139,7 +140,28 @@ | eye6 | number | 从左到右第 6 段宽度(cm):右眼外角 → 右脸颊 | | eye7 | number \| null | 从左到右第 7 段宽度(cm):右脸颊 → 人头最右(右耳外侧段)。该侧耳朵不可见时为 null | -> `eye1`~`eye7` 为从左到右共 7 段宽度,与标注图竖线一一对应。最左/最右端线取自耳朵分割外缘;某侧耳朵被头发或侧脸遮挡(不可见)时该侧端线省略,对应 `eye1` 或 `eye7` 为 `null`(键始终保留),实际有效段为 5 或 6 段。`eye3`/`eye5` 为左右眼宽、`eye4` 为两眼间距,与 `eye_width_cm` / `inter_eye_distance_cm` 语义一致。 +> `eye1`~`eye7` 为从左到右共 7 段宽度,与标注图竖线一一对应。最左/最右端线取自耳朵分割外缘;某侧耳朵被头发或侧脸遮挡(不可见)时该侧端线省略,对应 `eye1` 或 `eye7` 为 `null`(键始终保留),实际有效段为 5 或 6 段。`eye3`/`eye5` 为左右眼宽、`eye4` 为两眼间距,与 `eye_width_cm` / `inter_eye_distance_cm` 语义一致。`eye1`/`eye7` 的厘米数由 `vertical_lines` 里对应端点的 x 相减得到。 + +`vertical_lines`(竖线,原图像素,原点左上,x 向右,y 向下): + +| 字段 | 类型 | 说明 | +|------|------|------| +| y_top | int | 竖线共用上端。接口 1 在头顶之上,伸出 `max(6, round(短边 × 0.03))` 像素,可能略小于 0 | +| y_bottom | int | 竖线共用下端。接口 1 在下巴尖之下,伸出同样像素,可能略大于图高 | +| lines | array | 从左到右实际画出的竖线。每项 `{ "name": string, "x": int }` | + +`lines[].name` 从左到右: + +| name | 竖线 | +|------|------| +| left_head_edge | 人头最左(耳朵外缘)。该侧耳朵不可见时不出现,此时 `eye1` 为 `null` | +| left_cheek | 左脸颊 | +| left_eye_outer | 左眼外角 | +| left_eye_inner | 左眼内角 | +| right_eye_inner | 右眼内角 | +| right_eye_outer | 右眼外角 | +| right_cheek | 右脸颊 | +| right_head_edge | 人头最右(耳朵外缘)。该侧耳朵不可见时不出现,此时 `eye7` 为 `null` | ### 标注图片(UI)规范 @@ -183,6 +205,19 @@ "brow_center": { "x": 540, "y": 740 }, "nose_bottom": { "x": 540, "y": 1050 }, "chin_tip": { "x": 540, "y": 1360 } + }, + "vertical_lines": { + "y_top": 98, "y_bottom": 1410, + "lines": [ + { "name": "left_head_edge", "x": 120 }, + { "name": "left_cheek", "x": 210 }, + { "name": "left_eye_outer", "x": 310 }, + { "name": "left_eye_inner", "x": 420 }, + { "name": "right_eye_inner", "x": 560 }, + { "name": "right_eye_outer", "x": 670 }, + { "name": "right_cheek", "x": 780 }, + { "name": "right_head_edge", "x": 890 } + ] } } } @@ -214,6 +249,7 @@ | four_courts | object | 三庭数据(上/中/下庭,各含 cm 与 ratio;**无顶庭**) | | seven_eyes | object | 七眼数据(眼宽/脸宽/两眼间距 cm + 占比 ratios + **eye2~eye6** 共 5 段宽度) | | landmarks | object | 四个关键点像素坐标(发际线/眉心/鼻翼下缘/下巴尖) | +| vertical_lines | object | 标注图竖线坐标。结构同接口 1,但 `y_top`/`y_bottom` 为发际线到下巴尖(不伸出),`lines` 只有中间 6 条,无 `left_head_edge` / `right_head_edge` | | left_position | object | MediaPipe 21 号关键点坐标(左脸定位点),原图像素:`{ "x": int, "y": int }` | | right_position | object | MediaPipe 251 号关键点坐标(右脸定位点,与 21 号镜像),原图像素:`{ "x": int, "y": int }` | @@ -243,6 +279,17 @@ "brow_center": { "x": 540, "y": 740 }, "nose_bottom": { "x": 540, "y": 1050 }, "chin_tip": { "x": 540, "y": 1360 } + }, + "vertical_lines": { + "y_top": 430, "y_bottom": 1360, + "lines": [ + { "name": "left_cheek", "x": 210 }, + { "name": "left_eye_outer", "x": 310 }, + { "name": "left_eye_inner", "x": 420 }, + { "name": "right_eye_inner", "x": 560 }, + { "name": "right_eye_outer", "x": 670 }, + { "name": "right_cheek", "x": 780 } + ] } } } @@ -451,6 +498,7 @@ | four_courts | object | 四庭数据(顶/上/中/下庭 cm + 占比 ratios),结构同接口1 | | seven_eyes | object | 七眼数据(眼宽/脸宽/两眼间距 cm + 占比 ratios + eye1~eye7 从左到右 7 段宽度),结构同接口1 | | landmarks | object | 5 个纵向关键点像素坐标(hair_top/hairline/brow_center/nose_bottom/chin_tip),结构同接口1 | +| vertical_lines | object | 竖线坐标,结构同接口1 | | hairline_source | string | 发际线来源:`segmentation`(真实分割)/ `estimated`(比例估算)/ `corrected`(发际线贴近头顶、分割不可靠,已按 0.95×中庭反推修正) | | head_pose | object | 头部姿态角度(yaw/pitch/roll,单位:度) | | left_position | object | MediaPipe 21 号关键点坐标(左脸定位点),原图像素:`{ "x": int, "y": int }` | @@ -605,6 +653,7 @@ | four_courts | object | 四庭数据(顶/上/中/下庭 cm + 占比 ratios),结构同接口1 | | seven_eyes | object | 七眼数据(眼宽/脸宽/两眼间距 cm + 占比 ratios + eye1~eye7 从左到右 7 段宽度),结构同接口1 | | landmarks | object | 5 个纵向关键点像素坐标(hair_top/hairline/brow_center/nose_bottom/chin_tip),结构同接口1 | +| vertical_lines | object | 竖线坐标,结构同接口1 | | hairline_source | string | 发际线来源:`segmentation`(真实分割)/ `estimated`(比例估算)/ `corrected`(发际线贴近头顶、分割不可靠,已按 0.95×中庭反推修正) | | head_pose | object | 头部姿态角度(yaw/pitch/roll,单位:度) | | left_position | object | MediaPipe 21 号关键点坐标(左脸定位点),当前底图像素:`{ "x": int, "y": int }` | diff --git a/docs/网关待改动.md b/docs/网关待改动.md index 822597f..f4c413a 100644 --- a/docs/网关待改动.md +++ b/docs/网关待改动.md @@ -243,6 +243,17 @@ curl -s -X POST http://127.0.0.1:8080/api/v1/face/special-points \ --- +## 10. ✅【无需改代码】接口1/6 增加竖线坐标 `vertical_lines` + +worker(`ubuntu3090_debug`,提交 `b64313d`)已在接口 1、接口 6,以及接口 5/52 内嵌的 `face_measure` 中返回 `vertical_lines`。字段说明见 [`接口文档.md`](接口文档.md) 接口 1。 + +网关保持现有盲转发即可: + +- `vertical_lines` 是坐标 JSON,不是 `*_base64`,`rewrite_base64_to_url` 不会改写它。 +- 接口 1 含人头两端 `left_head_edge` / `right_head_edge`(耳朵不可见时该条不出现)。接口 6 只有中间 6 条,`y_top`/`y_bottom` 为发际线到下巴尖。 + +--- + ## 已经做好、无需再动的 - **接口4 在网关本机实现**(调豆包,不转发 worker)——已完成;config 里配 `ark`。