diff --git a/.gitignore b/.gitignore index 9316736..2f2f0e8 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,6 @@ _grow*_resp.json # 测试素材图(体积大,不入 git) image/test/ -# 批量报告输出(200张生成图+原图,体积大,不入 git) +# 批量报告输出(生成图+原图,体积大,不入 git) static/report_hairline_v2/ +static/report_hairline_v2.zip diff --git a/face_analysis/head_mask.py b/face_analysis/head_mask.py index 4e5b865..e65cdf5 100644 --- a/face_analysis/head_mask.py +++ b/face_analysis/head_mask.py @@ -2,8 +2,8 @@ 流程(详见需求讨论): 1. MediaPipe 关键点检测。 -2. 底部分割线 = 关键点 [21,68,104,69,108,151,337,299,333,298,251] 的连线(左端21→中心151→右端251), - 再把左端点 21 水平延伸到图片最左边、右端点 251 水平延伸到图片最右边。 +2. 底部分割线 = 关键点 [162,71,68,104,69,108,151,337,299,333,298,301,389] 的连线(左端162→中心151→右端389), + 再把左端点 162 水平延伸到图片最左边、右端点 389 水平延伸到图片最右边。 3. 上半区 = 分割线以上区域(多边形填充:左边缘→弧线→右边缘→上边缘闭合)。 4. 头发分割:BiSeNet 与 SegFormer 各出一张 hair_mask(两套供对比)。 5. 闭合区域(含额头):每列从最顶端头发像素向下填充到分割线,把头发与画线之间的额头皮肤 @@ -22,8 +22,9 @@ import numpy as np from face_analysis.detector import detector from face_analysis.calibration import estimate_scale_factor, normalized_to_pixel -# 底部额头弧线关键点(图像上从左到右:左端 21 → 中心 151 → 右端 251) -BASELINE_IDX = [21, 68, 104, 69, 108, 151, 337, 299, 333, 298, 251] +# 底部分割线关键点(图像上从左到右:左端 162 → 中心 151 → 右端 389) +# 162/389 为左右最外侧端点(向图片左右边缘水平延长);中间含 71/301 等点构成弧线 +BASELINE_IDX = [162, 71, 68, 104, 69, 108, 151, 337, 299, 333, 298, 301, 389] CENTER_IDX = 151 # 内缩方向的目标点(额头中心) ERODE_CM = 1.2 # 外缘内缩距离(厘米,默认;可由入参覆盖) SEGFORMER_HAIR = 13 # jonathandinu/face-parsing 中 hair 类索引 @@ -50,12 +51,12 @@ def _baseline_points(landmarks, w, h): def _upper_region_mask(baseline_pts, w, h): """分割线以上区域(bool,H×W)。 - 多边形顶点:左上角 →(0, y54)→ 弧线各点 →(w-1, y284)→ 右上角,闭合后填充。 - 其中 54→左边缘、284→右边缘为两段水平延长线。 + 多边形顶点:左上角 →(0, y左端)→ 弧线各点 →(w-1, y右端)→ 右上角,闭合后填充。 + 左端/右端为两段水平延长线(向图片左右边缘延伸)。 """ - x54, y54 = baseline_pts[0] - x284, y284 = baseline_pts[-1] - poly = [(0, 0), (0, y54)] + baseline_pts + [(w - 1, y284), (w - 1, 0)] + x0, y0 = baseline_pts[0] + x1, y1 = baseline_pts[-1] + poly = [(0, 0), (0, y0)] + baseline_pts + [(w - 1, y1), (w - 1, 0)] mask = np.zeros((h, w), np.uint8) cv2.fillPoly(mask, [np.array(poly, np.int32)], 1) return mask.astype(bool) @@ -108,9 +109,9 @@ def _overlay(image, mask_bool, color, alpha=0.45): def _draw_baseline(image, baseline_pts, w): """画分割线(含左右水平延长线)+ 关键点,中心点 151 标红。""" out = image.copy() - y54 = baseline_pts[0][1] - y284 = baseline_pts[-1][1] - chain = [(0, y54)] + baseline_pts + [(w - 1, y284)] + y0 = baseline_pts[0][1] + y1 = baseline_pts[-1][1] + chain = [(0, y0)] + baseline_pts + [(w - 1, y1)] for a, b in zip(chain[:-1], chain[1:]): cv2.line(out, a, b, (0, 255, 255), 2, cv2.LINE_AA) for idx, p in zip(BASELINE_IDX, baseline_pts): diff --git a/static/test_simple.html b/static/test_simple.html new file mode 100644 index 0000000..ed125e4 --- /dev/null +++ b/static/test_simple.html @@ -0,0 +1,123 @@ + + + + + +发际线生发 + + + +
+

发际线生发

+
+
+
+ + +
+
+ + +
+ +
+
其余参数走默认值(pushed 遮罩 + multiband 融合)
+
+ + + +
+
+
上传图片、选发型、点生成
+
+
+
+ + + + + +