定位

pf-face-service 是图模工坊的独立人脸检测微服务——为”自动避脸裁剪”提供算力。

  • 运行时:Python 3.12+
  • Web 框架:FastAPI
  • 推理引擎:ONNX Runtime
  • 模型:SCRFD-500m(SCRFD 系列,500MB FLOPs 量级,CPU 即可实时跑)
  • 包管理:uv(比 pip / poetry 快一个数量级)
  • 端口:开发环境 3010

为什么单独建一个 Python 服务? TS 生态里没有质量稳定的人脸检测库;Python + ONNX 是工程最短路径。把它做成”接口稳定的微服务”而非”动态库 + 进程内 FFI”,让 pf-service 可以无脑横向扩展 Bun 进程,不必把 Python 模型一起复制。

接口协议

POST /api/face/detect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 请求
{
"imageUrl": "http://localhost:1280/pf-bucket/public/demo.png",
"minScore": 0.45,
"maxFaces": 10
}

// 成功响应(code = "0000")
{
"code": "0000",
"message": "检测成功",
"data": {
"image": { "width": 1080, "height": 1920 },
"faces": [{ "x": 0.33, "y": 0.18, "w": 0.22, "h": 0.22, "score": 0.98 }],
"modelVersion": "scrfd_500m_bnkps",
"elapsedMs": 84
}
}

x / y / w / h 都是**相对原图的归一化坐标 [0, 1]**——这样无论原图 1024 还是 4096,前后端拿到的语义一致,缩放计算放调用方做。

GET /health

1
2
3
4
5
6
7
8
9
10
11
12
{
"code": "0000",
"data": {
"status": "ok",
"service": "pf-face-service",
"version": "0.1.0",
"modelPath": "/app/models/scrfd_500m_bnkps.onnx",
"modelLoaded": true,
"modelVersion": "scrfd_500m_bnkps",
"reason": null
}
}

模型未加载或加载失败时,状态会变成 degradedreason 给出原因。

调用链路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌───────────┐
│ 三端前端 │
└─────┬─────┘
│ 1. POST /shared/face/detect

┌───────────────────────────┐
│ pf-service │
│ shared/faceRoutes.ts │ ← 鉴权、参数校验、统一响应
└─────┬─────────────────────┘
│ 2. HTTP POST → http://localhost:3010/api/face/detect

┌───────────────────────────┐
│ pf-face-service │
│ FastAPI + SCRFD/ONNX │
└─────┬─────────────────────┘
│ 3. urllib 拉取 imageUrl

MinIO / 外部图床

也可以由后端合成 Job 在内部直接调用 —— 用于带 faceSafe: true 节点的合成场景,先取人脸框、再算最佳裁剪窗口。

模型与配置

默认值 环境变量
模型路径 apps/pf-face-service/models/scrfd_500m_bnkps.onnx FACE_MODEL_PATH
推理输入尺寸 640 FACE_INPUT_SIZE
下载图片超时 8000 ms FACE_DETECT_TIMEOUT_MS

模型输出兼容策略(见 app/main.py):

  • 自动归一 [N, C][1, N, C] 两种 shape
  • 不兼容时启动阶段直接 fail-fast,避免运行期才暴露

部署

1
2
3
4
5
6
cd apps/pf-face-service

uv sync # 安装依赖
uv run uvicorn app.main:app --host 0.0.0.0 \
--port 3010 # 生产
uv run uvicorn app.main:app --reload --port 3010 # 开发

monorepo 一键启动:

1
bun run --filter @wangijun/pf-face-service dev

容器化建议:

  • 基础镜像:python:3.12-slim
  • 必装:libgl1(OpenCV)、libglib2.0-0
  • 入口:uvicorn app.main:app --host 0.0.0.0 --port 3010
  • 健康探针:GET /health

性能

维度 表现
单图检测(CPU, i7 12 代) ~ 60–100 ms
内存占用 约 300–500 MB(含模型常驻)
模型切换 替换 onnx 文件 + 重启
GPU 加速 替换 ONNX Runtime 为 CUDA EP(可选)

CPU 单实例即可满足绝大多数打印电商的请求量;高峰场景横向多实例 + 负载均衡。

安全与边界

  • 接口本身不带鉴权——务必置于内网,前端调用必须经过 pf-service/shared/face/detect
  • 下载图片失败 / 超时会返回明确错误码,不挂起
  • 仅支持 HTTP/HTTPS URL;不接受 base64 / 文件上传(保持职责单一)
  • 单进程单模型;多模型同时上线建议跑多副本而非进程内动态加载

二开提示

要换检测模型(例如想识别”二维码 / 文字行 / 物体”):

  1. 把同协议的 ONNX 模型放到 models/
  2. app/main.py 替换 model_zoo 调用,保持响应字段不变
  3. modelVersion 字段以便上游识别

下一步