Files
hair/docs/实现说明.md
T
xslandClaude 8cd44848d6 feat(接口6): 复刻接口1,新增 /api/v1/face/measure-v2
- 抽取 _face_measure_impl() 共用实现,接口1/6 零逻辑差异
- 接口6 路径 POST /api/v1/face/measure-v2
- 入参/出参与接口1 完全一致
- 文档同步更新(接口文档、实现说明、网关待改动)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-23 20:27:51 +08:00

8.9 KiB
Raw Blame History

旷视五接口 — 实现说明(总)

把原先分散的「各接口技术方案 + 开发任务书 + 系统架构 + 网关任务书」合并成这一份简要总览。 对外 API 契约以 接口文档.md 为唯一权威;原始需求见 旷视具体需求.md 离线模型清单见 ../OFFLINE_ASSETS.md


1. 架构

两台机器、一个仓库:

客户端 ──HTTPS──> 外网网关(gateway/)  ──HTTP(X-Internal-Token)──> worker(GPU 机, app.py)
                     │ 薄代理 + 落盘改URL                          │ 跑算法(本地模型/ComfyUI)
                     └ 接口4 本机直接调豆包(不转发)                 └ 接口1/2/3/5/6/7
  • workerapp.py + face_analysis/ + hairline/):跑真正的算法,纯本地、无外网依赖。 对 /api/* 校验 X-Internal-Token(密码在 worker_config.jsonaccept_passwords); /health 模型就绪才返回 200。监听 8187./start.sh 控制开关,./run_worker.sh 热重载)。
  • 网关gateway/):薄反向代理,健康检查/派发/鉴权/把 worker 的 *_base64 落盘改成 *_url。 唯一例外是接口4 在网关本机直接实现(调外网豆包,不转发 worker)。
  • 图片三选一:所有接口图片入参 image_file/image_url/image_base64 严格三选一(接口3 是 marked_image_*)。
  • 响应信封{code, message, request_id, data};业务错误用 codeHTTP 一律 200)。

base64 → URL 映射(网关落盘改写,递归进数组、可空保留 null)

接口 worker 字段(内部) 对外字段
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
3 hair_growth_image_base64(可空) hair_growth_image_url
5 hairline_images[].image_base64 hairline_images[].image_url
7 results[].image_base64 / results[].grown_image_base64(可空) results[].image_url / results[].grown_image_url
4 (网关本机产出,无图片字段,features 为 JSON 字符串)

实现建议:递归遍历 data,凡 key 以 _base64 结尾就落盘改 _url,自动覆盖嵌套/新增字段。 图片格式:接口1 标注图含透明用 PNG;接口2/3/5 是不透明照片用 JPG(小很多,~9×)。 网关落盘按内容嗅探扩展名(PNG 头→.png,否则 .jpg)。

错误码

1001 无法识别人像 1002 分辨率过低 1003 非正面 1004 gender 必填/非法(接口2/5/7)| 1006 >1MB 1007 图片参数错误(0或多个)/未预期异常 | 1008 格式不支持。


2. 五个接口实现简述

接口1 四庭七眼测量 /api/v1/face/measureworker

  • 做什么:正面照 → 四庭(顶/上/中/下庭) + 七眼(眼宽/脸宽/间距) 的 cm 与占比、5 个关键点坐标、一张透明底标注 PNG。
  • 怎么实现face_analysis/):MediaPipe Face Mesh 468+虹膜点 → solvePnP 姿态校验(非正面 1003) → 虹膜直径法定标(px→cm) → 眉心以下实测眉心以上用 BiSeNet 头发分割取真实发际线/头顶(方案B), 失败回退比例推算(方案A,hairline_source 透出)。标注图 numpy 向量化渐变线 + 思源黑体。返回 annotated_image_base64
  • 门槛可配:MIN_SHORT_SIDE/MIN_LONG_SIDE(默认600/800)、姿态阈值 FRONTAL_*_THR(默认30°)。

接口6 四庭七眼测量 v2 /api/v1/face/measure-v2worker)—— 复刻接口1

  • 做什么:与接口 1 完全一致(正面照 → 四庭七眼 cm 与占比 + 5 个关键点 + 标注 PNG)。
  • 怎么实现:与接口 1 共用 _face_measure_impl(),零额外逻辑。对外路径 /api/v1/face/measure-v2
  • 网关改动:新增路由 POST /api/v1/face/measure-v2,转发到 worker 同路径;base64→URL 改写无需改动。

接口2 C端生发 /api/v1/hair/growworker)—— 预览 + 生发图

  • 做什么:正面照 + gender(必填) + hair_style(必填,逗号分隔多选,如 1,2,3) → 指定发际线类型 N 组预览图(发际线叠在照片上) + 生发后图(植发3个月效果)。
  • 怎么实现hairline/):移植 head3d——MediaPipe(Tasks) + SegFormer 分割 + 17 锚点射线检测 → 502 点 mesh, 按 face_ext.obj 的 UV 把发际线贴图渲染到额头(预览)。生发:黑贴图渲染遮罩 → 调本机 ComfyUI 8182add_hair.json(Flux-2) 出图。关键坑obj 是重排序,需 INDEX_MAP_468 把 MP 序→OBJ 序。 返回 results[].image_base64 + grown_image_base64
  • hair_style 映射:female 1=ellipse 2=flower 3=heart 4=straight 5=wavemale 1=ellipse 2=inverse_arc 3=m 4=straight。

接口7 C端生发 v2 /api/v1/hair/grow-v2worker)—— 接口2同款,add_hair2 工作流

  • 做什么:与接口 2 完全一致(正面照 + gender + hair_style 逗号分隔多选 → N 组预览+生发图)。
  • 与接口 2 的唯一区别ComfyUI 工作流使用 add_hair2.jsonFlux-2 Klein 9b),输入/遮罩节点同为 26 SaveImage 输出节点为 75(自动检测)。其他参数、响应结构、错误码完全相同
  • 网关改动:在 gateway/app.py 新增路由 POST /api/v1/hair/grow-v2,转发到 worker 同路径即可(盲转发, base64→URL 改写逻辑无需改动,数组内图片字段已覆盖)。详见 网关待改动.md

接口3 B端生发 /api/v1/hair/grow-bworker)—— 马克笔发际线

  • 做什么:医生用马克笔在额头画好发际线,只传这一张划线图 → 检测线 → 生发图。输出 hair_growth_image_url + hairline_type="custom"
  • 怎么实现:检测算法源自 headmark——黑帽响应图 + 鬓角锚点(MediaPipe 21/251) + Dijkstra 最小路径(scikit-image), 比全局阈值鲁棒;路径平均响应过低→拒识(1001)。检测路径建遮罩,划线图原样送 ComfyUI(提示词清除黑线)。

接口4 用户特征 /api/v1/face/features网关本机

  • 做什么:照片 → 几十项面部特征(脸型/眉形/肤色/三庭五眼/四季色彩季型/量感/基因风格/性别…)。data.features 是 JSON 字符串。
  • 怎么实现gateway/,逻辑参考 worker face_features.py / /home/xsl/fuyan):调火山方舟 豆包视觉模型 doubao-seed-1-6-vision(OpenAI 兼容,base64 data URI 喂图),解析 JSON + 映射 6 个英文优先字段并保留全部中文。 无人脸→1001。唯一调外网的接口:网关需可达 ark.cn-beijing.volces.comAPI Key 走网关配置(不入 git)。

接口5 发际线PNG生成 /api/v1/hairline/generateworker

  • 做什么:照片 + gender(必填) → N 张发际线叠加图(同接口2预览) + 最佳(order1)发际线曲线的面部中间点坐标。
  • 怎么实现:复用接口2 的 502 点渲染管线,输出 N 张叠图 + best_hairline_center_point(眉心 x × 该处发际线 y)。无生发。

3. 部署 / 环境要点

workerGPU 机)

  • Python 3.12(系统 3.13 无 mediapipe/torch wheel);venv 在 ./venv,依赖 requirements.txt
  • numpy<2(1.26.4)scikit-image==0.24.0别升 0.25+,会顶 numpy≥2 顶崩 mediapipe)。
  • ⚠️ 本机 RTX 5090(sm_120)pinned torch 2.2.2(cu121) 只到 sm_90 → GPU 算子报 "no kernel image" 代码已自动回退 CPUBiSeNet/SegFormer CPU 推理可用)。要用 5090 GPU 需换 torch cu128(≥2.7)。
  • 模型权重/字体见 ../OFFLINE_ASSETS.mdBiSeNet/SegFormer/face_landmarker.task 本地。
  • 生发接口依赖本机 ComfyUI(8188)Flux-2,它自带支持 5090 的 torch);worker 只调其 HTTP API,不跑 Flux。 ComfyUI 开了 HTTP Basic Authuser admin + 密码);密码放 password.txt(不入 git) / worker_config.json.comfyui_password / 环境变量 COMFYUI_PASSWORDURL 用 COMFYUI_URL
  • worker_config.json(不入 git)accept_passwords(鉴权) + 鉴权头 X-Internal-Token

网关机

  • 很轻:FastAPI/uvicorn/httpx + 接口4 的 volcengine-python-sdk[ark](或直接 httpx 调,OpenAI 兼容)。
  • 不装 torch/mediapipe/opencv。配置 gateway/config.json(不入 git)workers 列表、shared_passwordark 的 api_key/base_url/model、public_base_url、超时(生发接口慢,request_timeout_seconds 调大 ≥120s)。
  • 托管 /static/annotations/(落盘的图),定期清理。

维护:本文为简要总览;字段以 接口文档.md 为准。各接口更细的算法推导可查 git 历史中已合并的旧技术方案文档。