读Shape-Guided代码②推理

main

Namespace(CUDA=0, ckpt_path='checkpoint/best_ckpt/ckpt_000601.pth', datasets_path='../mvtec3d', grid_path='data/', group_mul=10, image_size=224, output_dir='output/', point_num=500, sampled_size=20, viz=False)
                            BS: 1
                            LR: 0.0001
                     POINT_NUM: 500
                     ckpt_path: checkpoint/best_ckpt/ckpt_000601.pth
                       classes: ['cable_gland']正常的话这里是所有类别都有,我是暂时debug只选了一种
                 datasets_path: ../mvtec3d
              dict_n_component: 3
                         epoch: 1000
                     grid_path: data/
                     group_mul: 10
                    image_size: 224
                      k_number: 10
                   method_name: ['RGB', 'SDF', 'RGB_SDF']
                    output_dir: output/2024-03-05-10-14-54-result
                    rgb_method: Dict
                  sampled_size: 20
                           viz: False

也就是说不仅要输入包成npz的patches,还要输入前面的tiff、png

patchcore.fit()

这一步相当于是建立memory bank

image_rocaucs_df
Out[1]: 
    Method
0      RGB
1      SDF
2  RGB_SDF

pixel_rocaucs_df
Out[2]: 
    Method
0      RGB
1      SDF
2  RGB_SDF

au_pros_df
Out[3]: 
          Method
0        RGB_0.3
1        RGB_0.2
2        RGB_0.1
3       RGB_0.07
4       RGB_0.05
5       RGB_0.03
6       RGB_0.01
7        SDF_0.3
8        SDF_0.2
9        SDF_0.1
10      SDF_0.07
11      SDF_0.05
12      SDF_0.03
13      SDF_0.01
14   RGB_SDF_0.3
15   RGB_SDF_0.2
16   RGB_SDF_0.1
17  RGB_SDF_0.07
18  RGB_SDF_0.05
19  RGB_SDF_0.03
20  RGB_SDF_0.01

不过作者这里默认处理的是train也就是只有正常样本的
在dataloader中读取图片PIL是400,400,然后Transform有缩放、双线性三次插值bicubic,标准化,最终结果是3,224,224的tensor
npz读取出164,500,3的points_gt_set和164,500的points_idx_set对应之前的grouped_xyz和org_idx_all

normal_points

对于500,3的ps_gt归一化
首先计算xyz三个维度上的极差的最大值tt
将其进行一定缩放后乘给ps_gt
然后平移,类似于x-μ(均值t,三个维度各自的),把点云的几何中心移到原点
返回处理后的ps_gt,(t,tt)

输入一个sample,包含三部分,
其一是img,也就是1,3,224,224
其二是points_gt,也就是164个1,500,3
其三是points_idx,也就是164个1,500
另一个输入是train_data_id,就是当前batch的编号
RGBSDFFeatures(
  (rgb_feature_extractor): RGB_Model(先输入img处理
    (backbone): FeatureListNet(看默认设置,这应该是个wide resnet50
      (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
      -》 1, 64, 112, 112
      (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (act1): ReLU(inplace=True)
      (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
      -》 1, 64, 56, 56
      (layer1): Sequential(
        (0): Bottleneck(
          (conv1): Conv2d(64, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
          (downsample): Sequential(
            (0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
            (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          )
        )
        (1): Bottleneck(
          (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
        )
        (2): Bottleneck(
          (conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
        )
      )
      (layer2): Sequential(
        (0): Bottleneck(
          (conv1): Conv2d(256, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
          (downsample): Sequential(
            (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
            (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          )
        )
        (1): Bottleneck(
          (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
        )
        (2): Bottleneck(
          (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
        )
        (3): Bottleneck(
          (conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act1): ReLU(inplace=True)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
          (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (drop_block): Identity()
          (act2): ReLU(inplace=True)
          (aa): Identity()
          (conv3): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
          (bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          (act3): ReLU(inplace=True)
        )
      )
    )
  )
  返回的rgb_feature_maps是1,256,56,56和1,512,28,28
  (average): AvgPool2d(kernel_size=3, stride=1, padding=0)
  (resize): AdaptiveAvgPool2d(output_size=(28, 28))
  1,256,56,56和1,512,28,28分别接连经过average和resize得到1,256,28,28和1,512,28,28,再拼接成1,768,28,28-》784,768,叫做rgb_patch_size28
  
  输入points_gt-》points_all,和points_idx给SDFFeature.get_feature
  	遍历points_all中的每一个points
  	并取出对应的points_idx叫做indices
  	把这个indices输入get_relative_rgb_f_indices
  		首先通过特征图28与原图224之间的比例尺换算行列索引
  		这里是根据点云的索引映射到rgb图像,再映射到特征图
  		点云和rgb固有的联系可能是从一开始就有的吧,毕竟都是800,800,3的大小,而且也没有其他的解释说明。。。	
  		此外通过偏移量B加入一些交叠周边信息
  		所以相当于就是把点云索引与特征图像素/特征对应上
  		得到rgb_f_indices
  	然后把points搞成1,3,500后给下面这个encoder
  	SDF_Model(
	  (encoder): encoder_BN(
	    (conv1): Conv1d(3, 64, kernel_size=(1,), stride=(1,))
	    (bn1): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
	    (relu): ReLU()
	    (conv2): Conv1d(64, 128, kernel_size=(1,), stride=(1,))
	    (bn2): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
	    (relu): ReLU()
	    (conv3): Conv1d(128, 1024, kernel_size=(1,), stride=(1,))
	    (bn3): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
	    (relu): ReLU()-》1,1024,500
	    max+view->1,1024
	    (fc1): Linear(in_features=1024, out_features=512, bias=True)
	    (bn4): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
	    (relu): ReLU()
	    (fc2): Linear(in_features=512, out_features=128, bias=True)
	    (bn5): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
	    (relu): ReLU()
	  )-》1,128
	  从而将这些得到的feature拼接为164,128 sdf_feature
	  还有rgb_feature_indices_patch是data_id * 784 + rgb_f_indices组成的一个列表,164长,里面每个长度不一
  从而填充列表,sdf_feature组成sdf_patch_lib,rgb_patch_size28组成rgb_patch_lib,rgb_feature_indices_patch则extend成为rgb_f_idx_patch_lib
  在把223(cable_gland)这223个batch全部处理完后进入foreground_subsampling
  	把前面的rgb和sdf列表拼接得到174832,768的rgb_patch_lib,34234,128的sdf_patch_lib
  	rgb_f_idx_patch_lib也拼接成了1639326
  	接下来应该是去除不重要的RGB特征
  	首先定义一个全-1的,长为174832的origin_f_map,用于跟踪哪些特征被使用
  	对拼接后的rgb_f_idx_patch_lib去重得到use_f_idices
  	从而利用这个索引去筛选rgb_patch_lib,留下54846,768
  	然后把0~54845这么54846个数写进origin_f_map[use_f_idices]
  	然后把这个origin_f_map转成long格式的tensor

)

patchcore.align()

同样是先搞成dataloader,但是这里每一batch是150而不是164,应该是这一部分做test,bs不一样(只取前24个batch)
从而对于每个batch有输入一个sample,包含三部分,
其一是img,也就是1,3,224,224
其二是points_gt,也就是150个1,500,3
其三是points_idx,也就是150个1,500
另一个输入是test_data_id,就是当前batch的编号
直接把points_gt,points_idx和test_data_id给get_feature
从而得到feature150,128,rgb_features_indices150组

Find_KNN_feature

首先计算feature每条向量和sdf_patch_lib每条向量的距离(乘法原理)

对于每个feature中的特征,使用torch.topk找到与之距离最近的k=10+1个特征的索引knn_idx。加1是因为在某些模式下,会去除最近的一个特征(可能是它自身)。

然后根据alignment模式的要求,只要knn_idx[:,1:],去除掉最近邻(自己)

对于feature中的每个特征,使用其K近邻特征(从sdf_patch_lib中选出的10,128 knn_features)作为字典进行稀疏编码。这里使用了正交匹配追踪算法(OMP)。相当于是把这k近邻作为极限量去表征feature[i]

得到一个1,10的code_matrix。这里的10个元素并不是直接表征了原向量,而是基向量的线性组合系数
然后再与基向量相乘恢复原本维度1,128 sparse_feature,我无法理解这一步的意义,但是向量内容确实变化了

尽管指定了n_nonzero_coefs=3,算法可能仍然返回了一个长度为10的稀疏表示向量,其中最多有3个非零系数。这样,code_matrix会是一个1x10的向量,其中大部分元素是0,只有至多3个元素是非零值。
重建的向量并不等同于原始的feature[patch],它是原始向量在字典向量线性空间中的一个最佳近似。这个近似通常包含原始向量最重要的信息,但它通过减少表示中的元素数量来去除冗余或噪声。(相当于一次特征提取过滤?)
总结来说,尽管重建的向量在尺寸上与原始向量相同,其内容反映了一种更稀疏、可能更具表达力的数据表示。这种表示可以更好地捕捉数据的关键特征,对于许多机器学习任务(如分类、聚类、异常检测)来说是非常有价值的。在您的上下文中,使用稀疏编码的目的可能是为了获得一个更好的特征表示,以提高后续任务的性能。

把这150patch的sparse_feature拼接成Dict_features 150,128
再根据knn_idx[:,0]取sdf_patch_lib,得到NN_feature 150,128
计算feature和Dict_features的逐行欧氏距离 min_val 150,
并取其最大值s
输出NN_feature、Dict_features、knn_idx->lib_idices、s-》sdf_s

get_score_map

输入Dict_features、points_all也就是points_gt、points_idx
遍历Dict_features每行
有point_feature(当前行的特征扩展得到的1,500,128),point_target(当前行对应points_all的1,500,3 points-》point_target)-》input_points
然后把这俩输入给下面的神经隐函数(Neural Implicit Function,NIF)(属于SDF_Model的一部分)

	  (NIF): local_NIF(
	    (fc1): Linear(in_features=128, out_features=512, bias=True)
	    relu
	    (fc2): Linear(in_features=3, out_features=512, bias=True)
	    relu
	    然后把这两层结果拼接得到1,500,1024
	    (fc3): Linear(in_features=1024, out_features=512, bias=True)
	    relu
	    (fc40): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc41): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc42): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc43): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc44): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc45): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc46): Linear(in_features=512, out_features=512, bias=True)
	    relu
	    (fc5): Linear(in_features=512, out_features=1, bias=True)
	  )
	)

输出一个sdf_c 1,500,1,reshape成500,并取绝对值
然后由此做出一个s_map 50176
遍历完所有的feature后再转成1,1,224,224(但是这个东西也不知道能表征什么特征图,可视化后基本是全黑的)
然后再把img过一遍RGB_Model得到rgb_feature_maps 也就是 1,256,56,56和1,512,28,28组的列表
然后再让他过一遍self.average和self.resize等一系列操作得到rgb_features_size28 784,768

Dict_compute_rgb_map

输入rgb_features_size28、rgb_features_indices、lib_idices(去重后150,10变成1270)

取出self中的rgb_patch_lib 54846,768

lib_idicesl记录的是指向内存中应该被用作形状指导的特定RGB特征
遍历lib_idices,对于其中每个patch_idx,函数通过查询self.rgb_f_idx_patch_lib来获取对应的特征索引_f_idx_28。self.rgb_f_idx_patch_lib是一个映射,将内存中的特征库索引映射到实际的RGB特征索引。然后,这个实际的索引被用于从self.origin_f_map中获取最终的特征索引。self.origin_f_map可能是一个映射,它记录了每个特征在rgb_lib中的位置。
所有通过上述步骤获取的特征索引被添加到rgb_indices_28列表中。这个列表最终包含了所有应该被用作形状指导的特征在rgb_lib中的索引。
去除重复的索引,并保留唯一的索引。这一步是必要的,因为可能有重复的索引指向同一个形状指导特征。然后,使用这些唯一的索引从rgb_lib中提取相应的形状指导RGB特征,形成shape_guide_rgb_features_28 8330,768。
从而实现了从整个RGB特征库中精确选取那些对当前测试数据具有指导意义的特征

再对rgb_features_indices去重得到前景rgb索引,从rgb_features_size28中取出258,768 rgb_patch_28
计算rgb_patch_28和shape_guide_rgb_features_28的距离并取top6 knn_val knn_idx,二者都是258,6
因为是对齐模式,所以去掉top1
随后同样遍历knn_idx,计算rgb_patch_28[patch]的稀疏编码并恢复得到sparse_features 1,768
收集并拼接得到258,768的Dict_features_size28
将其与rgb_patch_28逐行计算欧氏距离,根据前景rgb索引填入784,的s_map_size28,并计算最大值rgb_s
view成1,1,28,28并双线性插值得到1,1,224,224 rgb_map

blur

rgb_map, rgb_s通过blur(属于SDF_Model的一部分)

  (blur): KNNGaussianBlur()

初始化:

  • radius: 高斯模糊的半径,定义了模糊效果的强度。
  • unload: 将张量转换为PIL图像的转换。这是因为高斯模糊操作在PIL图像上执行。
  • load: 将PIL图像转换回张量的转换。
  • blur_kernel: 定义了一个高斯模糊滤波器,半径设置为4(虽然传入的是radius参数,但在这里直接使用了4作为值,这可能是个硬编码值或者错误)。

调用:
输入img是一个张量,假设是一个单通道图像(例如,一个特征图或分数图)。
map_max: 计算img的最大值,这个值用于后续的归一化步骤。
final_map: 执行模糊操作的主要步骤。首先,使用unload将输入图像转换为PIL图像,并将其归一化(除以map_max)。然后,应用高斯模糊滤波器blur_kernel。模糊后的图像再通过load转换回张量,并恢复原来的数值范围(乘以map_max)。

最后

# image_level
self.sdf_image_preds.append(sdf_s.numpy())
self.rgb_image_preds.append(rgb_s.numpy())
# pixel_level
self.rgb_pixel_preds.extend(rgb_map.flatten().numpy())
self.sdf_pixel_preds.extend(sdf_map.flatten().numpy())
cal_alignment

用于对齐两个分布(SDF和RGB)的权重和偏置
从前面的sdf_pixel_preds取出sdf_map,从rgb_pixel_preds取出rgb_map ,都是 1254400
类似于整理分布

self.weight = (sdf_upper - sdf_lower) / (rgb_upper - rgb_lower)
self.bias = sdf_lower - self.weight * rgb_lower

然后把每类的这个权重偏置记录进txt

patchcore.evaluate()

这次是用的test loader
每个batch有sample(1,3,224,224的tensor,148个1,500,3组成的list,148个1,500组成的list),mask 1,1,224,224,label 1,

首先同样是通过get_feature得到feature, rgb_features_indices

Find_KNN_feature

这次是testing模式
主要区别就是

        if mode == 'alignment':
            knn_idx = knn_idx[:,1:]
        elif mode == 'testing':
            knn_idx = knn_idx[:,:-1]

然后得到 sdf_map

Dict_compute_rgb_map

这次是testing模式
主要区别就是

        if mode == 'alignment':
            knn_idx = knn_idx[:, 1:]
            min_val_28 = knn_val[:, 1]
        elif mode == 'testing':
            knn_idx = knn_idx[:,:-1]
            min_val_28 = knn_val[:, 0]

然后得到 rgb_map, rgb_s
接下来

image_score = sdf_s * rgb_s
new_rgb_map = rgb_map * self.weight + self.bias
new_rgb_map = torch.clip(new_rgb_map, min=0, max=new_rgb_map.max())
pixel_map = torch.maximum(new_rgb_map, sdf_map)
blur

除了sdf_map和rgb_map,还有new_rgb_map和pixel_map

最后

self.image_list.append(sample[0].squeeze().numpy())
##### Record Image Level Score #####
self.image_labels.append(label.numpy())
self.sdf_image_preds.append(sdf_s.numpy())
self.rgb_image_preds.append(rgb_s.numpy())
self.image_preds.append((image_score).numpy())

##### Record Pixel Level Score #####
self.pixel_labels.extend(mask.flatten().numpy())
self.sdf_pixel_preds.extend(sdf_map.flatten().numpy())
self.rgb_pixel_preds.extend(rgb_map.flatten().numpy())
self.new_rgb_pixel_preds.extend(new_rgb_map.flatten().numpy())
self.pixel_preds.extend(pixel_map.flatten().numpy())
visualize_result

通过viz控制的可视化

score_map = np.asarray(self.pixel_preds).reshape(-1, self.image_size, self.image_size)
108,224,224
gt_mask = np.asarray(self.pixel_labels).reshape(-1, self.image_size, self.image_size)
108,224,224
gt_label_list = np.asarray(self.image_labels, dtype=np.bool)
108,1
det_threshold, seg_threshold = visualization(self.image_list, gt_label_list, self.image_preds, gt_mask, score_map, self.output_dir)
return  det_threshold, seg_threshold

其中visualization

计算检测阈值(det_threshold):
使用precision_recall_curve计算基于图像级别得分的精确度(precision)、召回率(recall)和阈值(thresholds)。
计算F1分数,它是精确度和召回率的调和平均值,用于评估模型在图像级别上的性能。
选择使F1分数最大化的阈值作为检测阈值(det_threshold)。
计算分割阈值(seg_threshold):
对掩码进行相同的处理,计算基于像素级别得分的精确度、召回率和阈值。
同样计算F1分数,并选择最大化F1分数的阈值作为分割阈值(seg_threshold)。

设定形态学处理的核(kernel),这里使用了半径为2的圆盘结构元素。
归一化分数scores,使其最大值为1,便于后续处理和显示。
遍历所有测试图像:对于每一张测试图像,进行以下处理:
图像处理:将图像从标准化状态恢复到原始状态。
真实掩码处理:将真实的掩码图像进行形态学开操作(去除小对象或平滑边界),然后将值缩放到[0, 255]范围内,用于可视化。
分数掩码处理:
创建一个与分数相同形状的空掩码。
根据阈值创建预测掩码,分数高于阈值的设置为1。
对预测掩码进行形态学开操作并缩放值至[0, 255]。
边界标记:使用mark_boundaries在原图上标记真实掩码和预测掩码的边界。
绘图:
创建三个子图:真实掩码标记的图像、原图叠加预测分数的热图、预测掩码标记的图像。
请添加图片描述

最后把"RGB", “SDF”, "RGB+SDF"三种方法的图像级和像素级的AUC以及PRO分数计算存储进txt

AUC就简单一些,直接调的sklearn的函数

collect_anomaly_scores函数:
这个函数遍历每个真实标签和对应的预测结果,使用形态学操作(scipy.ndimage.measurements.learn,它在这个上下文中用于识别和标记图像中的连通区域。在异常检测的场景下,label函数可以帮助区分独立的异常区域,对于每个独立区域,我们可以更精确地计算它们的异常分数。)计算真实的异常组件。
对于每个真实异常组件,收集该区域内的预测异常分数,并为正常像素(非异常区域的像素)收集预测分数。
最后,返回这些收集到的分数,用于compute_pro函数中阈值的选择和PRO值的计算。

  • 使用label函数:
    label函数对gt_map(真实标签图)应用,以识别和标记图中的连通组件。在异常检测的上下文中,这些连通组件代表独立的异常区域。
    labeled是一个与gt_map相同形状的数组,其中每个连通区域的像素被标记为一个独特的标签。
    n_components是图中识别的连通区域数量。
  • 处理每个连通区域:
    对于gt_map中的每个连通区域,函数提取该区域内的预测异常分数,并存储起来。这些分数稍后用于计算每个异常区域在不同阈值下被正确识别的程度。
    此外,函数还收集所有正常区域(标记为0的区域)的预测异常分数,这些分数用于计算假阳性率。
  • 排序:
    收集所有正常区域的预测分数后,函数对这些分数进行排序。排序后的分数用于确定不同阈值下的假阳性率,即在该阈值下,将多少正常像素错误地标记为异常。

通过这种方式,label函数在异常检测中发挥了关键作用,它使得能够对每个独立的异常区域进行更细致的分析。这对于理解模型的行为和改进模型的性能至关重要。

GroundTruthComponent类用于存储单个真实异常区域内的异常分数,并提供计算给定阈值下区域重叠度的方法。这里是对类及其方法的解释:
init 方法:

anomaly_scores 参数接收一个包含在真实异常区域内所有像素的异常分数的numpy数组。
self.anomaly_scores 存储了这些异常分数,并且对它们进行了排序。这个排序是为了高效计算不同阈值下的区域重叠度。
self.index 是一个指针,用于指示当前阈值将异常分数划分为正常(OK)和异常(NOK)像素的位置。
self.last_threshold 存储最后评估的阈值,确保计算时阈值是按递增顺序传入的。

compute_overlap 方法:

此方法计算在特定阈值下的区域重叠度。要求传入的阈值是递增的。
threshold 参数是要计算区域重叠度的阈值。
方法内部,self.index 会递增,直到它指向的异常分数刚好大于指定的阈值。
计算并返回指定阈值下正确标记为异常的像素占该真实异常区域的比例。这是通过1.0 - self.index / len(self.anomaly_scores)计算的,其中self.index是小于或等于阈值的分数数量,len(self.anomaly_scores)是区域内总的像素数量。
这种计算方式非常高效,因为通过维护一个排序的分数列表和一个指针,compute_overlap方法可以快速计算出在不同阈值下的区域重叠度,这对于评估异常检测模型的性能非常有用。

返回ground_truth_components 和 anomaly_scores_ok_pixel。ground_truth_components是一个GroundTruthComponent类的实例列表,每个实例代表一个独立的真实异常区域。anomaly_scores_ok_pixels是一个数组,包含了所有被认为是正常的像素的预测异常分数。

compute_pro函数:
首先,collect_anomaly_scores函数用于收集异常分数。这些分数分为两类:一类是真实异常区域内的分数,另一类是正常像素(潜在的假阳性)的分数。
根据设置的阈值数量(num_thresholds),在异常分数的排序列表中选择等距的阈值。
对于每个阈值,计算相应的假阳性率(FPR)和PRO值。

for pos in threshold_positions:
 	fpr = 1.0 - (pos + 1) / len(anomaly_scores_ok_pixels)

PRO值是通过遍历每个真实异常区域,计算其与预测异常区域重叠度的平均值来得到的。
其实就是说pro是预测异常和真实异常交集占真实异常的像素比例
fpr就是预测为异常,实际为正常的错检率

calculate_au_pro函数:
这个函数接收真实标签(gts),预测结果(predictions),积分限制(integration_limit),和阈值数量(num_thresholds)。
compute_pro函数被调用来计算PRO曲线。该曲线基于多个阈值下的假阳性率(FPR)和PRO值。
使用梯形法则(trapezoid函数)在给定的x_max(即integration_limit)范围内计算PRO曲线下的面积,然后该面积被归一化除以integration_limit。(ROCAUC是有sklearn直接提供计算面积的函数的)

我之前预处理没有完全,背景没有全都去掉所以下面这个结果应该有问题

python main.py --datasets_path ../mvtec3d --grid_path data/ --ckpt_path "checkpoint/best_ckpt/ckpt_000601.pth"

Running on class bagel

Extracting training-features for class bagel: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 244/244 [03:05<00:00,  1.32it/s]

Running ForeGround Subsampling on class bagel...
Computing weight and bias for alignment on class bagel

Extracting testing-features for class bagel: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 110/110 [05:23<00:00,  2.95s/it]

Finished running on class bagel
################################################################################


Running on class cable_gland

Extracting training-features for class cable_gland: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 223/223 [01:28<00:00,  2.53it/s]

Running ForeGround Subsampling on class cable_gland...
Computing weight and bias for alignment on class cable_gland

Extracting testing-features for class cable_gland: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 108/108 [02:27<00:00,  1.37s/it]

Finished running on class cable_gland
################################################################################


Running on class carrot

Extracting training-features for class carrot: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 286/286 [00:42<00:00,  6.72it/s]

Running ForeGround Subsampling on class carrot...
Computing weight and bias for alignment on class carrot

Extracting testing-features for class carrot: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 159/159 [01:38<00:00,  1.62it/s]

Finished running on class carrot
################################################################################


Running on class cookie

Extracting training-features for class cookie: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 210/210 [02:53<00:00,  1.21it/s]

Running ForeGround Subsampling on class cookie...
Computing weight and bias for alignment on class cookie

Extracting testing-features for class cookie: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 131/131 [06:23<00:00,  2.93s/it]

Finished running on class cookie
################################################################################


Running on class dowel

Extracting training-features for class dowel: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 288/288 [00:51<00:00,  5.55it/s]

Running ForeGround Subsampling on class dowel...
Computing weight and bias for alignment on class dowel

Extracting testing-features for class dowel: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 130/130 [01:26<00:00,  1.50it/s]

Finished running on class dowel
################################################################################


Running on class foam

Extracting training-features for class foam: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 236/236 [02:37<00:00,  1.50it/s]

Running ForeGround Subsampling on class foam...
Computing weight and bias for alignment on class foam

Extracting testing-features for class foam: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100/100 [04:19<00:00,  2.59s/it]

Finished running on class foam
################################################################################


Running on class peach

Extracting training-features for class peach: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 361/361 [02:38<00:00,  2.27it/s]

Running ForeGround Subsampling on class peach...
Computing weight and bias for alignment on class peach

Extracting testing-features for class peach: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 132/132 [03:47<00:00,  1.73s/it]

Finished running on class peach
################################################################################


Running on class potato

Extracting training-features for class potato: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 300/300 [01:09<00:00,  4.29it/s]

Running ForeGround Subsampling on class potato...
Computing weight and bias for alignment on class potato

Extracting testing-features for class potato: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 114/114 [01:38<00:00,  1.16it/s]

Finished running on class potato
################################################################################


Running on class rope

Extracting training-features for class rope: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 298/298 [00:42<00:00,  7.07it/s]

Running ForeGround Subsampling on class rope...
Computing weight and bias for alignment on class rope

Extracting testing-features for class rope: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 101/101 [01:00<00:00,  1.68it/s]

Finished running on class rope
################################################################################


Running on class tire

Extracting training-features for class tire: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 210/210 [01:13<00:00,  2.84it/s]

Running ForeGround Subsampling on class tire...
Computing weight and bias for alignment on class tire

Extracting testing-features for class tire: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 112/112 [02:23<00:00,  1.28s/it]

Finished running on class tire
################################################################################



################################################################################
############################# Image ROCAUC Results #############################
################################################################################

| Method   |   Bagel |   Cable_Gland |   Carrot |   Cookie |   Dowel |   Foam |   Peach |   Potato |   Rope |   Tire |   Mean |
|:---------|--------:|--------------:|---------:|---------:|--------:|-------:|--------:|---------:|-------:|-------:|-------:|
| RGB      |   0.908 |         0.936 |    0.856 |    0.663 |   0.971 |  0.766 |   0.766 |    0.644 |  0.869 |  0.713 |  0.809 |
| SDF      |   0.982 |         0.7   |    0.975 |    0.998 |   0.958 |  0.743 |   0.992 |    0.976 |  0.975 |  0.9   |  0.92  |
| RGB_SDF  |   0.982 |         0.904 |    0.983 |    0.991 |   0.98  |  0.869 |   0.987 |    0.972 |  0.966 |  0.883 |  0.952 |


################################################################################
############################# Pixel ROCAUC Results #############################
################################################################################

| Method   |   Bagel |   Cable_Gland |   Carrot |   Cookie |   Dowel |   Foam |   Peach |   Potato |   Rope |   Tire |   Mean |
|:---------|--------:|--------------:|---------:|---------:|--------:|-------:|--------:|---------:|-------:|-------:|-------:|
| RGB      |   0.99  |         0.993 |    0.99  |    0.981 |   0.992 |  0.927 |   0.984 |    0.987 |  0.995 |  0.991 |  0.983 |
| SDF      |   0.992 |         0.961 |    0.998 |    0.948 |   0.959 |  0.929 |   0.996 |    0.999 |  0.995 |  0.996 |  0.977 |
| RGB_SDF  |   0.996 |         0.993 |    0.998 |    0.992 |   0.991 |  0.997 |   0.997 |    0.999 |  0.998 |  0.997 |  0.996 |


##########################################################################
############################# AU PRO Results #############################
##########################################################################

| Method       |   Bagel |   Cable_Gland |   Carrot |   Cookie |   Dowel |   Foam |   Peach |   Potato |   Rope |   Tire |   Mean |
|:-------------|--------:|--------------:|---------:|---------:|--------:|-------:|--------:|---------:|-------:|-------:|-------:|
| RGB_0.3      |   0.944 |         0.972 |    0.959 |    0.912 |   0.961 |  0.776 |   0.936 |    0.95  |  0.955 |  0.957 |  0.932 |
| RGB_0.2      |   0.917 |         0.957 |    0.939 |    0.87  |   0.942 |  0.714 |   0.904 |    0.925 |  0.932 |  0.935 |  0.904 |
| RGB_0.1      |   0.836 |         0.915 |    0.878 |    0.764 |   0.885 |  0.604 |   0.81  |    0.849 |  0.865 |  0.872 |  0.828 |
| RGB_0.07     |   0.774 |         0.879 |    0.827 |    0.694 |   0.839 |  0.549 |   0.74  |    0.784 |  0.819 |  0.822 |  0.773 |
| RGB_0.05     |   0.701 |         0.832 |    0.764 |    0.62  |   0.784 |  0.497 |   0.665 |    0.703 |  0.766 |  0.763 |  0.71  |
| RGB_0.03     |   0.573 |         0.73  |    0.644 |    0.501 |   0.674 |  0.426 |   0.547 |    0.556 |  0.668 |  0.648 |  0.597 |
| RGB_0.01     |   0.276 |         0.397 |    0.327 |    0.25  |   0.346 |  0.233 |   0.276 |    0.252 |  0.357 |  0.33  |  0.304 |
| SDF_0.3      |   0.974 |         0.869 |    0.981 |    0.924 |   0.898 |  0.762 |   0.979 |    0.983 |  0.953 |  0.97  |  0.929 |
| SDF_0.2      |   0.961 |         0.803 |    0.972 |    0.904 |   0.858 |  0.699 |   0.968 |    0.974 |  0.935 |  0.956 |  0.903 |
| SDF_0.1      |   0.924 |         0.654 |    0.944 |    0.855 |   0.739 |  0.62  |   0.938 |    0.949 |  0.883 |  0.913 |  0.842 |
| SDF_0.07     |   0.895 |         0.578 |    0.92  |    0.82  |   0.67  |  0.585 |   0.911 |    0.926 |  0.852 |  0.883 |  0.804 |
| SDF_0.05     |   0.858 |         0.512 |    0.889 |    0.779 |   0.614 |  0.551 |   0.877 |    0.897 |  0.819 |  0.847 |  0.764 |
| SDF_0.03     |   0.778 |         0.425 |    0.818 |    0.701 |   0.536 |  0.491 |   0.799 |    0.829 |  0.751 |  0.77  |  0.69  |
| SDF_0.01     |   0.453 |         0.229 |    0.486 |    0.416 |   0.3   |  0.284 |   0.458 |    0.493 |  0.445 |  0.452 |  0.402 |
| RGB_SDF_0.3  |   0.981 |         0.972 |    0.982 |    0.972 |   0.962 |  0.979 |   0.981 |    0.983 |  0.974 |  0.975 |  0.976 |
| RGB_SDF_0.2  |   0.971 |         0.958 |    0.973 |    0.959 |   0.942 |  0.968 |   0.972 |    0.975 |  0.96  |  0.963 |  0.964 |
| RGB_SDF_0.1  |   0.943 |         0.917 |    0.945 |    0.92  |   0.889 |  0.936 |   0.944 |    0.949 |  0.921 |  0.927 |  0.929 |
| RGB_SDF_0.07 |   0.919 |         0.882 |    0.922 |    0.889 |   0.849 |  0.909 |   0.92  |    0.928 |  0.89  |  0.899 |  0.901 |
| RGB_SDF_0.05 |   0.887 |         0.838 |    0.891 |    0.851 |   0.803 |  0.872 |   0.889 |    0.899 |  0.853 |  0.864 |  0.865 |
| RGB_SDF_0.03 |   0.815 |         0.742 |    0.821 |    0.769 |   0.712 |  0.79  |   0.818 |    0.831 |  0.776 |  0.791 |  0.786 |
| RGB_SDF_0.01 |   0.481 |         0.405 |    0.487 |    0.454 |   0.394 |  0.452 |   0.481 |    0.497 |  0.454 |  0.466 |  0.457 |
  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值