feat(测量): 接口1/6 返回与标注图一致的竖线坐标
前端需要按标注图重绘竖线,坐标与画线共用同一套几何。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+59
-16
@@ -180,6 +180,60 @@ def _ear_edges_from_mask(ear_mask, hair_mask, y0, y1, left_cheek_x, right_cheek_
|
||||
return head_l, head_r
|
||||
|
||||
|
||||
# 返回给接口的竖线名 → eyes["points"] 里的键。从左到右的解剖顺序。
|
||||
_VERTICAL_INNER_LINES = (
|
||||
("left_cheek", "left_cheek"),
|
||||
("left_eye_outer", "left_outer"),
|
||||
("left_eye_inner", "left_inner"),
|
||||
("right_eye_inner", "right_inner"),
|
||||
("right_eye_outer", "right_outer"),
|
||||
("right_cheek", "right_cheek"),
|
||||
)
|
||||
|
||||
|
||||
def vertical_line_geometry(measure_result, image_width, image_height,
|
||||
ear_mask=None, hair_mask=None, variant="v1"):
|
||||
"""标注图竖线的像素几何,供画线与接口返回共用。
|
||||
|
||||
返回 {"y_top", "y_bottom", "lines": [{"name", "x"}, ...]}。
|
||||
x / y 均为原图像素整数(四舍五入)。lines 从左到右,只含实际画出的线。
|
||||
variant="v1":竖线从头顶上方延伸到下巴下方;有耳朵外缘时含
|
||||
left_head_edge / right_head_edge。
|
||||
variant="v6":竖线从发际线到下巴尖,不含人头两端。
|
||||
"""
|
||||
v = measure_result.vertical
|
||||
s = min(image_width, image_height)
|
||||
if variant == "v6":
|
||||
order = ["hairline", "brow_center", "nose_bottom", "chin_tip"]
|
||||
else:
|
||||
order = ["hair_top", "hairline", "brow_center", "nose_bottom", "chin_tip"]
|
||||
ys = [v[name][1] for name in order]
|
||||
|
||||
pts = measure_result.eyes["points"]
|
||||
named = [(public, float(pts[internal][0])) for public, internal in _VERTICAL_INNER_LINES]
|
||||
if variant != "v6":
|
||||
lcx, rcx = pts["left_cheek"][0], pts["right_cheek"][0]
|
||||
head_l, head_r = _ear_edges_from_mask(
|
||||
ear_mask, hair_mask, ys[0], ys[-1], lcx, rcx, (lcx + rcx) / 2)
|
||||
if head_l is not None:
|
||||
named.append(("left_head_edge", float(head_l)))
|
||||
if head_r is not None:
|
||||
named.append(("right_head_edge", float(head_r)))
|
||||
named.sort(key=lambda item: item[1])
|
||||
|
||||
fy0, fy1 = ys[0], ys[-1]
|
||||
over = max(6, round(s * 0.030))
|
||||
if variant == "v6":
|
||||
y_top, y_bottom = fy0, fy1
|
||||
else:
|
||||
y_top, y_bottom = fy0 - over, fy1 + over
|
||||
return {
|
||||
"y_top": int(round(y_top)),
|
||||
"y_bottom": int(round(y_bottom)),
|
||||
"lines": [{"name": name, "x": int(round(x))} for name, x in named],
|
||||
}
|
||||
|
||||
|
||||
def create_annotated_image(image_bgr, measure_result, ear_mask=None, hair_mask=None,
|
||||
variant="v1"):
|
||||
"""生成标注图层 PNG(透明底 RGBA,尺寸同原图)。返回 PIL.Image。
|
||||
@@ -221,19 +275,11 @@ def create_annotated_image(image_bgr, measure_result, ear_mask=None, hair_mask=N
|
||||
ys = [v[name][1] for name in order]
|
||||
|
||||
pts = measure_result.eyes["points"]
|
||||
seven_keys = ["left_cheek", "left_outer", "left_inner",
|
||||
"right_inner", "right_outer", "right_cheek"]
|
||||
base_xs = [pts[k][0] for k in seven_keys]
|
||||
if variant == "v6":
|
||||
xs = sorted(base_xs) # 接口6:仅七眼 6 点,不画人头最左/最右端线
|
||||
else:
|
||||
# 人头最左/最右:取自耳朵分割掩膜外缘(方案B,类7/8),脸纵向范围内统计。
|
||||
# 看不到耳朵(被头发/侧脸遮挡 → 掩膜空)或外缘未越过脸颊线则省略该侧端线。
|
||||
lcx, rcx = pts["left_cheek"][0], pts["right_cheek"][0]
|
||||
head_l, head_r = _ear_edges_from_mask(
|
||||
ear_mask, hair_mask, ys[0], ys[-1], lcx, rcx, (lcx + rcx) / 2)
|
||||
head_xs = [x for x in (head_l, head_r) if x is not None]
|
||||
xs = sorted(base_xs + head_xs) # 自左向右(6 或 7/8 点)
|
||||
# 竖线 x 与纵向范围与接口返回的 vertical_lines 同一份几何。
|
||||
geom = vertical_line_geometry(
|
||||
measure_result, w, h, ear_mask=ear_mask, hair_mask=hair_mask, variant=variant)
|
||||
xs = [line["x"] for line in geom["lines"]]
|
||||
v_top, v_bot = geom["y_top"], geom["y_bottom"]
|
||||
|
||||
# 人脸/人头包围盒
|
||||
fx0, fx1 = xs[0], xs[-1]
|
||||
@@ -241,9 +287,6 @@ def create_annotated_image(image_bgr, measure_result, ear_mask=None, hair_mask=N
|
||||
face_cx = (fx0 + fx1) / 2
|
||||
over = max(6, round(s * 0.030)) # 线超出包围盒的长度(参考图风格)
|
||||
face_half = (fx1 - fx0) / 2 + over # 横线超出最外侧竖线一点
|
||||
# 竖线纵向范围:v6 = 发际线→下巴尖(不超出);v1 = 头顶→下巴尖并两端超出一点
|
||||
v_top = fy0 if variant == "v6" else fy0 - over
|
||||
v_bot = fy1 if variant == "v6" else fy1 + over
|
||||
|
||||
# --- 1. 横向分界线(渐变,覆盖头宽并超出一点) ---
|
||||
for cy in ys:
|
||||
|
||||
Reference in New Issue
Block a user