Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

5 亚像素边界:从抗锯齿边解 α

✅ 已验证 · 代码 code/contour.pyalpha_from_image()

丢掉的信息在哪

origin 是一张抗锯齿的图。在两个部件交界的像素上,颜色是两侧颜色的线性混合

c = α · C_p + (1 − α) · C_q

α 就是这个像素有多大比例属于部件 p——它编码着边界的亚像素位置第 4 章的分割把每个像素硬判给一个部件,α 在那一刻被扔了。而它正是「边平不平」的全部依据:一条从二值 mask 里提出来的边,位置误差可达 ±0.5 像素,放大 8 倍就是 ±4 像素的抖动。

解 α

把 c 投影到 C_p − C_q 这条直线上:

α = ((c − C_q) · (C_p − C_q)) / |C_p − C_q|²

两个关键细节:

C_p、C_q 不能用固有色。 有明暗时,固有色离该处的实际颜色很远,解出来的 α 会系统性偏。做法是各取本侧最近的干净像素:把 mask 腐蚀掉 band=2 层,剩下的核心区用最近邻向外推成整幅的场。所以 C_p / C_q 是随位置变的。

两侧颜色太接近时退回二值。 |C_p − C_q| < min_sep(12) 时 α 病态,硬解会把噪声放大成边界抖动。这些地方 α 直接取 0 或 1。

只在边界带(mask 膨胀 2 层 ∖ 腐蚀 2 层)里解,其余地方 α = mask。

实测

Hair_Front(90,473 px):

解出分数覆盖的像素5,896(6.5% 的部件像素是亚像素边)
边界带 α 分布 p10 / p50 / p900.05 / 0.30 / 0.89
面积:硬 mask → 分数90,473 → 88,135(−2.58%)

面积缩小是对的——硬 mask 把任何标签为 p 的像素都算作整个像素,哪怕它只有 30% 属于 p。

它值多少

第 7 章的自检里,同一套矢量化,喂二值 mask 和喂 α 场的差别:

输入K90(8x)aliased%形状偏差
二值 mask(最佳参数)0.04482.80.0715
α 场0.04222.60.0485

形状准确度好 1.5 倍。这是 origin 里唯一残存的亚像素信息,不用就没了。

也用于色阶边界

同一个函数加 domain= 参数,可以在部件内部解色阶之间的 α:这时「另一侧」是同部件里更浅或更深的那档,而不是隔壁部件。搞混的话色阶边会去跟部件边对齐,位置整体偏掉。第 6 章cel.vectorize() 就是这么调的。