"""姿态校验测试:真实正面图通过 + 合成大 yaw 拒绝 + 阈值门控。""" import cv2 import numpy as np from conftest import fixture, _LM from face_analysis import pose from face_analysis.detector import detector from face_analysis.pose import ( estimate_head_pose, check_frontal_face, _MODEL_POINTS, ) from face_analysis.face_mesh_landmarks import PNP_INDICES def _project_model_with_yaw(yaw_deg, W=1000, H=1000, tz=1000.0): """把 _MODEL_POINTS 绕 Y 轴旋转 yaw 后投影回像素,构造伪 landmarks。 与 pose.estimate_head_pose 使用同一相机模型,故应能近似反解出该 yaw。 """ a = np.radians(yaw_deg) Ry = np.array([[np.cos(a), 0, np.sin(a)], [0, 1, 0], [-np.sin(a), 0, np.cos(a)]]) focal = float(W) cx, cy = W / 2, H / 2 lm = [_LM(0.5, 0.5) for _ in range(478)] for idx, X in zip(PNP_INDICES, _MODEL_POINTS): Xc = Ry @ X + np.array([0, 0, tz]) u = focal * Xc[0] / Xc[2] + cx v = focal * Xc[1] / Xc[2] + cy lm[idx] = _LM(u / W, v / H) class _Holder: landmark = lm return _Holder() def test_frontal_image_passes(): img = cv2.imread(fixture("frontal.jpg")) h, w = img.shape[:2] lms = detector.detect(img) assert lms is not None assert check_frontal_face(lms, w, h) is True def test_synthetic_large_yaw_recovered_and_rejected(): holder = _project_model_with_yaw(40.0) yaw, pitch, roll = estimate_head_pose(holder, 1000, 1000) # 反解出的 yaw 量级应接近 40°(符号取决于约定) assert abs(yaw) > 30 # 默认阈值(30°)下应判为非正面 assert check_frontal_face(holder, 1000, 1000) is False def test_threshold_gating_rejects_when_zeroed(): """阈值门控逻辑:阈值压到 0,则任何非零角度都应被拒。""" img = cv2.imread(fixture("frontal.jpg")) h, w = img.shape[:2] lms = detector.detect(img) assert check_frontal_face(lms, w, h, yaw_thr=0, pitch_thr=0, roll_thr=0) is False def test_pose_none_is_not_blocked(): """solvePnP 失败(返回 None)时不拦截,check_frontal_face 返回 True。""" assert pose.estimate_head_pose.__doc__ # 占位,确保导入