接口1: 标注图人头最左/最右竖线改用耳朵分割外缘

- 最左/最右竖线由「头发轮廓」改为同一 BiSeNet 的耳朵类(7/8)外缘,
  看不到耳朵(被头发/侧脸遮挡→掩膜空)则该侧不画线
- 外耳轮廓常被误标成头发: 从耳朵外缘沿紧邻前景(耳∪发)按脸宽自适应
  外扩回收(上限 face_w*0.045, 遇背景间隙即停)
- 先按人脸包围盒裁剪再分割: BiSeNet 在紧裁人脸上训练, 整张全身/街拍图
  脸偏小会严重欠分割、丢耳朵; 裁剪后映射回原图, 耳朵稳定可分
- segment_hair_and_ears() 单次推理同出 hair_mask + ear_mask

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
xsl
2026-06-24 21:36:23 +08:00
co-authored by Claude Opus 4.8
parent 5fd7d05d26
commit a9439e4975
3 changed files with 124 additions and 36 deletions
+12 -5
View File
@@ -362,17 +362,24 @@ async def _face_measure_impl(image_file, image_url, image_base64, variant="v1"):
return None, err(1003, "角度问题,请上传正面照")
head_pose = estimate_head_pose(landmarks, w, h)
# 7. 头发分割(方案 B),失败传 None 由 measure 内部回退方案 A
# 7. 头发/耳朵分割(方案 B,单次推理),失败传 None 由 measure 内部回退方案 A
# 传人脸包围盒 → 先按人脸裁剪再分割(全身/街拍等脸偏小的图也能稳出耳朵)。
hair_mask = None
ear_mask = None
try:
from face_analysis.hair_segmenter import get_segmenter
hair_mask = get_segmenter().segment_hair(image)
from face_analysis.calibration import normalized_to_pixel
pxs = [normalized_to_pixel(p, w, h) for p in landmarks.landmark]
face_box = (min(p[0] for p in pxs), min(p[1] for p in pxs),
max(p[0] for p in pxs), max(p[1] for p in pxs))
hair_mask, ear_mask = get_segmenter().segment_hair_and_ears(image, face_box=face_box)
except Exception as seg_e: # noqa: BLE001
logger.warning("头发分割失败,回退方案A%s", seg_e)
logger.warning("头发/耳朵分割失败,回退方案A%s", seg_e)
# 8. 测量 + 标注图
# 8. 测量 + 标注图hair_mask 定发际线/头顶;ear_mask 定人头最左/最右竖线)
result = measure_face(landmarks, hair_mask, w, h, head_pose=head_pose)
annotated = create_annotated_image(image, result, hair_mask=hair_mask, variant=variant)
annotated = create_annotated_image(
image, result, ear_mask=ear_mask, hair_mask=hair_mask, variant=variant)
buf = BytesIO()
annotated.save(buf, format="PNG")