feat(boxes): 新增传统 CV 定位方法(--method cv),不依赖分类模型
背景差分:大半径方框均值估背景 → 残差(可选极性)→ 平滑 → 有无判定 → 按峰值归一化, 再交给与 gradcam / shap 同一套 cam_to_boxes 出框。只依赖 numpy。 与归因方法的差别:不需要 --checkpoint,分类器还没训练出来就能先跑一版框,也不受分类 精度影响;类别只能来自目录(--target label);找的是偏离局部背景的区域,而不是模型 据以判断类别的区域,所以对局部目标好、对铺满整图的纹理类给不出有意义的框。 有无缺陷是显式判定的:残差峰值要同时高于绝对对比度下限与噪声水平(MAD 估计)的 15 倍, 否则返回全零图。这条是必需的——实测纯噪声图的"峰值/MAD"稳定在 9 附近(最大 10.9, 几乎与噪声大小无关),有 0.25 对比度黑点时是 25~330;若只按峰值归一化,正常图的噪声 会被拉到 1,满图假框。阈值 15 取在两者之间。 顺带把积分图方框均值从 shap_explain.smooth 提到 engine/classical.box_mean 共用, 避免两处实现各自漂移。 实测:单张 0.011 秒(全量 11k 约 2 分钟);真实薄膜图上框能套住黑点、虫状物与划痕。 已知局限:背景估计在图像边界处对明暗阶跃有响应,贴边的交界可能出假框。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
32e56cb230
commit
65bc6b2045
@@ -531,7 +531,7 @@ model:
|
||||
## 从分类到定位:生成框标签
|
||||
|
||||
分类模型只学过"这是什么缺陷",没学过"缺陷在哪"。归因方法能把分类分数反推回空间位置,
|
||||
阈值化 + 取连通域外接矩形,就得到弱监督的框标签:
|
||||
阈值化 + 取连通域外接矩形,就得到弱监督的框标签。另外还有一条不依赖模型的传统 CV 路线:
|
||||
|
||||
```bash
|
||||
# Grad-CAM(默认,全量 11k 张约 2 分钟)
|
||||
@@ -539,6 +539,9 @@ uv run python scripts/generate_boxes.py --out runs/boxes_gradcam --method gradca
|
||||
|
||||
# SHAP Partition(更精细,全量约 1.7 小时)
|
||||
uv run python scripts/generate_boxes.py --out runs/boxes_shap --method shap
|
||||
|
||||
# 传统 CV 背景差分(最快,且不需要分类模型)
|
||||
uv run python scripts/generate_boxes.py --out runs/boxes_cv --method cv
|
||||
```
|
||||
|
||||
**默认对全量数据生成**;`--limit` / `--per-class` 只用于快速试参数。
|
||||
@@ -562,18 +565,50 @@ runs/<name>/
|
||||
两者只在预测与目录不一致的图上有区别——`boxes_shap` 那次是 0.85%,约 95 张。
|
||||
`summary.json` 里的 `prediction_matches_folder_label` 记录两者的一致率(实测 0.987)。
|
||||
|
||||
### 两种方法的取舍
|
||||
### 三种方法的取舍
|
||||
|
||||
| | `gradcam` | `shap` |
|
||||
| --- | --- | --- |
|
||||
| 分辨率 | 受限于特征图(layer3 @128² 只有 8×8) | 与输入同分辨率(128×128) |
|
||||
| 单张耗时 | ~0.01 秒 | ~0.55 秒 |
|
||||
| 小目标 | 几像素的黑点在 8×8 上不可分辨 | 能贴合到几十像素的目标 |
|
||||
| | `gradcam` | `shap` | `cv` |
|
||||
| --- | --- | --- | --- |
|
||||
| 依赖分类模型 | 是 | 是 | **否** |
|
||||
| 分辨率 | 受限于特征图(layer3 @128² 只有 8×8) | 与输入同分辨率(128×128) | 原图分辨率 |
|
||||
| 单张耗时 | ~0.01 秒 | ~0.55 秒 | ~0.011 秒 |
|
||||
| 小目标 | 几像素的黑点在 8×8 上不可分辨 | 能贴合到几十像素的目标 | 贴合好 |
|
||||
| 找的是什么 | 模型据以判断类别的区域 | 同左,更精细 | 偏离局部背景的区域 |
|
||||
|
||||
实测框面积占比(越小越贴合缺陷本体):`blackdot` 0.019(shap) vs 0.269(gradcam)、
|
||||
`mosquito` 0.030 vs 0.173、`whitedot` 0.076 vs 0.253。小目标类别上 SHAP 明显更准,
|
||||
代价是慢 50 倍。SHAP 需要 `uv sync --group explain`。
|
||||
|
||||
### 传统 CV:背景差分(`--method cv`)
|
||||
|
||||
实现在 [engine/classical.py](engine/classical.py):大半径方框均值估背景 → 残差(可选只看
|
||||
比背景暗/亮的一侧)→ 小半径平滑 → 判定有无缺陷 → 按峰值归一化,最后交给和另外两种方法
|
||||
**同一套** `cam_to_boxes` 出框。只依赖 numpy。
|
||||
|
||||
它与归因方法有三点本质差别:
|
||||
|
||||
- **不需要分类模型**,`--checkpoint` 都不用给,分类器还没训练出来就能先跑一版框;
|
||||
也因此不受分类精度影响。
|
||||
- **类别只能来自目录**(`--target label`),它自己不判类。
|
||||
- **找的是"看起来和周围不一样"的区域**,不是"模型据以判断类别的区域"。对
|
||||
`blackdot` / `whitedot` / `mosquito` 这类局部目标效果好;`ripple` / `wrinkle` 这种铺满整图的
|
||||
纹理类本来就没有局部目标,它给不出有意义的框。
|
||||
|
||||
**有无缺陷是显式判定的**,这点和归因方法不同。残差峰值要同时高于绝对对比度下限
|
||||
(`--cv-min-contrast`,默认 0.05≈13 个灰阶)和噪声水平的 15 倍,否则直接返回全零图、出 0 个框。
|
||||
噪声水平用残差的 MAD 估计。这条判定是必需的:实测纯噪声图的"残差峰值 / MAD"稳定在 9 附近
|
||||
(最大 10.9,几乎与噪声大小无关),而有一个对比度 0.25 的黑点时是 25~330,两者分得很开;
|
||||
若只按峰值归一化,一张正常图的噪声会被拉到 1,满图假框。所以正常图天然得到空标签。
|
||||
|
||||
调参:`--cv-radius`(背景半径,0=按图取 `min(高,宽)//8`;太小会把缺陷自身算进背景)、
|
||||
`--cv-polarity`(`dark` 黑点蚊虫 / `bright` 白点反光 / `both`)、`--cv-min-contrast`、
|
||||
`--smooth-radius`(与 shap 共用)、`--cv-max-side`(分析前缩边长,默认 256——框是归一化的,
|
||||
缩放不影响标签,但能挡住纯 Python 的连通域标记在大图上变慢)。
|
||||
|
||||
> **已知局限。** 背景估计在图像边界处会对强烈的明暗阶跃产生响应,所以贴着边框的
|
||||
> 明暗交界可能被框出来。对 `edge_clean` / `edge_dirty` 这种本来就在拍边缘的类别,这是真实信号;
|
||||
> 对其他类别则是假框,复核时留意。
|
||||
|
||||
### 调参
|
||||
|
||||
- `--max-boxes`(默认 1):一张裁片通常只对应一处缺陷,多出来的框多半是噪声
|
||||
|
||||
Reference in New Issue
Block a user