feat: 新增42号人脸特征与72号B端关键点接口

This commit is contained in:
xsl
2026-11-07 20:35:55 +08:00
parent ccb9c142c4
commit c9a47a76c9
16 changed files with 983 additions and 25 deletions
+51 -10
View File
@@ -1585,6 +1585,49 @@ async def face_features(
return ok({"features": features})
@app.post(
"/api/v1/face/b-end-points",
summary="72号接口:B 端五个关键点",
tags=["人脸分析"],
)
async def face_b_end_points(
image_file: Optional[UploadFile] = File(default=None, description="上传图片文件(JPG/PNG)"),
image_url: Optional[str] = Form(default=None, description="图片 URL"),
image_base64: Optional[str] = Form(default=None, description="图片 base64"),
):
"""返回扶正裁切后的底图、六个白点标注层、坐标及距离。"""
from fastapi.concurrency import run_in_threadpool
from face_analysis.b_end_points import build_b_end_annotation
from face_analysis.special_points import NoFaceError, NonFrontalError
from hairline.framing import NoPortraitFaceError, decode_portrait
raw, error = await resolve_image_bytes(image_file, image_url, image_base64)
if error is not None:
return error
image = decode_portrait(raw)
if image is None:
return err(1008, "图片格式不支持(仅 JPG / PNG)")
try:
processed, overlay, data = await run_in_threadpool(build_b_end_annotation, image)
except (NoFaceError, NoPortraitFaceError):
return err(1001, "无法识别人像")
except NonFrontalError:
return err(1003, "角度问题,请上传正面照")
except ValueError as exc:
return err(1003, str(exc))
except Exception:
logger.exception("接口72 标注生成失败")
return err(1007, "标注生成失败")
data.update(
input_image_base64=_jpg_b64(processed),
annotated_image_base64=_rgba_png_b64(overlay),
)
return ok(data)
# ---------------------------------------------------------------------------
# 接口 5:发际线 PNG 生成(恢复 a3fddbb 历史版本)
# ---------------------------------------------------------------------------
@@ -1765,21 +1808,19 @@ async def _generate_hairline52_data(image, gender, hair_styles, use_mask=True,
compare_rules=False, normalize_image=True):
"""Share measurement coordinates between interface 52 and its comparison page."""
from fastapi.concurrency import run_in_threadpool
from hairline.service import generate_hairline_pngs_52, get_landmarker
from hairline.service import generate_hairline_pngs_52
original = image
framing, normalized_landmarks, landmarks_override = None, None, None
if normalize_image:
from hairline.framing import normalize_portrait
from mediapipe.framework.formats import landmark_pb2
landmarks = await run_in_threadpool(
get_landmarker().detect, cv2.cvtColor(image, cv2.COLOR_BGR2RGB), include_iris=True)
if landmarks is None:
from hairline.face_landmarks import to_landmark_list
from hairline.framing import NoPortraitFaceError, prepare_portrait
try:
image, normalized_landmarks, framing = await run_in_threadpool(prepare_portrait, image)
except NoPortraitFaceError:
return None, None
image, normalized_landmarks, framing = await run_in_threadpool(normalize_portrait, image, landmarks)
landmarks_override = landmark_pb2.NormalizedLandmarkList()
for point in normalized_landmarks:
landmarks_override.landmark.add(x=float(point[0]), y=float(point[1]), z=float(point[2]))
landmarks_override = to_landmark_list(normalized_landmarks)
fm, reference = None, None
try:
measured = await run_in_threadpool(_run_face_measure_data, image, landmarks_override=landmarks_override)