快速检测-FPC异常检测

FPC检测是导师交给的第一个横向落地项目 是实际为公司提供缺陷检测方案 作为第一次实战 希望能把这一路上的挫折和困难记录下来 后面避免重复犯错

项目的总流程概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56


┌──────────────────┐
│ 测试图片 (NG/OK) │
└────────┬─────────┘


┌──────────────────────────────────────┐
│ Stage 1: CPR检索 (基于Bank) │
│ DenseNet + K-means + 直方图匹配 │
└──────────────────────────────────────┘

┌────────────────┴────────────────┐
│ 在ROI的Bank中找Top-5相似OK图片 │
└────────────────┬────────────────┘

┌────────────────────┴────────────────────┐
│ │
▼ ▼
┌──────────────────────┐ ┌─────────────────────────┐
│ 测试图片特征提取 │ │ Bank图片特征提取 │
│ (ResNet50 layer2+3) │ │ (ResNet50 layer2+3) │
└──────────────────────┘ └─────────────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌─────────────────────────┐
│ PatchCore多层特征融合 │ │ PatchCore多层特征融合 │
│ train_patchcore_ │ │ train_patchcore_ │
│ residual.py │ │ residual.py │
└──────────────────────┘ └─────────────────────────
│ │
│ (B, 1024, 32, 32) │ (B, 1024, 32, 32)
│ │
└──────────────────┬────────────────────────┘


┌──────────────────────────────────────┐
│ Stage 2: 残差计算 │
│ residual = test_feat - bank_feat │
└──────────────────────────────────────┘

┌────────────────┴────────────────┐
│ 输出: 残差特征 (1, 1024, 32, 32) │
└────────────────┬────────────────┘


┌──────────────────────────────────────┐
│ Stage 3: Swin Transformer分类 │
│ train_patchcore_residual.py │
│ 8层Transformer Encoder │
└──────────────────────────────────────┘

┌────────────────┴────────────────┐
│ 输出: OK (0) / NG (1) │
│ │
└─────────────────────────────────┘

1.数据集的预处理

这部分大致可以分成以下几步:

  • 利用sift重定位算法截取目标ROI
  • 利用随机柏林噪音制造缺陷
  • padding填灰

最后的数据集结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
data/
├── 18062-图像2-ROI0/
│ ├── bank/
│ │ └── good/
│ │ ├── img/ # 正常样本图片 (.jpg)
│ │ └── label/ # 标注文件 (.png)
│ ├── test/
│ │ ├── good/
│ │ │ ├── img/
│ │ │ ├── label/
│ │ │ └── residual/ # 残差图
│ │ └── ng/ # 异常样本
│ │ ├── img/
│ │ ├── label/
│ │ └── residual/
│ └── train/
│ ├── good/
│ │ ├── img/
│ │ ├── label/
│ │ └── residual/
│ └── ng/
│ ├── img/
│ ├── label/
│ └── residual/

①sift重定位截取ROI


首先搞清楚我们的任务目标,原始数据集中是对整个FPC电子元件进行电子拍照,如果把整张图片都放进模型去识别,则会浪费很多资源。因为有很多无关的信息,所以我们应该单独把容易出现缺陷的部位抽离出来。 但是数据集中有这么多的图片,总不可能人工一个一个的去标注,因此就引入了sift重定位技术,仅仅需要几张拥有ROI标注的图片,我们就可以对整个数据集进行ROI截取

1.阶段一 关键点检测和描述符计算

1
2
3
# 3. 检测关键点并计算描述符
kp1, des1 = sift.detectAndCompute(gray_template, None)
kp2, des2 = sift.detectAndCompute(gray_target, None)

kp 表示检测到的关键点位置 是一个 对象列表

des表示每个关键点的特征描述符

关键点是什么?

sift算法会找到图像中稳定,显著的点(比如角点和边缘点)

KeyPoint 对象包含什么

kp[0].pt # (x, y) 像素坐标
kp[0].size # 关键点邻域直径
kp[0].angle # 方向(0-360°)
kp[0].response # 响应强度(越强越好)
kp[0].octave # 金字塔层级

描述符是什么?

描述符是对关键点周围区域的数学指纹:

关键点周围 16×16 区域:
┌─────────────────┐
│ · · · · · · │ ← 计算梯度方向
│ \ | / · · │
│ · · · · · · │ ↓
│ / | \ · · │ 统计 8 方向直方图
│ · · · · · · │ ↓
└─────────────────┘ 生成 128 维向量

作用是用来匹配不同图像中的同一个点

同一个物理点,他们的描述符相似,则匹配成功

2.特征匹配和筛选

其实说白了就是根据不同点的描述符来找最相似的坐标 然后这个匹配的过程可以用最暴力的方法 针对每一个点都去遍历des2的每一个描述符。实际代码中我们使用FLANN匹配器,可以加快这个匹配过程,我不是很想去细说这个算法,就把它当作一个加速的黑盒吧

1
2
3
4
5
6
# 4. 特征匹配 (使用 FLANN 匹配器,速度较快)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)

然后是筛选出优秀的匹配点 针对每一个des1点,我们都要去寻找TOP-2的相似点,分别是最近邻和次近邻 然后我们还要确保最近邻比次近邻匹配点要好30% 我们才认定这是一个好的匹配点

1
2
3
4
5
# 5. Lowe's Ratio Test 筛选优秀匹配点
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)

3.利用匹配点计算单应性矩阵H

其实这里单独理解起来很简单 就是计算出模板图片和目标图片的变换矩阵后 针对模板的ROI坐标进行一个变换 既可以到达目标图片的ROI位置 最后再是一下透视变换校正的优化

2.CPR检索寻找最近邻图片

3.Patchcore多层特征融合和残差计算

4. 残差特征经过swin transformer分类头