feat(worker): 接口1 四庭七眼测量真实实现(替换 Mock)

worker 侧从 Mock 替换为真实算法:
- face_analysis 包:detector(MediaPipe 478点) / pose(solvePnP 姿态) /
  calibration(虹膜直径法) / hair_segmenter+bisenet_model(方案B 头发分割) /
  measure(方案A兜底+B/A决策+七眼+换算) / annotation(numpy渐变线+中文标注)
- app.py:/api/v1/face/measure 接真实实现,返回 annotated_image_base64
  (不落盘不拼URL,落盘由网关做);加 X-Internal-Token 鉴权、/health 就绪态、
  可配置分辨率门槛、异常兜底
- 部署:start.sh/run_worker.sh/hair-worker.service 监听 8187;worker_config 示例
- 测试 tests/:Tier1合成真值<1e-6 + Tier2缩放不变 + Tier3叠加 + 错误码集成 +
  数值回归,pytest 24 项全绿
- 文档补实测基线表 + RTX5090/torch 说明

注:worker 为 RTX 5090(sm_120),pinned torch 2.2.2(cu121) 只到 sm_90,
BiSeNet 已自动回退 CPU(方案B 正常);要用 GPU 需换 torch cu128(≥2.7)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
xsl
2026-06-14 16:07:28 +08:00
co-authored by Claude Opus 4.8
parent 3b706ec0ef
commit 8d3b145111
25 changed files with 1806 additions and 35 deletions
@@ -841,6 +841,39 @@ torchvision==0.17.2
---
## 12. 实测基线数值表(frontal.jpg682×811
worker 侧首次实现后的实测值,作为数值回归基线(`tests/test_pipeline.py`)。
| 量 | 方案 A(兜底,mask=None) | 方案 B(分割,主) |
|----|--------------------------|--------------------|
| hairline_source | estimated | segmentation |
| 顶庭 cm | 5.06 | 6.51 |
| 上庭 cm | 6.07 | 4.08 |
| 中庭 cm | 7.23(实测) | 7.23(实测) |
| 下庭 cm | 5.64(实测) | 5.64(实测) |
| 全脸高 cm | 24.01 | 23.47 |
| 眼宽 cm | 2.52 | 2.52 |
| 脸宽 cm | 12.49 | 12.49 |
| 两眼间距 cm | 3.24 | 3.24 |
| px_per_cm(虹膜法) | 15.06 | 15.06 |
| head_pose (yaw/pitch/roll) | 15.73 / 20.93 / 3.40 | 同左 |
> 说明:中/下庭、七眼、px_per_cm、姿态在两方案下一致(均为实测);顶/上庭方案 A 为
> 比例推算、方案 B 为分割实测,二者差异正体现"方案 B 取真实发际线/头顶"的价值。
> 数值回归测试固定走方案 A(确定性、与 torch 无关),容差 1%。
### 运行环境实测要点(worker = RTX 5090
- **GPU 架构兼容性**:本 worker 为 **RTX 5090compute capability 12.0 / Blackwell, sm_120**
锁定的 `torch==2.2.2+cu121` 仅编译到 **sm_90**,在 5090 上执行 CUDA 算子会报
`CUDA error: no kernel image is available``hair_segmenter._select_device()` 已做
一次小算子探测,失败自动回退 **CPU**BiSeNet CPU 推理约 0.31s/张,方案 B 正常可用)。
- **要真正用上 5090 GPU**:需换装支持 sm_120 的构建(**torch cu128,≥2.7**,配套
torchvision),代码无需改动(`_select_device` 会自动选 CUDA)。可设 `FORCE_CPU=1` 强制 CPU。
---
> **文档版本**: v2.0
> **创建日期**: 2026-06-13v2.0 修订:修复循环论证/分辨率/字体/numpy 等问题,引入方案 B 分割 + solvePnP 姿态)
> **依赖模型**: MediaPipe Face Mesh (468 landmarks) + BiSeNet face-parsing (头发分割)