1032 lines
59 KiB
Markdown
1032 lines
59 KiB
Markdown
# 旷视五个接口 — 接口文档(输入 / 输出定义)
|
||
|
||
> 本文档依据《旷视具体需求.docx》整理,描述各接口的**输入**与**输出**。
|
||
> 文中标记为 **【待确认】** 的内容为需求文档中尚未明确、需与对方对齐的点。
|
||
|
||
> 下文示例用于说明响应结构。接口52由网关转发至GPU worker执行真实计算,具体接入与部署见[接口52网关接入](接口52网关接入.md)。
|
||
|
||
---
|
||
|
||
## 服务地址与接口路径
|
||
|
||
- **Base URL**:`https://hair.xiangsilian.com`
|
||
- **在线接口文档(Swagger)**:`https://hair.xiangsilian.com/docs`
|
||
|
||
| 接口 | 方法 | 路径 |
|
||
|------|------|------|
|
||
| 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` |
|
||
| 4 用户特征 | POST | `/api/v1/face/features` |
|
||
| 42 用户特征与两项判定 | POST | `/api/v1/face/features-42` |
|
||
| 72 B 端五个关键点 | POST | `/api/v1/face/b-end-points` |
|
||
| 5 发际线 PNG 生成 | POST | `/api/v1/hairline/generate` |
|
||
| 52 发际线新规则 | POST | `/api/v1/hairline/generate-52` |
|
||
|
||
---
|
||
|
||
## 通用约定
|
||
|
||
以下约定适用于全部 5 个接口(最终以联调为准)。
|
||
|
||
### 请求
|
||
|
||
| 项目 | 约定 |
|
||
|------|------|
|
||
| 协议 | HTTPS |
|
||
| 请求方式 | `POST` |
|
||
| 图片传参方式 | 三选一,三种都支持:① `multipart/form-data` 上传文件、② 图片 URL、③ base64 字符串 |
|
||
| 字符编码 | UTF-8 |
|
||
| 坐标基准 | 所有返回的坐标均以**原图像素**为基准(原点为图片左上角,x 向右、y 向下) |
|
||
|
||
图片传参字段(三者**严格互斥**,必须且只能传其一;传了 0 个或 ≥2 个均报错):
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| image_file | file | 上传文件(`multipart/form-data`) |
|
||
| image_url | string | 图片 URL |
|
||
| image_base64 | string | 图片 base64 字符串,**需带前缀**,如 `data:image/png;base64,xxxx` |
|
||
|
||
### 图片输入要求(接口 1/2/3/4/5 通用)
|
||
|
||
| 项目 | 要求 |
|
||
|------|------|
|
||
| 图片类型 | 用户正面照(人像) |
|
||
| 人脸数量 | 默认仅支持**单人**;若检测到 2 或 3 张人脸(即多人),报错并提示客户端 |
|
||
| 单文件大小 | ≤ 1 MB |
|
||
| 最低分辨率 | 720 × 1024 |
|
||
| 最大分辨率 | 2160 × 3840 |
|
||
| 格式 | 目前仅支持 JPG / PNG |
|
||
|
||
### 统一响应结构
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "唯一请求标识",
|
||
"data": { }
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| code | int | 0 表示成功,非 0 表示失败 |
|
||
| message | string | 结果描述 / 错误原因 |
|
||
| request_id | string | 请求追踪 ID |
|
||
| data | object | 业务数据,结构见各接口 |
|
||
|
||
### 通用错误码(识别失败时返回,见需求文档)
|
||
|
||
| code | message | 说明 |
|
||
|------|---------|------|
|
||
| 1001 | 无法识别人像 | 图片中未检测到人脸 |
|
||
| 1002 | 人像分辨率过低 | 低于最低分辨率要求 |
|
||
| 1003 | 角度问题,非正面照 | 非正面 / 角度过大 |
|
||
| 1004 | gender 必填/非法 | 接口2/5 的 `gender` 缺失或非 `male`/`female` |
|
||
| 1005 | 检测到多张人脸 | 默认仅支持单人,检测到 2 人或以上时返回 |
|
||
| 1006 | 文件超出大小限制 | 单文件超过 1 MB |
|
||
| 1007 | 图片参数错误 | file / url / base64 未传,或同时传了多个(三者严格互斥) |
|
||
| 1008 | 图片格式不支持 | 非 JPG / PNG |
|
||
| 1009 | 未授权 | worker内部`X-Internal-Token`错误;网关42/52/72号客户端 Bearer 令牌缺失、无效或过期。HTTP401 |
|
||
|
||
---
|
||
|
||
## 接口 1:四庭七眼测量标注接口
|
||
|
||
**说明**:输入用户照片,输出一张标注好四庭七眼数据的 PNG 图片(**不包含人物本体,仅标注图层**),同时返回四庭、七眼的比例与厘米数值。
|
||
|
||
**请求**:`POST /api/v1/face/measure`
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」(`image_file` / `image_url` / `image_base64` 三选一)。本接口无其他专属参数。
|
||
|
||
### 输出(data)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| annotated_image_url | string | 标注好数据的 PNG 图片(仅标注图层,不含人物) |
|
||
| face_total_height_cm | number | 全脸总高度(cm) |
|
||
| 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 }` |
|
||
|
||
`four_courts`(四庭,自上而下):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| top_court_cm | number | 顶庭长度(cm),示例:约占全脸高度 22% |
|
||
| upper_court_cm | number | 上庭长度(cm),约占 25% |
|
||
| middle_court_cm | number | 中庭长度(cm),约占 28% |
|
||
| lower_court_cm | number | 下庭长度(cm),约占 25% |
|
||
| ratios | object | 各庭占全脸高度的百分比 |
|
||
|
||
`seven_eyes`(七眼,横向):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| eye_width_cm | number | 单眼宽度(cm) |
|
||
| face_width_cm | number | 脸宽(cm) |
|
||
| inter_eye_distance_cm | number | 两眼间距(cm) |
|
||
| ratios | object | 七眼各段占脸宽的比例 |
|
||
| eye1 | number \| null | 从左到右第 1 段宽度(cm):人头最左 → 左脸颊(左耳外侧段)。该侧耳朵不可见时为 null |
|
||
| eye2 | number | 从左到右第 2 段宽度(cm):左脸颊 → 左眼外角 |
|
||
| eye3 | number | 从左到右第 3 段宽度(cm):左眼外角 → 左眼内角(左眼宽度) |
|
||
| eye4 | number | 从左到右第 4 段宽度(cm):左眼内角 → 右眼内角(两眼间距) |
|
||
| eye5 | number | 从左到右第 5 段宽度(cm):右眼内角 → 右眼外角(右眼宽度) |
|
||
| 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` 的厘米数由 `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)规范
|
||
|
||
| 项目 | 要求 |
|
||
|------|------|
|
||
| 颜色 | 字体及所有线/箭头 `#FFFFFF` 100%,透明底 |
|
||
| 尺寸 | 字号/线宽/虚线/箭头按图片**短边自适应缩放**(非固定 pt) |
|
||
| 横线 | 5 条分界线(头顶/发际线/眉心/鼻翼下缘/下巴尖),两端渐变消失并略超出最外侧竖线;线名在线**右上方** |
|
||
| 竖线 | 人头最左 + 七眼 6 点 + 人头最右(最外两条取自头发分割轮廓),两端渐变消失并略超出头顶/下巴 |
|
||
| 数值排布 | 四庭数值(名 + 数值两行,**不带 cm**)统一在图片**左侧**呈现;七眼段宽**上下穿插**展示;底部统一标「单位cm」 |
|
||
| 线样式 | 段宽/庭高用**虚线 + 实心三角双箭头**标示(箭头尖端落在虚线两端) |
|
||
|
||
### 响应示例(当前 Mock 返回值)
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"annotated_image_url": "https://hair.xiangsilian.com/static/sample.jpg",
|
||
"face_total_height_cm": 13.76,
|
||
"four_courts": {
|
||
"top_court_cm": 3.44,
|
||
"upper_court_cm": 3.44,
|
||
"middle_court_cm": 3.44,
|
||
"lower_court_cm": 3.44,
|
||
"ratios": { "top_court": 0.25, "upper_court": 0.25, "middle_court": 0.25, "lower_court": 0.25 }
|
||
},
|
||
"seven_eyes": {
|
||
"eye_width_cm": 3.44,
|
||
"face_width_cm": 24.08,
|
||
"inter_eye_distance_cm": 3.44,
|
||
"ratios": { "eye_width": 0.143, "inter_eye_distance": 0.143 },
|
||
"eye1": 3.44, "eye2": 3.44, "eye3": 3.44, "eye4": 3.44,
|
||
"eye5": 3.44, "eye6": 3.44, "eye7": 3.44
|
||
},
|
||
"landmarks": {
|
||
"hair_top": { "x": 540, "y": 120 },
|
||
"hairline": { "x": 540, "y": 430 },
|
||
"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 }
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 接口 6:四庭七眼测量 v2 接口
|
||
|
||
**说明**:基于[接口 1](#接口-1四庭七眼测量标注接口)的变体。与接口 1 的差异:
|
||
|
||
- **去顶庭**:不画头顶横线、不返回顶庭数据。`four_courts` 仅含上/中/下庭,`landmarks` 无 `hair_top`,`face_total_height_cm` 为三庭之和(不含顶庭)。
|
||
- **竖线范围**:纵向竖线从**发际线**画到**下巴尖**(接口 1 为头顶→下巴尖)。
|
||
- **不画人头最左/最右端线**:仅画七眼 6 点(左脸颊/左眼外角/左眼内角/右眼内角/右眼外角/右脸颊)共 5 段标尺,不取头发轮廓的头部端线(接口 1 会多出最左/最右 2 条头部端线、共 7 段)。
|
||
- 其余(实心三角箭头、虚线样式、字体、单位cm、七眼数据)与接口 1 一致。
|
||
|
||
**请求**:`POST /api/v1/face/measure-v2`
|
||
|
||
### 输入
|
||
|
||
与接口 1 完全相同。图片参数见「通用约定 → 图片传参字段」(`image_file` / `image_url` / `image_base64` 三选一)。本接口无其他专属参数。
|
||
|
||
### 输出(data)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| annotated_image_url | string | 标注图层 PNG URL(透明底,仅标注线/文字,不含人物) |
|
||
| face_total_height_cm | number | 面部总高度(cm)= 上庭 + 中庭 + 下庭(**不含顶庭**) |
|
||
| 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 }` |
|
||
|
||
> 接口6 是**三庭五眼**:`four_courts`/`landmarks` 不含顶庭与头顶点(无 `top_court_cm`/`hair_top`);`seven_eyes` 只含 **eye2~eye6**(左脸颊/左眼/两眼间距/右眼/右脸颊,5 段),**无 eye1/eye7**(耳外段需头发轮廓端线,仅接口1 有)。
|
||
|
||
### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"annotated_image_url": "https://hair.xiangsilian.com/static/sample.jpg",
|
||
"face_total_height_cm": 10.32,
|
||
"four_courts": {
|
||
"upper_court_cm": 3.44, "middle_court_cm": 3.44, "lower_court_cm": 3.44,
|
||
"ratios": { "upper_court": 0.333, "middle_court": 0.333, "lower_court": 0.333 }
|
||
},
|
||
"seven_eyes": {
|
||
"eye_width_cm": 3.44, "face_width_cm": 24.08, "inter_eye_distance_cm": 3.44,
|
||
"ratios": { "eye_width": 0.143, "inter_eye_distance": 0.143 },
|
||
"eye2": 3.0, "eye3": 3.44, "eye4": 3.44, "eye5": 3.44, "eye6": 3.0
|
||
},
|
||
"landmarks": {
|
||
"hairline": { "x": 540, "y": 430 },
|
||
"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 }
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 接口 2:C 端生发接口
|
||
|
||
**说明**:输入用户正面照 + 性别 + 发型序号(可多选),按指定发际线类型渲染发际线曲线透明 PNG + 生发图。
|
||
|
||
> **每个方案返回两张图**:`image_url`=「发际线曲线**透明 PNG**(仅白色曲线,透明底,需叠加原图显示)」;`grown_image_url`=
|
||
> 经 ComfyUI/Flux 的「植发 3 个月**生发后图片**」(完整人像照片)。两者均已实现,实现简述见 [`实现说明.md`](实现说明.md)。
|
||
|
||
**请求**:`POST /api/v1/hair/grow`
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」。专属参数:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| gender | string | **是** | 性别:`male` / `female`。决定使用的发际线贴图集合 |
|
||
| hair_style | string | **是** | 发型序号,**逗号分隔多选**(如 `1,2,3`),最多不超过该性别的预设数。female:1=ellipse, 2=flower, 3=heart, 4=straight, 5=wave, 6=bigflower, 7=clasicalflower;male:1=ellipse, 2=inverse_arc, 3=m, 4=straight, 5=heart, 6=Softpetal, 7=wave。越界/非法返回 `1007` |
|
||
| beauty_enabled | bool | 否 | 生发图是否带美颜效果,默认 false(当前阶段不生效) |
|
||
| use_mask | bool | 否 | 是否启用 inpaint 遮罩,默认 `true`。`false` 时用干净原图生成(空遮罩、不烧模板黑线),供测试对比。仅对原生发(male / female 6/7)管线生效 |
|
||
| prompt | string | 否 | ComfyUI 提示词,默认「填充遮罩区域的头发,人物增加一点磨皮」,会替换工作流节点 60 的文本。所有分支生效(male / female 6/7 原生生发,female 1..5 换发型后的发际线带重绘;不进 change_hair)。注:非默认提示词会使 ComfyUI 的 CLIP 条件缓存失效,换词后首次请求明显变慢(实测 45s vs 命中缓存 8s),相同提示词的后续请求恢复正常 |
|
||
| flux_model | string | 否 | Flux 模型文件名(如 `flux-2-klein-9b-Q5_K_M.gguf`)。缺省用工作流默认(9B-Q4_K_M);`.gguf` 自动切 UnetLoaderGGUF 并同步配套 qwen CLIP |
|
||
| redraw_max_side | int | 否 | female 1..5 重绘管线送模型前的长边上限像素。缺省=服务端默认(`REDRAW_MAX_SIDE`,当前 1024);`0`=不缩图(原图直送,更慢) |
|
||
|
||
### 输出(data)
|
||
|
||
`results`:发际线方案数组,**数量 = 所选发型数**。每个元素:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| image_url | string | 发际线曲线**透明 PNG** URL(仅白色发际线曲线,透明底,**不含人物**,需前端叠加原图显示) |
|
||
| grown_image_url | string | **生发后图片** URL(ComfyUI/Flux「植发 3 个月」效果图,完整人像照片) |
|
||
| hairline_type | string | 发际线类型 key:`ellipse`/`flower`/`heart`/`straight`/`wave`/`bigflower`/`clasicalflower`(female),`ellipse`/`m`/`straight`/`inverse_arc`/`heart`/`Softpetal`/`wave`(male) |
|
||
| order | int | 排序序号(当前阶段固定 `1..N`,按贴图顺序,暂不计算合适度) |
|
||
|
||
> ⚠️ 生发图由本机 ComfyUI(Flux-2,端口 8188)生成,**一次请求生成指定发型的 1 张、同步返回**;
|
||
> female 1..5 的生发图另依赖 change_hair 换发型服务(:8801)。
|
||
> worker 侧返回 `image_base64` / `grown_image_base64`,
|
||
> 网关落盘后改写为上表的 `image_url` / `grown_image_url`。
|
||
>
|
||
> 💡 `image_url` 为透明底 PNG,前端需用绝对定位叠加到原图上显示(参考[测试页](https://hair.xiangsilian.com/static/test_interface2.html)的 `.img-stack` 叠加结构)。
|
||
|
||
### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"results": [
|
||
{ "image_url": "https://hair.xiangsilian.com/static/annotations/uuid1.png", "grown_image_url": "https://hair.xiangsilian.com/static/annotations/grown1.png", "hairline_type": "ellipse", "order": 1 },
|
||
{ "image_url": "https://hair.xiangsilian.com/static/annotations/uuid2.png", "grown_image_url": "https://hair.xiangsilian.com/static/annotations/grown2.png", "hairline_type": "flower", "order": 2 }
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
> 识别失败时返回通用错误码(1001 / 1002 / 1003 等)。`gender` 缺失或非法值返回 **1004**;本接口已改为必填入参,不再自动判别性别。
|
||
|
||
---
|
||
|
||
## 接口 22 生发密度与发际线高度
|
||
|
||
`POST /api/v1/hair/grow-22` 需要现有对接账号签发的 `Authorization: Bearer <access_token>`。 [账号登录与刷新](接口42-52-72网关鉴权.md)
|
||
|
||
表单图片字段三选一,`gender` 为 `female` 或 `male`,`hair_style` 用逗号分隔发型序号(两种性别均为 `1–7`)。
|
||
|
||
| 字段 | 取值 |
|
||
|---|---|
|
||
| `hair_density` | 必填,至少一档。稀疏 `sparse`、正常 `normal`、浓密 `dense`。多选时重复提交同名字段。 |
|
||
| `hairline_height` | 默认 `0`,范围 `[-1, 1]`。负值提高发际线,正值降低发际线。 |
|
||
|
||
每个「发型 × 密度」组合返回一项,包含 `order`、`hairline_type`、`hair_density`、发际线透明 PNG `image_url` 和生发图 `grown_image_url`。单项生发失败时保留 `grown_image_base64: null`。
|
||
|
||
[网关测试页](/static/test_interface22.html)登录后可直接生图。首次生成使用默认高度 `0`,出图后通过同源接口 52 加载发际线预览,再调整形状、高度和密度。
|
||
|
||
## 接口 3:B 端生发接口
|
||
|
||
**说明**:医生/操作端在用户照片上用马克笔标注目标发际线后,**只需上传这一张划线图**。系统检测划线 →
|
||
据此生成生发后图片。
|
||
|
||
**请求**:`POST /api/v1/hair/grow-b`
|
||
|
||
### 输入
|
||
|
||
划线图片支持「文件 / URL / base64」三选一(字段:`marked_image_file` / `marked_image_url` / `marked_image_base64`)。
|
||
**不需要原始照片**(划线图本身就是用户照片 + 手绘线)。
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| marked_image_* | file / string | 是 | 已用马克笔标注发际线的图片,三选一 |
|
||
| use_mask | bool | 否 | 是否画发际线,默认 `true`。`false` 时跳过划线检测、直接送划线图,模型仅凭手绘黑线生发,供测试对比 |
|
||
| prompt | string | 否 | ComfyUI 提示词,默认「补充遮罩区域的头发,加一点美颜」,会替换工作流节点 60 的文本 |
|
||
|
||
### 输出(data)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| hair_growth_image_url | string | **生发后图片**(检测划线 → ComfyUI/Flux「植发 3 个月」效果)。worker 返回 `hair_growth_image_base64` |
|
||
| hairline_type | string | 发际线形,手绘为定制,固定 `"custom"` |
|
||
|
||
> 未检测到划线(或无人脸)返回 **1001**。
|
||
|
||
### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"hair_growth_image_url": "https://hair.xiangsilian.com/static/annotations/grown.png",
|
||
"hairline_type": "custom"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 接口 4:用户特征接口
|
||
|
||
**说明**:输入用户照片,由**火山方舟 豆包视觉模型**(`doubao-seed-1-6-vision`)分析,输出一大批面部特征。
|
||
|
||
**请求**:`POST /api/v1/face/features`
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」。本接口无其他专属参数。
|
||
|
||
### 输出(data)
|
||
|
||
`data.features` 是一个 **JSON 字符串**(不是对象,客户端 `JSON.parse()` 后用),**仅含以下 7 个英文字段**:
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| face_shape | 脸形(脸型)|
|
||
| eyebrow_shape | 眉形 |
|
||
| facial_age | 面部年龄(区间字符串,如"18-25岁")|
|
||
| dynamic_static_type | 动静类型(静态型/动态型)|
|
||
| gender | 性别(男/女)|
|
||
| gene_style | 基因风格(如"自然型")|
|
||
| contour_type | 轮廓类型(轮廓偏曲/轮廓适中/轮廓偏直)|
|
||
|
||
> 无人脸返回 `1001`(据 doubao「图片是否有人脸」判定)。⚠️ 本接口是唯一调**外网云模型**的接口,
|
||
> worker 需可访问 `ark.cn-beijing.volces.com`;API Key 走 worker 配置/环境变量。
|
||
|
||
### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"features": "{\"face_shape\":\"鹅蛋脸\",\"eyebrow_shape\":\"平眉\",\"facial_age\":\"18-25岁\",\"dynamic_static_type\":\"静态型\",\"gender\":\"女\",\"gene_style\":\"少年型\",\"contour_type\":\"轮廓适中\"}"
|
||
}
|
||
}
|
||
```
|
||
|
||
> `features` 为字符串形式的 JSON,固定上述 7 个字段。
|
||
|
||
---
|
||
|
||
## 接口 72:B 端五个关键点
|
||
|
||
`POST /api/v1/face/b-end-points` 接受三选一图片参数 `image_file`、`image_url`、`image_base64`。复用接口 52 的扶正裁切与虹膜/眼宽厘米估算,返回坐标以处理后底图为准。使用[客户端 Bearer 鉴权](接口42-52-72网关鉴权.md)。
|
||
|
||
| 点号 | 位置 | 标注 |
|
||
|---|---|---|
|
||
| 1 | MediaPipe 4 | 白点 |
|
||
| 2 | HL center | 白点 |
|
||
| 3 | HL center 向画面右侧水平偏移 2 cm | 白点 |
|
||
| 4 | HL left | 白点 |
|
||
| 5 | HL left left | 白点 |
|
||
| 6 | MediaPipe 446 | 白点,兼作射线锚点 |
|
||
|
||
六个白点的直径相同。标注层包含三条从起点向目标方向延伸并逐渐变淡的白色虚线(2→1、4→6、5→6),以及 2→3 白色虚线段。2→1 射线比默认可见长度短 15%,5→6 射线的绘制长度比默认长度多 30%,可见范围以图像边界为限。四个距离文字分别放在 2–1 中点左侧、2–3 中点上方、4–6 中点左侧和 5–6 中点右侧,距离保留一位小数,2–3 固定显示 `2.0 CM`。
|
||
|
||
| `data` 字段 | 内容 |
|
||
|---|---|
|
||
| `input_image_url` | 扶正裁切后的底图 |
|
||
| `annotated_image_url` | 与底图同尺寸的透明 PNG 标注层 |
|
||
| `points` | 按字符串点号 `"1"` 到 `"6"` 索引的像素坐标 `{x, y}` |
|
||
| `measurements_cm` | 四个距离,以 `2-1`、`2-3`、`4-6`、`5-6` 索引 |
|
||
| `image_size` | 处理后尺寸 `{width, height}` |
|
||
| `coordinate_space` | `processed_image` |
|
||
| `px_per_cm` | 估算的每厘米像素数 |
|
||
|
||
Worker 直连时图片字段使用 `_base64` 后缀。检测不到人脸返回 `1001`,非正面或标注所需点位无法取得时返回 `1003`,图片无法解码返回 `1008`。
|
||
|
||
---
|
||
|
||
## 42号接口-加两个特征点
|
||
|
||
**请求** `POST /api/v1/face/features-42`,使用 `multipart/form-data`。
|
||
|
||
| 请求字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `image_file` | 文件 | 上传 JPG 或 PNG |
|
||
| `image_url` | 字符串 | 网关可访问的图片 URL |
|
||
| `image_base64` | 字符串 | 图片 Base64,支持带 `data:image/…;base64,` 前缀 |
|
||
|
||
以上三个字段必须且只能传一个。图片按 EXIF 方向解码后,先检查原图的人脸完整性和俯拍角度。俯拍使用头部姿态估算的正向 pitch,超过服务端环境变量 `FRONTAL_PITCH_THR`(默认 30°)时拦截。通过质检后复用接口 52 的扶正裁切规则,再用处理后的图片做面部特征分析。
|
||
|
||
客户端通过 Bearer 令牌鉴权,详见[客户端鉴权](接口42-52-72网关鉴权.md)。
|
||
|
||
### 质检失败与异常响应
|
||
|
||
以下业务响应的 HTTP 状态均为 `200`。
|
||
|
||
| `code` | 情况 | `message` |
|
||
|---|---|---|
|
||
| `1001` | 原图未检测到人脸 | 无法识别人像,请上传包含完整人脸的照片 |
|
||
| `1003` | 人脸关键点触及或超出图片边界 | 人脸不完整,请保留完整的脸部轮廓 |
|
||
| `1003` | 估算的俯拍角度超过阈值 | 俯拍角度过大,请将相机调整至与脸部平视 |
|
||
| `1003` | 扶正裁切失败,无法保留所需人脸关键点 | 人脸过于贴近边缘,无法扶正裁切 |
|
||
| `1008` | URL 图片下载失败 | 图片下载失败(URL 不可达) |
|
||
| `1008` | Base64 解码失败 | 图片格式不支持(base64 解码失败) |
|
||
| `1008` | 图片无法读取 | 图片格式不支持(仅 JPG / PNG) |
|
||
| `1007` | 图片字段未按三选一传入 | 图片参数错误:必须且只能传 image_file / image_url / image_base64 其中一个 |
|
||
| `1007` | 图片预处理内部异常 | 图片预处理失败 |
|
||
| `1001` | 质检通过,但视觉模型未识别到人脸 | 无法识别人像 |
|
||
| `1007` | 视觉模型调用异常 | 分析服务异常:… |
|
||
|
||
响应包含 `code`(整数业务码)、`message`(字符串提示)、`request_id`(字符串请求标识)和 `data`(失败时为 `null`)。三个 `1003` 情况可通过 `message` 区分,响应中没有独立的质检原因或角度字段。
|
||
|
||
```json
|
||
{
|
||
"code": 1003,
|
||
"message": "俯拍角度过大,请将相机调整至与脸部平视",
|
||
"request_id": "gw-xxxxxxxx",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### 成功响应
|
||
|
||
`data.features` 仍为 JSON 字符串,保留 main 网关接口 4 的七项特征(含 `contour_type`),在其中新增:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `zygoma_expansion` | boolean 或 null | 颧弓外扩 |
|
||
| `temple_hollowing` | boolean 或 null | 太阳穴凹陷 |
|
||
|
||
Ft 两端保持横坐标并取平均高度,Zy 为 234 到 454 的距离。只有 Ft ÷ Zy 严格小于 0.8 时两项同时为 `true`,否则同时为 `false`。所需点位缺失或无法测量时两项均为 `null`。网关测量复用接口 99 的 MediaPipe 与 BiSeNet 依赖,裁切还需要 Pillow。
|
||
|
||
---
|
||
|
||
## 接口 5:发际线 PNG 生成接口
|
||
|
||
**说明**:入参同接口2(先选性别、再多选发型)。对每个选中发型返回 `middle` / `high` / `low` **三档**发际线叠图与**生发图**,并返回「最合适发际线」的面部中间点坐标。
|
||
|
||
**请求**:`POST /api/v1/hairline/generate`
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」。专属参数:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| gender | string | **是** | 性别:`male` / `female`。决定发型集合(female 7 / male 7)。缺失/非法返回 `1004` |
|
||
| hair_style | string | **是** | 发型序号,**逗号分隔多选**(如 `1,2,3`),决定返回哪些发际线类型。female:1=ellipse, 2=flower, 3=heart, 4=straight, 5=wave, 6=bigflower, 7=clasicalflower;male:1=ellipse, 2=inverse_arc, 3=m, 4=straight, 5=heart, 6=Softpetal, 7=wave。缺失/越界/非法返回 `1007` |
|
||
| use_mask | bool | 否 | 生发是否启用 inpaint 遮罩,默认 `true`。`false` 时用干净原图生成(空遮罩、不烧模板黑线),供测试对比 |
|
||
| prompt | string | 否 | ComfyUI 提示词,默认「补充遮罩区域的头发,加一点美颜」,会替换工作流节点 60 的文本 |
|
||
| generate_grow_image | bool | 否 | 是否生成生发效果图(ComfyUI 生发,全流程最耗时),默认 `true`。传 `false` 时跳过生发,各发型 `grown_image_*` 恒为 `null`,仅返回三档发际线叠图与中心点,可大幅降低耗时 |
|
||
|
||
> ⚠️ 三档叠图分别用 `hairline_texture` / `hairline_texture_high` / `hairline_texture_low` 三套同名贴图;**生发黑模板固定取自 `hairline_texture_black/`(middle 档)**,即生发目标固定压到 middle 档,每个发型仅 1 张生发图。
|
||
|
||
### 输出(data)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| hairline_images | object[] | **选中发型**列表,**数量 = 所选发型数**,元素见下表 |
|
||
| best_hairline_center_point | object \| null | **首个选中发型**的 **middle 档**发际线曲线「面部中间点」坐标,原图像素:`{ "x": number, "y": number }` |
|
||
| high_hairline_center_point | object \| null | 同上,**high 档**发际线中点(发际线偏高) |
|
||
| low_hairline_center_point | object \| null | 同上,**low 档**发际线中点(发际线偏低) |
|
||
| face_measure | object \| null | **复用接口1**的四庭七眼测量**数值**(不含标注图)。独立流程,测量失败(无人脸/非正面/分割失败)时为 `null`,不影响发际线主结果。字段结构见下表 |
|
||
|
||
`hairline_images` 元素:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| hairline_type | string | 发际线类型 key:`ellipse`/`flower`/`heart`/`straight`/`wave`/`bigflower`/`clasicalflower`(female),`ellipse`/`m`/`straight`/`inverse_arc`/`heart`/`Softpetal`/`wave`(male) |
|
||
| image_middle_url | string | middle 档发际线曲线**透明 PNG** URL(仅曲线,透明底,**不含人物**,需叠加原图显示) |
|
||
| image_high_url | string | high 档发际线曲线**透明 PNG** URL(同上,high 档曲线) |
|
||
| image_low_url | string | low 档发际线曲线**透明 PNG** URL(同上,low 档曲线) |
|
||
| grown_image_url | string \| null | **生发后图片** URL(ComfyUI「植发」效果图,完整人像照片,生发失败或 `generate_grow_image=false` 时为 `null`) |
|
||
| order | int | 发型序号(= 传入的 hair_style 值) |
|
||
|
||
> worker 侧返回 `image_middle_base64` / `image_high_base64` / `image_low_base64` / `grown_image_base64`,网关落盘后改写为上表对应的 `*_url`。
|
||
>
|
||
> 💡 三档 `image_*_url` 为透明底 PNG,前端需用绝对定位叠加到原图上显示(参考[测试页](https://hair.xiangsilian.com/static/test_interface5.html)的 `.img-stack` 叠加结构)。`grown_image_url` 是完整人像照片,直接显示即可。
|
||
|
||
`face_measure` 元素(与[接口1](#接口-1四庭七眼测量标注接口)的 `data` 同构,**不含** `annotated_image_*` 标注图字段):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| face_total_height_cm | number | 全脸总高度(cm)= 四庭之和 |
|
||
| 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 }` |
|
||
| right_position | object | MediaPipe 251 号关键点坐标(右脸定位点,与 21 号镜像),原图像素:`{ "x": int, "y": int }` |
|
||
|
||
> `eye1`~`eye7` 为从左到右共 7 段宽度,eye1=左耳外段、eye7=右耳外段,某侧耳朵不可见时对应段为 `null`。详见接口1说明。
|
||
|
||
### 响应示例(当前 Mock 返回值)
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"hairline_images": [
|
||
{
|
||
"hairline_type": "ellipse",
|
||
"image_middle_url": "https://hair.xiangsilian.com/static/annotations/mid1.png",
|
||
"image_high_url": "https://hair.xiangsilian.com/static/annotations/high1.png",
|
||
"image_low_url": "https://hair.xiangsilian.com/static/annotations/low1.png",
|
||
"grown_image_url": "https://hair.xiangsilian.com/static/annotations/grown1.png",
|
||
"order": 1
|
||
},
|
||
{
|
||
"hairline_type": "heart",
|
||
"image_middle_url": "https://hair.xiangsilian.com/static/annotations/mid3.png",
|
||
"image_high_url": "https://hair.xiangsilian.com/static/annotations/high3.png",
|
||
"image_low_url": "https://hair.xiangsilian.com/static/annotations/low3.png",
|
||
"grown_image_base64": null,
|
||
"order": 3
|
||
}
|
||
],
|
||
"best_hairline_center_point": { "x": 540, "y": 430 },
|
||
"high_hairline_center_point": { "x": 540, "y": 380 },
|
||
"low_hairline_center_point": { "x": 540, "y": 480 },
|
||
"face_measure": {
|
||
"face_total_height_cm": 26.76,
|
||
"four_courts": {
|
||
"top_court_cm": 5.77, "upper_court_cm": 5.93,
|
||
"middle_court_cm": 7.62, "lower_court_cm": 7.44,
|
||
"ratios": { "top_court": 0.216, "upper_court": 0.222,
|
||
"middle_court": 0.285, "lower_court": 0.278 }
|
||
},
|
||
"seven_eyes": {
|
||
"eye_width_cm": 2.76, "face_width_cm": 15.08,
|
||
"inter_eye_distance_cm": 3.9,
|
||
"ratios": { "eye_width": 0.183, "inter_eye_distance": 0.259 },
|
||
"eye1": null, "eye2": 3.0, "eye3": 2.76, "eye4": 3.9,
|
||
"eye5": 2.76, "eye6": 3.0, "eye7": null
|
||
},
|
||
"landmarks": {
|
||
"hair_top": { "x": 504, "y": 103 },
|
||
"hairline": { "x": 504, "y": 228 },
|
||
"brow_center": { "x": 504, "y": 357 },
|
||
"nose_bottom": { "x": 505, "y": 522 },
|
||
"chin_tip": { "x": 506, "y": 683 }
|
||
},
|
||
"hairline_source": "segmentation",
|
||
"head_pose": { "yaw": -1.39, "pitch": 2.49, "roll": -0.06 }
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
> 说明:生发失败的元素中,网关不改写 `null` 值,故字段名保持为 `grown_image_base64: null`(有值时才改写为 `grown_image_url`),与接口2生发失败项一致。
|
||
|
||
---
|
||
|
||
## 接口 52:发际线 PNG 生成接口
|
||
|
||
**说明**:入参同接口2(先选性别、再多选发型)。对每个选中发型返回一张 `middle` 设计叠图与生发图。**目标上庭 =(中庭 + 下庭)÷2**,从眉心向上定位;所有形状的中点对齐同一目标。`high` / `low` 已停用,旧字段保留为 `null`。
|
||
|
||
**请求**:`POST /api/v1/hairline/generate-52`(网关透传至同路径GPU worker)
|
||
|
||
**客户端鉴权**:使用 `Authorization: Bearer <access_token>`。登录、刷新和错误码见[客户端鉴权](接口42-52-72网关鉴权.md)。
|
||
|
||
**职责**:GPU worker返回base64并完成算法处理,网关负责透传参数、附加内部Token、保存非空图片及返回公网URL。网关不增加IP限制;网关出口IP须已在worker白名单内。部署与完整字段约定见[接口52网关接入](接口52网关接入.md)。
|
||
|
||
### 图片扶正与标准构图
|
||
|
||
显式传`normalize_image=true`时,先扶正9→152,再按**高(3÷0.9)×L≈3.3333×L、宽2.5×L(3:4)**裁切,L为9→152的直线像素距离。相对原3L规则,裁切框宽高均扩大约11.11%,同样显示尺寸下人物缩小约10%。高度取最接近的4的倍数,宽度取高度的3/4。眉心定位沿用已扶正参考图:9号点相对位置约(49.0061%,34.4136%)。参考图仍为786×1048,两点X均为385.18784。
|
||
|
||
目标窗口完全在原图有效区域内才进行标准裁切。标准窗口不足时,偏长图片(宽/高<3/4)采用原图有效范围内最大的3:4窗口,尽量贴近眉心目标位置并保留完整人脸,`status=aspect_crop`;人脸放不进3:4窗口时回退仅扶正并返回原因。其余图片空间不足时仍仅扶正、去除旋转空角。不放大小图。**所有叠图与坐标使用响应中的处理后底图 `input_image_*`**,调用方应使用该底图叠加;`normalize_image=false` 可关闭此预处理。
|
||
|
||
`framing`包含处理前后尺寸、旋转角、是否标准裁切、跳过原因、`source_to_processed`/`processed_to_source`仿射矩阵。`status`区分`reference_crop`(标准窗口)、`aspect_crop`(长图3:4裁切)和`rotation_only`(仅扶正);`standard_crop=false`时仍可能有`aspect_crop=true`。`aspect_crop_skip_reason`说明长图无法保留人脸时的回退原因。`original_image_*`保留EXIF方向修正后的上传图,原图坐标可通过逆矩阵还原。
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」。专属参数:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| gender | string | **是** | 性别:`male` / `female`。决定发型集合(female 7 / male 7)。缺失/非法返回 `1004` |
|
||
| hair_style | string | **是** | 发型序号,**逗号分隔多选**(如 `1,2,3`),决定返回哪些发际线类型。female:1=ellipse, 2=flower, 3=heart, 4=straight, 5=wave, 6=bigflower, 7=clasicalflower;male:1=ellipse, 2=inverse_arc, 3=m, 4=straight, 5=heart, 6=Softpetal, 7=wave。缺失/越界/非法返回 `1007` |
|
||
| use_mask | bool | 否 | 生发是否启用 inpaint 遮罩,默认 `true`。`false` 时用干净原图生成(空遮罩、不烧模板黑线),供测试对比 |
|
||
| prompt | string | 否 | ComfyUI 提示词,默认「填充遮罩区域的头发,人物增加一点磨皮」,会替换工作流节点 60 的文本 |
|
||
| normalize_image | bool | 否 | 新增可选开关,默认false:保留原图尺寸与坐标。传true开启扶正和裁切;网关测试页显式传true |
|
||
| generate_grow_image | bool | 否 | 是否生成生发效果图(ComfyUI 生发,全流程最耗时),默认 `true`。传 `false` 时跳过生发,各发型 `grown_image_*` 恒为 `null`,仅返回设计叠图、中心点与高度规则数据,可大幅降低耗时 |
|
||
|
||
> 生发划线与遮罩直接取新位置的 middle 曲线;旧规则的头发边界与模板用于保留形状和倾斜,不决定新中心高度。计算使用未取整的像素高度,与四庭测量采用同一 Y 方向;测量主流程失败时用发际线管线的同编号关键点计算,`measurement_source` 标明来源。目标越出图片或无法对齐时返回 1007,不静默回退旧规则。
|
||
|
||
### 高度规则与对比
|
||
|
||
- **参数与字段**:原有8个请求参数、响应外层结构和所有旧返回字段均保留。`use_mask=true`、默认prompt、`generate_grow_image=true`保持原值。新功能只新增可选参数和返回字段;旧请求未传`normalize_image`时保留原图坐标,high/low按已约定保留null,worker始终返回`*_base64`;网关沿用原有规则,有图片时改为`*_url`,空图片保留`*_base64: null`。
|
||
- `height_rule`:`middle_height_px`、`lower_height_px`、`target_upper_height_px`、`target_upper_height_cm`(可空)、`target_center`、`brow_center`、`nose_bottom`、`chin_tip`、`measurement_source`。
|
||
- 每项 `height_placement`:`old_center`、`new_center`、`shift_x_px`、`shift_y_px`(正=下移)、`target_error_px`。浮点坐标保留精度,旧中心点字段继续返回取整像素。
|
||
- `face_measure` 为处理后底图的实际测量值,未被设计目标覆盖。
|
||
- `guide_landmarks`:9、150、152、162、251、389 号 MediaPipe 点的图片像素坐标,供对比页绘制参考框。参考框左/右边分别经过162/389,底边经过9,高度为9→152距离的70%;两侧均固定平行9→152。
|
||
- 网关测试页:`/static/test_interface52.html`。worker本机另有旧/新规则对比页,本次不向网关开放调试接口。
|
||
- 规则页每个发型依次展示未裁切原图、middle、裁切发际线叠加、生发图。“裁切发际线叠加”由页面按`guide_landmarks`生成:在相同处理后底图上叠加矩形内的middle PNG,矩形外保持透明;左/右边过162/389,底边过9,高度0.7×9→152,左右边平行9→152。
|
||
|
||
### 网关输出(data)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| hairline_images | object[] | **选中发型**列表,**数量 = 所选发型数**,元素见下表 |
|
||
| best_hairline_center_point | object \| null | **首个选中发型**的 **middle 档**发际线曲线「面部中间点」坐标,当前底图像素:`{ "x": number, "y": number }` |
|
||
| high_hairline_center_point | object \| null | 已停用,固定 `null` |
|
||
| low_hairline_center_point | object \| null | 已停用,固定 `null` |
|
||
| input_image_url | string | 当前底图JPEG的URL,发际线叠图、参考点和测量值均使用该图的坐标系 |
|
||
| original_image_url | string | EXIF方向修正后的上传原图JPEG的URL |
|
||
| width / height | int | 当前底图的实际像素宽 / 高 |
|
||
| coordinate_space | string | 开启预处理时为 `processed_image`,关闭时为 `original_image` |
|
||
| framing | object \| null | 裁切状态、旋转角、前后尺寸、轴线误差及双向仿射矩阵;关闭预处理时为 `null` |
|
||
| height_rule | object | 中庭/下庭、目标上庭高度、目标中心及测量来源,详见“高度规则与对比” |
|
||
| guide_landmarks | object | 9、150、152、162、251、389 号点的当前底图像素坐标;参考框实际使用9、152、162、389 |
|
||
| face_measure | object \| null | **复用接口1**的四庭七眼测量**数值**(不含标注图)。独立流程,测量失败(无人脸/非正面/分割失败)时为 `null`,不影响发际线主结果。字段结构见下表 |
|
||
|
||
`hairline_images` 元素:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| hairline_type | string | 发际线类型 key:`ellipse`/`flower`/`heart`/`straight`/`wave`/`bigflower`/`clasicalflower`(female),`ellipse`/`m`/`straight`/`inverse_arc`/`heart`/`Softpetal`/`wave`(male) |
|
||
| image_middle_url | string | middle 档发际线曲线**透明 PNG** URL(仅曲线,透明底,**不含人物**,需叠加 `input_image_url` 显示) |
|
||
| image_high_base64 | null | 已停用,保留字段为 `null` |
|
||
| image_low_base64 | null | 已停用,保留字段为 `null` |
|
||
| grown_image_url | string | 生发成功时的完整人像JPEG URL |
|
||
| grown_image_base64 | null | 生发失败或`generate_grow_image=false`时返回;此时没有`grown_image_url` |
|
||
| order | int | 发型序号(= 传入的 hair_style 值) |
|
||
| height_placement | object | 旧/新中心、XY移动距离、目标误差,均为未取整的像素值 |
|
||
|
||
> 上表为网关响应字段。worker直连时所有图片字段均为`*_base64`;网关仅对有图片内容的字段生成`*_url`,high/low等空字段保留`*_base64: null`,沿用既有行为。
|
||
>
|
||
> middle透明PNG叠加到`input_image_url`,生发图直接显示。网关测试页为`/static/test_interface52.html`,页面不包含内部Token。
|
||
|
||
`face_measure` 元素(与[接口1](#接口-1四庭七眼测量标注接口)的 `data` 同构,**不含** `annotated_image_*` 标注图字段):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| face_total_height_cm | number | 全脸总高度(cm)= 四庭之和 |
|
||
| 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 }` |
|
||
| right_position | object | MediaPipe 251 号关键点坐标(右脸定位点,与 21 号镜像),当前底图像素:`{ "x": int, "y": int }` |
|
||
|
||
> `eye1`~`eye7` 为从左到右共 7 段宽度,eye1=左耳外段、eye7=右耳外段,某侧耳朵不可见时对应段为 `null`。详见接口1说明。
|
||
|
||
### 网关响应结构示例(normalize_image=false)
|
||
|
||
此例未开启裁切,坐标基于原图;`face_measure=null`时仍可按发际线关键点计算目标。有图片内容的字段已落盘为公网URL;空图片字段仍返回null,保留base64字段名。
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"request_id": "mock-request-id",
|
||
"data": {
|
||
"hairline_images": [
|
||
{
|
||
"hairline_type": "ellipse",
|
||
"image_middle_url": "https://hair.xiangsilian.com/static/annotations/middle.png",
|
||
"image_high_base64": null,
|
||
"image_low_base64": null,
|
||
"grown_image_url": "https://hair.xiangsilian.com/static/annotations/grown.jpg",
|
||
"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
|
||
}
|
||
}
|
||
],
|
||
"best_hairline_center_point": {"x": 540, "y": 430},
|
||
"high_hairline_center_point": null,
|
||
"low_hairline_center_point": null,
|
||
"input_image_url": "https://hair.xiangsilian.com/static/annotations/input.jpg",
|
||
"original_image_url": "https://hair.xiangsilian.com/static/annotations/original.jpg",
|
||
"width": 1080, "height": 1440,
|
||
"coordinate_space": "original_image",
|
||
"framing": null,
|
||
"height_rule": {
|
||
"name": "court_average", "formula": "上庭 = (中庭 + 下庭) / 2",
|
||
"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}
|
||
},
|
||
"guide_landmarks": {
|
||
"9": {"x": 540.0, "y": 740.0}, "150": {"x": 470.0, "y": 1340.0},
|
||
"152": {"x": 540.0, "y": 1360.0}, "162": {"x": 300.0, "y": 680.0},
|
||
"251": {"x": 770.0, "y": 630.0}, "389": {"x": 780.0, "y": 680.0}
|
||
},
|
||
"face_measure": null
|
||
}
|
||
}
|
||
```
|
||
|
||
开启`normalize_image=true`后,`coordinate_space=processed_image`,`framing`包含前述裁切状态和坐标变换,所有发际线图片须叠加到`input_image_url`。原始图片仅用于展示和坐标回溯。
|
||
|
||
---
|
||
|
||
## 接口 9:头发遮罩生成接口
|
||
|
||
**说明**:输入一张含人头的照片,生成头发遮罩,并返回**每一步的可视化图**(供对比调试)。
|
||
|
||
**请求**:`POST /api/v1/head/mask`
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」。专属参数:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| erode_cm | float | 否 | 外缘朝中心(鼻梁 151 点)内缩的距离(厘米),默认 1.2 |
|
||
|
||
### 处理流程
|
||
|
||
1. MediaPipe 关键点 → 底部分割线(关键点 21,68,104,69,108,151,337,299,333,298,251 的连线,21 水平延伸到最左、251 延伸到最右)。
|
||
2. 分割线以上为「上半区」。
|
||
3. 头发分割(**BiSeNet** 与 **SegFormer** 两套);从每列最顶端头发向下填充到分割线,得到**含额头**的闭合区域(头发+额头,不割断)。
|
||
4. 外缘朝中心点 151 内缩 `erode_cm`(虹膜标定换算像素)、底线不动 → 最终遮罩。
|
||
|
||
### 输出(data)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| px_per_cm | float | 虹膜标定出的像素/厘米 |
|
||
| erode_cm / erode_px | float / int | 内缩距离(厘米 / 像素) |
|
||
| image_size | object | `{width, height}` 原图尺寸 |
|
||
| steps_common | object | `landmarks_baseline_*`(关键点/分割线)、`upper_region_*`(上半区) |
|
||
| bisenet / segformer | object | 两套分割模型各自的结果:`hair_mask_*`(原始头发分割)、`closed_region_*`(含额头闭合区域)、`final_overlay_*`(最终遮罩叠加可视化)、`mask_*`(最终遮罩 PNG)及 `hair_pixels`/`closed_pixels`/`mask_pixels` 像素计数 |
|
||
|
||
> worker 返回 `*_base64`(data URI),经网关改写为 `*_url`。
|
||
|
||
---
|
||
|
||
## 接口 10:头部外缘膨胀带遮罩接口
|
||
|
||
**说明**:先和接口9 一样得到内缩后的基准遮罩,再沿头部外缘生成一条膨胀带遮罩。
|
||
|
||
**请求**:`POST /api/v1/head/band`
|
||
|
||
### 输入
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| erode_cm | float | 否 | 基准遮罩外缘朝 151 内缩距离(厘米,同接口9),默认 1.2 |
|
||
| dilate_cm | float | 否 | 外轮廓线膨胀后带子总宽(厘米),默认 2.0 |
|
||
|
||
### 处理流程
|
||
|
||
1. 取基准遮罩的**外轮廓线**,去掉贴着底部分割线的那一段(只留头发/头部外缘弧线)。
|
||
2. 把外轮廓线膨胀成带子(总宽 `dilate_cm`;半径=总宽/2,虹膜标定换算像素)。
|
||
3. 裁到分割线以上(不越过底线)。
|
||
|
||
### 输出(data)
|
||
|
||
结构同接口9,但每组结果为 `base_mask_*`(基准遮罩)/ `contour_*`(外轮廓线)/ `band_overlay_*`(带子叠加可视化)/ `mask_*`(最终带状遮罩)及 `base_pixels`/`band_pixels` 计数,另有 `dilate_cm`/`dilate_radius_px`。
|
||
|
||
---
|
||
|
||
## 接口 11:发际线生发接口(不含重绘)
|
||
|
||
**说明**:输入一张**发际线较高 / 头发稀少**的正脸图 + 发际线类型 ID,输出同一个人、同一发型、按该类型压低发际线后的图片,并返回**每一步可视化**。重绘版见接口12。
|
||
|
||
**请求**:`POST /api/v1/hairline/grow`
|
||
|
||
### 输入
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| hairline_id | string | **是** | 发际线类型 ID(= change_hair `hair_id`):`chang_tuoyuan` / `chang_huaban` / `chang_xinxing` / `chang_zhixian` / `chang_bolang` |
|
||
| gen_backend | string | 否 | 生成后端:`swaphair`(换发型 LoRA,默认)|`hairgrow`(区域生发 inpaint) |
|
||
| hairgrow_strength | float | 否 | 区域生发强度(仅 hairgrow 后端),默认 0.75 |
|
||
| is_hr | bool | 否 | 高清模式(换发型输出 1152×1536,否则 576×768),默认 false |
|
||
| seg_model | string | 否 | 头发分割模型:`bisenet` | `segformer`(默认) |
|
||
| erode_cm | float | 否 | baseline 参考内缩距离(厘米),默认 0.6 |
|
||
| hairline_push_cm | float | 否 | 发际线外推距离(厘米,往头发方向推进),默认 1.0 |
|
||
| hairline_edge | string | 否 | 发际线提取方式:`column`(默认)|`contour` |
|
||
| swap_mode | string | 否 | 换发型取图模式:`ext_mask`(默认,把接口9 遮罩传给 webui 精确重绘)|`as_is`(用内部遮罩,贴回时再裁) |
|
||
| edge_erode_px | int | 否 | 贴图前遮罩内缩像素(防边缘露皮/光晕),默认 3 |
|
||
| denoising_strength | float | 否 | 换发型 webui 重绘强度(越大生发越激进),默认 0.6 |
|
||
| mb_levels | int | 否 | 多频段金字塔层数(2~6),默认 5 |
|
||
| blend_method | string | 否 | 接缝融合:`multiband`(默认)|`seamless`(泊松)|`two_stage`(泊松→多频段)|`feather`(高斯羽化)|`alpha_gradient`(距离渐变) |
|
||
| color_match | bool | 否 | 融合前 Reinhard 颜色迁移消除整体色差,默认 true |
|
||
| color_match_strength | float | 否 | 颜色迁移强度(0~1),默认 1.0 |
|
||
| mb_feather_px | int | 否 | 多频段最细层掩码轻羽化像素,默认 1 |
|
||
| transition_band_px | int | 否 | keep-region 过渡带边距(-1=自动按层数 2^n),默认 -1 |
|
||
| inpainting_fill | int | 否 | change_hair 服务端重绘填充:0=保留原图 | 1=噪声(默认)| 2=纯色 | 3=潜变量噪声 |
|
||
| mask_blur | int | 否 | change_hair 服务端遮罩边缘模糊像素,默认 11 |
|
||
| mask_dilate_scale | float | 否 | change_hair 服务端遮罩膨胀缩放(<1 收缩防越界),默认 1.0 |
|
||
|
||
### 输出(data)
|
||
|
||
`steps`:`input`(原图)/ `baseline`(底部分割线)/ `hair_mask`(头发分割)/ `mask_overlay`(遮罩叠加)/ `mask`(遮罩)/ `swap_raw`(换发型原始输出)/ `alpha`(融合权重)/ `final`(最终结果)各步图(`*_base64`,经网关改写为 `*_url`),另有 `timings_ms` 分步耗时。
|
||
|
||
> ⚠️ `swaphair` 后端依赖 change_hair(:8801)在跑;`hairgrow` 后端依赖 ComfyUI(:8188)。
|
||
|
||
---
|
||
|
||
## 接口 12:发际线带重绘接口
|
||
|
||
**说明**:接口11 的**下游重绘阶段**——内部先跑接口11 核心管线拿 ④ 接缝融合最终图(final),再取发际线重绘带(外推 `band_lo_mult×push` ~ `band_hi_mult×push` 之间、经 baseline 截断只留上部的带状区域)作遮罩,用 **Flux-2(ComfyUI,reference-latent 保色重绘)** 重绘。
|
||
|
||
**同时产出两版结果**:
|
||
|
||
- `redraw_full`:ComfyUI 整帧输出(全脸美颜 + 全脸重绘)。
|
||
- `redraw_band`:加发只在发际线带内、band 外保留 final 结构并按 `beauty_alpha` 融入全脸美颜。
|
||
|
||
**请求**:`POST /api/v1/hairline/grow_v2`
|
||
|
||
### 输入
|
||
|
||
接口11 的全部参数(见上表)再叠加:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| comfyui_prompt | string | 否 | Flux-2 重绘提示词,缺省「填充遮罩区域的头发,人物增加一点磨皮」 |
|
||
| beauty_alpha | float | 否 | redraw_band 版 band 外的全脸美颜融入强度(0=band 外纯用 final,1≈整帧版),默认 0.6 |
|
||
| band_lo_mult | float | 否 | 重绘带外推倍率下限(相对 hairline_push_cm),默认 0.5 |
|
||
| band_hi_mult | float | 否 | 重绘带外推倍率上限(相对 hairline_push_cm),默认 1.5 |
|
||
|
||
### 输出(data)
|
||
|
||
`steps`:`input` / `final`(重绘输入基底)/ `redraw_band_overlay`(重绘带可视化)/ `redraw_full`(A 整帧)/ `redraw_band`(B 局部+美颜)/ `redraw_c`(兼容旧字段,=redraw_full);重绘异常在 `redraw.c_error` 报告。
|
||
|
||
### 精简变体(生产推荐)
|
||
|
||
| 路径 | 说明 |
|
||
|------|------|
|
||
| `POST /api/v1/hairline/grow_v2_final` | 仅需图片 + `hairline_id`,其余参数固化为调优默认值(push=0.8cm / two_stage / color_match=关 等),**最终取整帧重绘 `redraw_full`** |
|
||
| `POST /api/v1/hairline/grow_v2_final_v2` | 参数同上,**最终取 B 局部加发+全脸美颜 `redraw_band`** |
|
||
|
||
> ⚠️ 依赖 ComfyUI(:8188)与 change_hair(:8801)均在跑。接口2 女性 1..5 的生发图即复用 `grow_v2_final` 管线。
|
||
|
||
---
|
||
|
||
## 接口 99:人脸特殊点检测接口
|
||
|
||
**说明**:输入一张人脸照片,返回一组**固定编号 MediaPipe Face Mesh 关键点**的像素坐标,外加 5 个**发际线特殊点**(射线与头发分割发际线的交点)。**生产路径:网关本机实现(纯 CPU,不转发 worker,同接口4 模式)**;worker 侧保留同路径接口供测试页直连。
|
||
|
||
**请求**:`POST /api/v1/face/special-points`
|
||
|
||
### 输入
|
||
|
||
图片参数见「通用约定 → 图片传参字段」,无专属参数。要求单人正面照(非正面返回 `1003`)。
|
||
|
||
### 输出(data)
|
||
|
||
`points`:字段名 = `<组英文名>_<点ID>`,值为原图像素坐标 `{x, y}`(int);发际线点不可得(秃顶/分割失败)时为 null。
|
||
|
||
| 组 | 字段前缀 | 点 ID |
|
||
|----|----------|-------|
|
||
| 眉毛 | `eyebrow` | 107 66 105 63 70 336 296 334 293 300 |
|
||
| 眼睛 | `eye` | 189 222 224 113 226 413 442 444 342 446 |
|
||
| 嘴 | `mouth` | 0 37 40 67 91 84 17 405 375 409 269 |
|
||
| 中线鼻 | `nose_midline` | 9 168 197 5 4 194 278 48 437 217 |
|
||
| 面颊 | `cheek` | 101 117 123 187 205 50 330 346 352 280 425 |
|
||
| 边线 | `face_edge` | 162 234 58 136 149 152 378 365 288 401 366 389 251 |
|
||
|
||
**发际线特殊点**(沿关键点 a→b 方向射线步进,首次进入 BiSeNet 头发 mask 处为交点;双射线取中点,仅一条命中返回该点):
|
||
|
||
| 字段 | 计算 |
|
||
|------|------|
|
||
| `hair_line_center` | 151→10 方向交点 |
|
||
| `hair_line_right` | mid(108→109 交点A, 69→67 交点B) |
|
||
| `hair_line_right_right` | mid(104→103 交点A, 68→54 交点B) |
|
||
| `hair_line_left` | mid(337→338 交点A, 299→297 交点B) |
|
||
| `hair_line_left_left` | mid(333→332 交点A, 298→284 交点B) |
|
||
|
||
另有 `image_size`(宽高)与 `hairline_source`(`segmentation`=发际线点已算出 / `unavailable`=分割不可用,发际线点为 null)。
|
||
|
||
---
|
||
|
||
## 汇总:输入输出一览
|
||
|
||
| 接口 | 路径 | 输入 | 主要输出 |
|
||
|------|------|------|----------|
|
||
| 1 四庭七眼测量 | `/api/v1/face/measure` | 用户照片 | 标注 PNG(无人物)+ 四庭/七眼厘米数值与坐标 |
|
||
| 6 四庭七眼测量 v2 | `/api/v1/face/measure-v2` | 用户照片 | 同接口1,去顶庭变体 |
|
||
| 2 C 端生发 | `/api/v1/hair/grow` | 用户照片 + gender + hair_style(多选) | 每发型预览 PNG + 生发图(female 1..5=换发型+重绘,其余=原生生发) |
|
||
| 7 C 端生发 v2 | `/api/v1/hair/grow-v2` | 同接口2 | 同接口2(add_hair2 工作流,已弃用隐藏) |
|
||
| 3 B 端生发 | `/api/v1/hair/grow-b` | 划线图片 | 检测到的发际线 + 生发后图片 |
|
||
| 4 用户特征 | `/api/v1/face/features` | 用户照片 | 7 个用户特征字段(脸形/眉形/年龄/动静/性别/基因风格/轮廓类型),网关本机实现 |
|
||
| 5 发际线 PNG | `/api/v1/hairline/generate` | 用户照片 + gender + hair_style(多选) | 每个选中发型 middle/high/low 三档发际线叠图 + 生发图 + 最合适发际线面部中间点坐标 |
|
||
| 9 头发遮罩 | `/api/v1/head/mask` | 用户照片 | 头发遮罩 + 分步可视化(BiSeNet/SegFormer 双路) |
|
||
| 10 外缘膨胀带 | `/api/v1/head/band` | 用户照片 | 头部外缘带状遮罩 + 分步可视化 |
|
||
| 11 发际线生发 | `/api/v1/hairline/grow` | 用户照片 + hairline_id | 压低发际线图 + 全步骤可视化(不含重绘) |
|
||
| 12 发际线带重绘 | `/api/v1/hairline/grow_v2` | 同接口11 + 重绘带参数 | final 基底 + Flux-2 保色重绘(整帧/局部两版);精简版 `grow_v2_final[_v2]` 仅需图片+hairline_id |
|
||
| 99 人脸特殊点 | `/api/v1/face/special-points` | 用户照片 | 60 个固定编号关键点 + 5 个发际线特殊点像素坐标 JSON(**网关本机实现**,同接口4) |
|
||
|
||
> 另有 worker 本机工具路由(不在对外契约内):`/api/swapHair/v1`、`/hairColor/v2`(CORS 代理 change_hair)、`/api/v1/redraw`(直接 ComfyUI 重绘)、`/api/v1/debug/*`。
|
||
|
||
---
|
||
|
||
## 已确认结论
|
||
|
||
- 图片传参:**文件 / URL / base64 三种都支持,严格互斥**(必须且只能传一个,base64 需带 `data:image/...;base64,` 前缀)。
|
||
- 默认单人;检测到 2~3 人报错提示客户端;单文件 **≤ 1 MB**;格式仅 **JPG / PNG**。
|
||
- 所有坐标基准:**原图像素**。
|
||
- 接口 2:提供「美颜」开关 `beauty_enabled`。
|
||
- 接口 3:仅传**划线图**(`marked_image_*` 三选一),需返回发际线类型(`custom`)。
|
||
- 接口 4:`data` 返回**一个 JSON 字符串**,字段不固定,可随时调整。
|
||
- 接口 5:发际线 PNG 数量 **N 不固定**,需按合适度**排序**。
|
||
- 英文字段命名由本文档约定(见各接口表格)。
|
||
|
||
## 待与需求方确认的问题清单
|
||
|
||
1. 接口 1 标注图片设计稿(见下方说明)。
|