# 发际线生发遮罩算法(pushed 模式) > 对应接口11 `/api/v1/hairline/grow`、接口12 `/api/v1/hairline/grow_v2`,`mask_type=pushed`。 > 代码:`face_analysis/hairline_grow.py`(`_extract_hairline` / `_pushed_mask` / `compute_mask`)。 ## 概述 pushed 模式是发际线生发的默认遮罩算法(接口11/12 的 `mask_type` 三选一:`eroded` / `closed` / `pushed`,当前只用 pushed)。它从头发分割结果中提取「头发/皮肤交界线」(发际线),以眉心为圆心逐点径向外推一段距离,与 baseline 组成闭合区域作为最终遮罩。这样遮罩顶部会覆盖现有头发下沿一小段,贴回生发结果时顶部与真头发重叠、过渡自然。 ## 算法流程(5 步) ``` ①-a baseline 分割线 ← 眉骨/glabella 关键点折线(含 151 中心点) ①-b 上半区 upper ← baseline 以上的区域(裁剪范围) ①-c 头发分割 hair_mask ← segformer/bisenet 的原始头发像素 ①-f 头发/皮肤交界线 ← 逐列取头发下沿,用 baseline 水平 y 线截断 ①-g 径向外推 + 闭合 ← 以 151 点为圆心逐点外推,与 baseline 组闭合区域 = 最终遮罩 ``` > ①-d(填充到基线 top_fill)、①-e(闭合区域 closed)是旧 eroded/closed 模式的中间产物,pushed 模式不走这条流程,前端不展示。 ### ①-a baseline 分割线 MediaPipe 人脸关键点 `[21,68,104,69,108,151,337,299,333,298,251]` 连成折线(左端 21 → 中心 151 → 右端 251),再向左右边缘水平延长。151 点(glabella/眉心)是后续径向外推的圆心。代码 `_baseline_points` / `_draw_baseline`。 ### ①-b 上半区 upper baseline 折线以上的多边形区域(`_upper_region_mask`)。作为后续裁剪范围,保证遮罩不越界到下半脸。 ### ①-c 头发分割 hair_mask segformer(默认)或 bisenet 得到的头发二值掩码。 ### ①-f 头发/皮肤交界线(核心改动) 代码 `_extract_hairline`: 1. **逐列取头发下沿**:对每个 x 列,取 `hair_mask` 中最靠下的头发像素 y 坐标(`column` 模式)。 2. **baseline 水平截断**:逐列计算 baseline 折线的 y 值 `baseline_y[x]`,只保留「发际线 y < baseline_y」的列(baseline 线以上 = 额头+发际线区域;baseline 以下 = 脸下半部,丢弃)。 3. 结果 `hairline_y[x]`:长度 = 图宽的数组,baseline 以下或无头发处为 NaN。 **关键点**:截断是**水平方向**用 baseline 的 y 值切割,不是竖线、不是 baseline 的 x 范围。这样得到的是真实的头发/皮肤交界弧线。 > `mode=contour` 用 `cv2.findContours` 取轮廓代替逐列下沿,但实测它会混入头顶边缘(y 异常偏小),**推荐用 `column`(默认)**。 ### ①-g 径向外推 + 闭合区域(最终遮罩) 代码 `_pushed_mask`: 1. **逐点径向外推**:圆心 = 151 点 (cx, cy)。对每个发际线有效点 (x, y),计算从圆心指向它的单位向量 `(ux, uy)`,外推后新位置 `(x + ux·push_px, y + uy·push_px)`。额头正上方的点往上推,两侧的点斜向外上推。`push_px = hairline_push_cm × px_per_cm`(默认 1cm)。 2. **按列归并 + 插值填补锯齿**:外推后新 x 坐标可能落在相邻列,逐列取最靠上的 y 作为遮罩顶界;径向归并产生的空列用线性插值填补,保证遮罩顶界连续(否则会被连通域分析切成碎片)。 3. **与 baseline 组闭合区域**:逐列从外推后发际线 `pushed_y[x]` 填充到 `baseline_y[x]`,得到遮罩。 4. **后处理**:`& upper` 去掉越界部分,`_largest_cc` 保留最大连通域。 最终遮罩 = `[径向外推发际线 → baseline]` 之间的闭合区域,顶部含现有头发下沿约 push_cm,底部到 baseline。 ## 关键参数 | 参数 | 默认 | 说明 | |---|---|---| | `mask_type` | `eroded`(接口默认)/ `pushed`(当前推荐) | pushed 走上述流程;eroded/closed 走旧的 top_fill→closed/eroded 流程 | | `hairline_push_cm` | 1.0 | 发际线径向外推距离(厘米),= push_px / px_per_cm。`px_per_cm` 由虹膜直径标定 | | `hairline_edge` | `column` | 发际线提取方式:`column`(逐列下沿,推荐)/ `contour`(轮廓,易混入头顶) | ## 与旧模式(eroded/closed)的区别 | | eroded/closed | pushed(当前) | |---|---|---| | 遮罩顶界 | top_fill(头发向下填充含额头)外缘内缩 | 头发/皮肤交界线 径向外推 | | 是否用 baseline 截断 | 用 baseline 组上半区 upper | 用 baseline 水平 y 线截断发际线 + 作遮罩底界 | | 遮罩形状 | 整个额头闭合区域 | 发际线附近一带(顶部覆盖现有头发 push_cm) | ## 调试 - 调试页:`http://:8187/static/test_interface11_debug.html`(带前后端日志面板、下载日志按钮) - 后端日志:`/home/xsl/hair/log/hairline_grow.log`(按 `[rid]` 关联一次请求),下载接口 `/api/v1/debug/hairline_log?rid=&tail=500` - 可视化步骤:①-a baseline / ①-b upper / ①-c 头发分割 / ①-f 交界线 / ①-g 外推+遮罩 / 最终遮罩 / 生成 / 贴回 / 融合