33 measure.py
✅ 已验证 · 代码 code/measure.py · 不依赖 Krita
gauge.py 看整张画像不像人画的;measure.py 看一条边干不干净。探针和自检都用它。
指标
measure.metrics(img, mask=None):
| 指标 | 量法 | 含义 |
|---|---|---|
K90 | 边缘像素 level-set 曲率 ` | div(∇I/ |
W | 沿法线量 10–90% 过渡宽度(px)的中位 | 边有多软 |
aliased | W < 1.0 的边像素比例 | 硬台阶 |
mushy | W > 2.5 的比例 | 糊 |
empty | 假分辨率判据(dB) | 大画布上有没有对应尺度的信息 |
edge_ratio(img):高频能量落在梯度脊 3px 内的比例。
三条实现细节
- 边像素必须加脊约束(梯度局部最大 + 强度门槛 + 法线剖面对比度门槛),否则柔和渐变会被当成边,指标失去意义。
- W 的采样走原图,不走平滑图:在平滑图上量等于把硬边也抹成 σ 宽的过渡,硬边和抗锯齿边量出一样的 1.50。平滑图只用来定法线方向。
- 边像素 < 30 个时返回
None,不硬给数。
锚点
| K90 | 说明 | |
|---|---|---|
| 解析抗锯齿(真值) | 0.037–0.064 | 下限 |
| 矢量层渲染 | 0.033–0.042 | 比真值还光滑 |
coverage(smooth=1.2) 1x 升采样 | 0.052–0.15 | |
| 二值 mask 直接升采样 | 0.31 | 全是台阶 |
指标会骗人
empty 在放大目标上是反的。 它抓「假分辨率」,但一张忠实的 4x 重建确实不含 1024 以上的信息:
| empty | 判 | |
|---|---|---|
| 4x NEAREST(最偷懒的放大) | 39.93 dB | ○ 通过 |
| 4x LANCZOS(诚实放大) | 51.58 dB | ✗ 不通过 |
而往上撒均匀噪声能把 empty 拉回 41——照样通过。所以配 edge_ratio:origin 0.997 / 诚实 4x 0.996 / 均匀噪声作弊 0.607。门槛 empty < 44 且 edge_ratio > 0.90,两项都要在隐藏 Texture 层和后期之后量。
梯度 p90 不能当细节密度指标:被 grain 这类均匀噪声主导。grain 从 26 降到 8(画面明显更干净)后 p90 反而从 11.7 掉到 4.5。
颜色数受色域宽度影响:单一皮肤色的椭圆 8 种,参照图「脸部 538 色」含眼睛、嘴、发梢。不可比。
K90 对软边内容没有判别力:真实厚涂的边本来就该软,参照图 K90 0.68 / mushy 68.8%,和「脏」是两回事。厚涂/风景只跑 empty。
大气层的伤害
grain(overlay)和 grade(soft_light)有硬上限,超过就伤边缘(1000×1300 多图层人物,梯度 p99):
| 强度 | grain | grade | p99 |
|---|---|---|---|
| 0% | 0 | 0 | 317 |
| 50% | 13 | 32 | 264 |
| 100% | 26 | 64 | 210(掉 34%) |
安全区:grain ≤ 8 / grade ≤ 24。