终于修改对了,还是opus
This commit is contained in:
+18
-20
@@ -13,8 +13,8 @@ pushed 模式是发际线生发的默认遮罩算法(接口11/12 的 `mask_typ
|
||||
①-a baseline 分割线 ← 眉骨/glabella 关键点折线(含 151 中心点)
|
||||
①-b 上半区 upper ← baseline 以上的区域(裁剪范围)
|
||||
①-c 头发分割 hair_mask ← segformer/bisenet 的原始头发像素
|
||||
①-f 头发/皮肤交界线 ← 逐列取头发下沿,用 baseline 水平 y 线截断
|
||||
①-g 径向外推 + 闭合 ← 以 151 点为圆心逐点外推,与 baseline 组闭合区域 = 最终遮罩
|
||||
①-f 头发内轮廓线 ← 取头发轮廓中朝脸一侧的那段(额头弧+两侧到下颌),有序折线
|
||||
①-g 径向外推 + 成带 ← 以 151 点为圆心把内轮廓逐点向外推 push_px,内轮廓↔外推线之间的带 = 最终遮罩
|
||||
```
|
||||
|
||||
> ①-d(填充到基线 top_fill)、①-e(闭合区域 closed)是旧 eroded/closed 模式的中间产物,pushed 模式不走这条流程,前端不展示。
|
||||
@@ -31,44 +31,42 @@ baseline 折线以上的多边形区域(`_upper_region_mask`)。作为后续
|
||||
|
||||
segformer(默认)或 bisenet 得到的头发二值掩码。
|
||||
|
||||
### ①-f 头发/皮肤交界线(核心改动)
|
||||
### ①-f 头发内轮廓线(核心改动)
|
||||
|
||||
代码 `_extract_hairline`:
|
||||
代码 `_extract_hairline`。目标是提取「头发区域朝脸一侧的内轮廓线」:额头弧 + 左右两侧鬓角/脸颊边界,一直向下到下颌,是一条**有序折线**(不再是逐列一个 y 的数组,因为两侧近乎竖直、一个 x 对多个 y)。
|
||||
|
||||
1. **逐列取头发下沿**:对每个 x 列,取 `hair_mask` 中最靠下的头发像素 y 坐标(`column` 模式)。
|
||||
2. **baseline 水平截断**:逐列计算 baseline 折线的 y 值 `baseline_y[x]`,只保留「发际线 y < baseline_y」的列(baseline 线以上 = 额头+发际线区域;baseline 以下 = 脸下半部,丢弃)。
|
||||
3. 结果 `hairline_y[x]`:长度 = 图宽的数组,baseline 以下或无头发处为 NaN。
|
||||
1. **取头发轮廓**:`hair_mask` 最大连通域,`cv2.findContours(RETR_EXTERNAL, CHAIN_APPROX_NONE)` 取稠密、保序的外轮廓点。
|
||||
2. **内侧判定**:轮廓同时含「朝背景的外侧剪影」和「朝脸的内轮廓」。对每个轮廓点,朝脸中心 151 方向采样 `sample_px`(≈0.4cm)像素,落点若是**非头发**像素 → 该点朝向脸(内轮廓);否则是外侧剪影,丢弃。
|
||||
3. **取最长连续内侧段**:内轮廓点在闭合轮廓上本是一段连续弧,先做 1D 环形闭运算填掉判定抖动的小缝,再取最长连续 True 段并保序。
|
||||
4. **下颌截断**:丢掉 y > `chin_y`(下巴关键点 152 的 y)的点,把两侧末端截到下颌一带 → 得到「环脸」内轮廓弧。
|
||||
|
||||
**关键点**:截断是**水平方向**用 baseline 的 y 值切割,不是竖线、不是 baseline 的 x 范围。这样得到的是真实的头发/皮肤交界弧线。
|
||||
|
||||
> `mode=contour` 用 `cv2.findContours` 取轮廓代替逐列下沿,但实测它会混入头顶边缘(y 异常偏小),**推荐用 `column`(默认)**。
|
||||
**关键点**:不再用 baseline 做水平截断、也不再逐列取下沿;截断改为「朝脸内侧」判定 + 下颌 y 截断,因此能同时拿到额头弧和两侧竖直边界。
|
||||
|
||||
### ①-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` 保留最大连通域。
|
||||
1. **逐点径向外推**:圆心 = 151 点 (cx, cy)。对内轮廓上每个点 (x, y),沿「从圆心指向它」的单位向量 `(ux, uy)` **向外**(远离脸中心 = 推进现有头发)外推 `push_px`,得到外推线(黄线)点 `(x + ux·push_px, y + uy·push_px)`。`push_px = hairline_push_cm × px_per_cm`(默认 1cm)。
|
||||
2. **逐列归并**:只取外推线中落在 baseline 以上的点,逐列取最靠上的 y 作为遮罩顶界 `pushed_y[x]`;空列线性插值填补。两侧鬓角落到 baseline 以下的段落自然被排除。
|
||||
3. **与 baseline 组闭合区域**:逐列从 `pushed_y[x]` 填充到 `baseline_y[x]`(仅 `pushed_y < baseline_y` 的列),`& upper` 去越界、`_largest_cc` 保留最大连通域。
|
||||
|
||||
最终遮罩 = `[径向外推发际线 → baseline]` 之间的闭合区域,顶部含现有头发下沿约 push_cm,底部到 baseline。
|
||||
最终遮罩 = **外推发际线(①-g 黄线)与 baseline 分割线(①-a)组成的闭合区域**:顶界=外推发际线(覆盖现有头发约 push_cm),底界=baseline。与旧逻辑一致,区别只是 `pushed_y` 现在来自修正后的整条内轮廓,额头弧已延伸到两侧鬓角,额头遮罩宽度不再被截短。
|
||||
|
||||
## 关键参数
|
||||
|
||||
| 参数 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `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`(轮廓,易混入头顶) |
|
||||
| `hairline_push_cm` | 1.0 | 内轮廓径向外推距离(厘米),= push_px / px_per_cm。`px_per_cm` 由虹膜直径标定 |
|
||||
| `hairline_edge` | `column` | 兼容保留的入参;新版内轮廓提取(轮廓+内侧判定)不再按它分支,取值不影响结果 |
|
||||
|
||||
## 与旧模式(eroded/closed)的区别
|
||||
|
||||
| | eroded/closed | pushed(当前) |
|
||||
|---|---|---|
|
||||
| 遮罩顶界 | top_fill(头发向下填充含额头)外缘内缩 | 头发/皮肤交界线 径向外推 |
|
||||
| 是否用 baseline 截断 | 用 baseline 组上半区 upper | 用 baseline 水平 y 线截断发际线 + 作遮罩底界 |
|
||||
| 遮罩形状 | 整个额头闭合区域 | 发际线附近一带(顶部覆盖现有头发 push_cm) |
|
||||
| 遮罩来源 | top_fill(头发向下填充含额头)外缘内缩 | 头发内轮廓线 径向外推成带 |
|
||||
| 截断方式 | 用 baseline 组上半区 upper | 内侧判定 + 下颌 y 截断(不再用 baseline) |
|
||||
| 遮罩形状 | 整个额头闭合区域 | 沿内轮廓的环脸带(额头弧+两侧,压住现有头发 push_cm) |
|
||||
|
||||
## 调试
|
||||
|
||||
|
||||
Reference in New Issue
Block a user