fix(接口4): worker 移除脸型 Mock + face_shape 改本机 MediaPipe 计算

- worker /api/v1/face/features 不再返回假成功数据,直接告知仅网关实现,
  避免本机误打 :8187 被 Mock 结果误导。
- 网关 ark_api_key 加载优先级改为 gateway/config.json 优先(原先误读
  worker_config.json 里的失效 key)。
- 接口4 face_shape 不再采信豆包结果,改用本机 face/face_shape_classifier.py
  (MediaPipe 7 类)计算覆盖;其余 5 项特征仍走豆包。
- 修复 face_shape_classifier 共享 FaceMesh 实例的线程安全问题(加锁),
  避免网关侧接口4 并发请求时崩溃/结果错乱。
- 新增 /api/v1/debug/face-shape 调试接口 + static/test_face_shape.html
  单图调试页(worker 侧)。
- 更新文档:网关机现在也需要 mediapipe/opencv-python/numpy<2。

⚠️ 部署前提醒:网关机需先安装 mediapipe==0.10.14 / opencv-python==4.10.0.84 /
numpy==1.26.4,否则接口4 会返回 1007「分析服务异常」。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
xsl
2026-07-29 14:41:49 +08:00
co-authored by Cursor
parent a748dfd1e5
commit 9fb5b486c0
8 changed files with 446 additions and 72 deletions
+8 -2
View File
@@ -14,6 +14,7 @@ face_shape_classifier.py
from __future__ import annotations
import math
import threading
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
@@ -343,6 +344,9 @@ def get_mixed_description(details: Dict) -> str:
_face_mesh = None
# mediapipe Solutions API 的单个 FaceMesh 实例不是线程安全的;被 web 服务用
# run_in_threadpool 并发调用时(如网关接口4、worker 调试接口)必须加锁串行化。
_face_mesh_lock = threading.Lock()
def _get_face_mesh():
@@ -502,7 +506,8 @@ def annotate_face_features(
if landmarks is None:
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
results = _get_face_mesh().process(rgb)
with _face_mesh_lock:
results = _get_face_mesh().process(rgb)
if not results.multi_face_landmarks:
raise ValueError("未检测到人脸关键点")
landmarks = results.multi_face_landmarks[0].landmark
@@ -741,7 +746,8 @@ def classify_from_image(
bgr = _load_image(image)
h, w = bgr.shape[:2]
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
results = _get_face_mesh().process(rgb)
with _face_mesh_lock:
results = _get_face_mesh().process(rgb)
if not results.multi_face_landmarks:
raise ValueError("未检测到人脸关键点")