save code
This commit is contained in:
@@ -365,6 +365,7 @@ def _run_face_measure_data(image, variant="v1"):
|
||||
if variant == "v6":
|
||||
vd = result.vertical
|
||||
base_px = vd["upper_court_px"] + vd["middle_court_px"] + vd["lower_court_px"]
|
||||
# 接口6 是三庭:去掉顶庭相关字段(top_court_cm / ratios.top_court / landmarks.hair_top)
|
||||
data["four_courts"]["ratios"] = {
|
||||
"upper_court": round(vd["upper_court_px"] / base_px, 3),
|
||||
"middle_court": round(vd["middle_court_px"] / base_px, 3),
|
||||
@@ -375,27 +376,31 @@ def _run_face_measure_data(image, variant="v1"):
|
||||
result.upper_cm + result.middle_cm + result.lower_cm, 2)
|
||||
data["landmarks"].pop("hair_top", None)
|
||||
|
||||
# 七眼:从左到右共 7 段宽度(cm),仅接口1(v1)含人头最左/最右端线段。
|
||||
# eye1=左耳外段 eye2=左脸颊段 eye3=左眼 eye4=两眼间距 eye5=右眼 eye6=右脸颊段 eye7=右耳外段
|
||||
# 端线取自耳朵分割外缘(与标注图同源);某侧耳朵不可见 → 该侧端线缺失 → 对应 eye 置 null(保留键)。
|
||||
if variant != "v6":
|
||||
try:
|
||||
# 七眼段宽度(cm)。eye1=左耳外段 eye2=左脸颊 eye3=左眼 eye4=两眼间距 eye5=右眼 eye6=右脸颊 eye7=右耳外段。
|
||||
# eye2~eye6(5段)只用内部分点,接口1/6 共用;eye1/eye7 需耳朵分割端线,仅接口1 有。
|
||||
try:
|
||||
epts = result.eyes["points"]
|
||||
lcx, rcx = epts["left_cheek"][0], epts["right_cheek"][0]
|
||||
pc = result.px_per_cm
|
||||
inner_xs = [lcx, epts["left_outer"][0], epts["left_inner"][0],
|
||||
epts["right_inner"][0], epts["right_outer"][0], rcx]
|
||||
for i in range(5):
|
||||
a, b = inner_xs[i], inner_xs[i + 1]
|
||||
data["seven_eyes"][f"eye{i + 2}"] = (
|
||||
None if (a is None or b is None) else round((b - a) / pc, 2))
|
||||
if variant != "v6":
|
||||
# 接口1 额外算 eye1/eye7(左/右耳外段),需耳朵分割端线
|
||||
from face_analysis.annotation import _ear_edges_from_mask
|
||||
epts = result.eyes["points"]
|
||||
lcx, rcx = epts["left_cheek"][0], epts["right_cheek"][0]
|
||||
head_l, head_r = _ear_edges_from_mask(
|
||||
ear_mask, hair_mask,
|
||||
result.vertical["hair_top"][1], result.vertical["chin_tip"][1],
|
||||
lcx, rcx, (lcx + rcx) / 2)
|
||||
xs = [head_l, lcx, epts["left_outer"][0], epts["left_inner"][0],
|
||||
epts["right_inner"][0], epts["right_outer"][0], rcx, head_r]
|
||||
pc = result.px_per_cm
|
||||
for i in range(7):
|
||||
a, b = xs[i], xs[i + 1]
|
||||
data["seven_eyes"][f"eye{i + 1}"] = (
|
||||
None if (a is None or b is None) else round((b - a) / pc, 2))
|
||||
except Exception as seg_e: # noqa: BLE001
|
||||
logger.warning("七眼段宽度计算失败:%s", seg_e)
|
||||
data["seven_eyes"]["eye1"] = (
|
||||
None if (head_l is None) else round((lcx - head_l) / pc, 2))
|
||||
data["seven_eyes"]["eye7"] = (
|
||||
None if (head_r is None) else round((head_r - rcx) / pc, 2))
|
||||
except Exception as seg_e: # noqa: BLE001
|
||||
logger.warning("七眼段宽度计算失败:%s", seg_e)
|
||||
|
||||
return data, result, hair_mask, ear_mask
|
||||
|
||||
@@ -547,13 +552,13 @@ async def face_measure(
|
||||
输入用户正面照,返回:
|
||||
- 标注好四庭七眼数据的 **PNG 图片**(仅标注图层,不含人物)
|
||||
- 三庭(上庭/中庭/下庭)各段**厘米数值及占比**(**不含顶庭**)
|
||||
- 七眼(眼宽/脸宽/两眼间距)**厘米数值及占比**
|
||||
- 五眼段宽(eye2~eye6:左脸颊/左眼/两眼间距/右眼/右脸颊)**厘米数值**,另含眼宽/脸宽/两眼间距
|
||||
- 四个关键分界点的**原图像素坐标**(发际线/眉心/鼻翼下缘/下巴尖)
|
||||
|
||||
基于接口1 的变体,与接口1 的差异:
|
||||
- **去顶庭**:不画头顶横线、不返回顶庭数据;`face_total_height_cm` 为三庭之和
|
||||
- **竖线范围**:纵向竖线从发际线画到下巴尖(接口1 为头顶→下巴尖)
|
||||
- **不画人头最左/最右端线**:仅七眼 6 点共 5 段标尺,不取头发轮廓端线(接口1 为 8 线 7 段)
|
||||
- **不画人头最左/最右端线**:仅七眼 6 点共 5 段标尺(eye2~eye6),不取头发轮廓端线(接口1 为 8 线 7 段 eye1~eye7)
|
||||
|
||||
其余(箭头/虚线/字体/单位cm/七眼数据)与接口1 一致。
|
||||
|
||||
@@ -598,6 +603,7 @@ async def face_measure(
|
||||
"face_width_cm": 24.08,
|
||||
"inter_eye_distance_cm": 3.44,
|
||||
"ratios": {"eye_width": 0.143, "inter_eye_distance": 0.143},
|
||||
"eye2": 3.44, "eye3": 3.44, "eye4": 3.44, "eye5": 3.44, "eye6": 3.44,
|
||||
},
|
||||
"landmarks": {
|
||||
"hairline": {"x": 540, "y": 430},
|
||||
@@ -1050,8 +1056,10 @@ async def face_features(
|
||||
- `grown_image_url`:该发型的**生发图**(生发失败时为 `null`)。
|
||||
- `hairline_type`:发际线类型 key。
|
||||
worker 返回 `*_base64`,网关落盘后改写为 `*_url`。
|
||||
- `best_hairline_center_point`:**首个选中发型**的 middle 档发际线曲线**面部中间点**坐标,
|
||||
- `best_hairline_center_point`:**首个选中发型**的 **middle 档**发际线曲线**面部中间点**坐标,
|
||||
以**原图像素**为基准(左上角为原点,x 向右,y 向下)。
|
||||
- `high_hairline_center_point`:同上,**high 档**发际线中点。
|
||||
- `low_hairline_center_point`:同上,**low 档**发际线中点。
|
||||
""",
|
||||
responses={
|
||||
200: {
|
||||
@@ -1068,6 +1076,8 @@ async def face_features(
|
||||
{"hairline_type": "flower", "image_middle_base64": "iVBORw0KGgo...", "image_high_base64": "iVBORw0KGgo...", "image_low_base64": "iVBORw0KGgo...", "grown_image_base64": None, "order": 2},
|
||||
],
|
||||
"best_hairline_center_point": {"x": 540, "y": 430},
|
||||
"high_hairline_center_point": {"x": 540, "y": 380},
|
||||
"low_hairline_center_point": {"x": 540, "y": 480},
|
||||
"face_measure": {
|
||||
"face_total_height_cm": 13.76,
|
||||
"four_courts": {
|
||||
@@ -1155,10 +1165,14 @@ async def hairline_generate(
|
||||
if it.get("grown_png") else None),
|
||||
"order": it["order"],
|
||||
})
|
||||
c = res["best_center"]
|
||||
c = res.get("best_centers") or {}
|
||||
def _pt(p):
|
||||
return ({"x": p[0], "y": p[1]} if p else None)
|
||||
data = {
|
||||
"hairline_images": hairline_images,
|
||||
"best_hairline_center_point": ({"x": c[0], "y": c[1]} if c else None),
|
||||
"best_hairline_center_point": _pt(c.get("middle")),
|
||||
"high_hairline_center_point": _pt(c.get("high")),
|
||||
"low_hairline_center_point": _pt(c.get("low")),
|
||||
}
|
||||
|
||||
# face_measure:复用接口1测量数值(四庭/七眼 eye1~eye7/landmarks/姿态),不含标注图。
|
||||
@@ -1395,8 +1409,18 @@ async def hairline_grow(
|
||||
edge_erode_px: int = Form(default=3, description="贴图前遮罩内缩像素(防边缘露皮/光晕),默认 3"),
|
||||
denoising_strength: float = Form(default=0.6, description="换发型 webui 重绘强度(越大生发越激进),默认 0.6"),
|
||||
mb_levels: int = Form(default=5, description="多频段金字塔层数(2~6,越大低频色差抹得越宽),默认 5"),
|
||||
blend_method: str = Form(default="multiband", description="接缝融合方法:multiband(多频段金字塔,默认) | seamless(泊松无缝克隆) | two_stage(泊松→多频段两段式,大色差场景) | feather(高斯羽化) | alpha_gradient(距离变换内渐变)"),
|
||||
color_match: bool = Form(default=True, description="融合前 Reinhard 颜色迁移消除整体色差(对 multiband/feather/alpha_gradient 有效;seamless/two_stage 自带调色故跳过),默认 True"),
|
||||
color_match_strength: float = Form(default=1.0, description="颜色迁移强度(0~1,1=全迁移,<1 只迁移部分防过度改色),默认 1.0"),
|
||||
mb_feather_px: int = Form(default=1, description="多频段最细层掩码轻羽化像素(0=不羽化,消除发丝边缘锯齿),默认 1"),
|
||||
transition_band_px: int = Form(default=-1, description="keep-region 过渡带边距(-1=自动按层数 2**n,>=0 用绝对像素与层数解耦),默认 -1"),
|
||||
redraw: bool = Form(default=False, description="发际线带重绘开关:开启后额外跑一条分支——外推↔发际线带重绘,final输入,swapHair/Flux-2两路对比,结果单独展示(不替换final),默认 False"),
|
||||
inpainting_fill: int = Form(default=1, description="change_hair服务端重绘填充:0=保留原图(治染绿) | 1=填充噪声(默认/原始) | 2=纯色 | 3=潜变量噪声。默认 1"),
|
||||
mask_blur: int = Form(default=11, description="change_hair服务端遮罩边缘模糊像素(原始11,越大颜色越易从边缘渗透),默认 11"),
|
||||
mask_dilate_scale: float = Form(default=1.0, description="change_hair服务端遮罩膨胀缩放(1.0=原始核尺寸,<1收缩防越界),默认 1.0"),
|
||||
comfyui_prompt: Optional[str] = Form(default=None, description="redraw Flux-2路提示词,None用默认「补充遮罩区域的头发,加一点美颜」"),
|
||||
):
|
||||
"""接口11:发际线生发 + 分步可视化。遮罩固定 pushed、融合固定 multiband。"""
|
||||
"""接口11:发际线生发 + 分步可视化。遮罩固定 pushed,融合默认 multiband(可选 seamless/two_stage/feather)。"""
|
||||
raw, e = await resolve_image_bytes(image_file, image_url, image_base64)
|
||||
if e is not None:
|
||||
return e
|
||||
@@ -1410,8 +1434,12 @@ async def hairline_grow(
|
||||
from face_analysis.hairline_grow import generate_hairline_grow, NoFaceError, SwapError
|
||||
from uuid import uuid4 as _uuid4
|
||||
rid = _uuid4().hex[:8]
|
||||
logger.info("[%s] 接口11 收到请求: hairline_push_cm=%s hairline_edge=%s mb_levels=%s",
|
||||
rid, hairline_push_cm, hairline_edge, mb_levels)
|
||||
logger.info("[%s] 接口11 收到请求: hairline_push_cm=%s hairline_edge=%s mb_levels=%s "
|
||||
"blend=%s color_match=%s cm_strength=%s mb_feather_px=%s transition_band_px=%s redraw=%s "
|
||||
"inpainting_fill=%s mask_blur=%s mask_dilate_scale=%s",
|
||||
rid, hairline_push_cm, hairline_edge, mb_levels, blend_method,
|
||||
color_match, color_match_strength, mb_feather_px, transition_band_px, redraw,
|
||||
inpainting_fill, mask_blur, mask_dilate_scale)
|
||||
try:
|
||||
data = await run_in_threadpool(
|
||||
generate_hairline_grow, image, hairline_id,
|
||||
@@ -1419,7 +1447,11 @@ async def hairline_grow(
|
||||
edge_erode_px=edge_erode_px, denoising_strength=denoising_strength,
|
||||
gen_backend=gen_backend, hairgrow_strength=hairgrow_strength,
|
||||
mb_levels=mb_levels, hairline_push_cm=hairline_push_cm,
|
||||
hairline_edge=hairline_edge, rid=rid)
|
||||
hairline_edge=hairline_edge, blend_method=blend_method,
|
||||
color_match=color_match, color_match_strength=color_match_strength,
|
||||
mb_feather_px=mb_feather_px, transition_band_px=transition_band_px,
|
||||
redraw=redraw, inpainting_fill=inpainting_fill, mask_blur=mask_blur,
|
||||
mask_dilate_scale=mask_dilate_scale, comfyui_prompt=comfyui_prompt, rid=rid)
|
||||
except NoFaceError:
|
||||
return err(1001, "无法识别人像")
|
||||
except SwapError as se:
|
||||
|
||||
Reference in New Issue
Block a user