红茶馆

红茶半两酒个人博客

0%

MIOU计算

MIOU计算(同时去除不想要的类别)

其实主要就是把Cityscapes相关的MIOU代码拿过来改了点东西,可以用在其他数据集或者自己的数据集上。

深度学习计算机视觉图像分割领域指标mIoU(平均交并比)计算代码与逐行解析 - 代码先锋网

这篇文章对源代码讲解挺细致的。
首先来看看compute_mIoU函数,前半部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def compute_mIoU(gt_dir, pred_dir, devkit_dir=''):
# 分别输入标签路径,预测图像路径,图像txt文件名路径
num_classes = 6 # 类别数量
name_classes = np.array(['不透水面', '建筑物', '低矮植被', '树木', '汽车', '背景'], dtype=np.str_) # 每个类别名称

hist = np.zeros((num_classes, num_classes)) # 混淆矩阵

image_path_list = join(devkit_dir, 'image_test.txt') # 图像(预测)列表
label_path_list = join(devkit_dir, 'label_test.txt') # 标签(真实)列表

gt_imgs = open(label_path_list, 'r').read().splitlines()
gt_dir = gt_dir+'/labels'
gt_imgs = [join(gt_dir, x) for x in gt_imgs] # 这三行代码补充了完整的标签真实路径

pred_imgs = open(image_path_list, 'r').read().splitlines()
pred_imgs = [join(pred_dir, x.split('/')[-1]) for x in pred_imgs] # 这两行代码补充完完整的预测标签路径
mapping = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]

相对于源代码,修改了如下num_classes、name_classes、mapping。主要是这三个需要从info.json这个数据集获取,我们自己的数据集或者下载的其他数据集可能没有(当然,自己制作下应该也可以,不过我感觉放到程序里可能看起来直观点)num_classes是数据类别,这个类别最好从0开始标注。比如标签图是0,1,2三个类,不要设置成1,2,3。这样后面混淆矩阵计算时候可能有麻烦。当然,如果你的标签图就是1,2,3这样的,可以修改mapping为[[1,0],[2,1],[3,2]]。name_classes是类别名称,请注意从小到大对应起来,比如第一个不透水面,对应就是0,建筑物的话对应就是1。mapping是标签映射的,比如向上面提到的,我们的标签是1,3,4,5,7这种杂乱的时候,最好自己映射下,把他们映射成0,1,2,3,4这样的。另外如果我们有不想要的类别,比如1,3,4,5,7中,4和7类别我不想用来计算,那我们的映射可以这样:[[1,0],[3,1],[4,255],[5,2],[7,255]],把他们映射成255,具体为啥,后面会讲。后续其他的似乎不需要改动啥,直接抄过来就行,注意下路径就好。然后我们来看看compute_mIoU后半部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for ind in range(len(gt_imgs)):
pred = np.array(Image.open(pred_imgs[ind]))
label = np.array(Image.open(gt_imgs[ind]))
label = label_mapping(label, mapping)
if len(label.flatten()) != len(pred.flatten()):
print('Skipping: len(gt) = {:d}, len(pred) = {:d}, {:s}, {:s}'.format(len(label.flatten()),
len(pred.flatten()), gt_imgs[ind],
pred_imgs[ind]))
continue
hist += fast_hist(label.flatten(), pred.flatten(), num_classes)
if ind > 0 and ind % 10 == 0:
print('{:d} / {:d}: {:0.2f}'.format(ind, len(gt_imgs), 100 * np.mean(per_class_iu(hist))))
mIoUs = per_class_iu(hist)
if not silence:
for ind_class in range(num_classes):
print('===>' + name_classes[ind_class] + ':\t' + str(round(mIoUs[ind_class] * 100, 2)))
print('===> mIoU: ' + str(round(np.nanmean(mIoUs) * 100, 2)))
return mIoUs

这里面涉及三个重要的函数,label_mapping,fast_hist,per_class_iu。
下面分别介绍。

1
2
3
4
5
def label_mapping(input, mapping):
output = np.copy(input)
for ind in range(len(mapping)):
output[input == mapping[ind][0]] = mapping[ind][1]
return np.array(output, dtype=np.int64)

标签映射,很简单,把mapping中标签映射下,方便我们后续计算。

1
2
3
def fast_hist(a, b, n):
k = (a >= 0) & (a < n)
return np.bincount(n * a[k].astype(int) + b[k], minlength=n ** 2).reshape(n, n)

也很简单,具体计算细节可以自己推导下(有空的话我后续会补充)。其中a<n就限制了255这一类不想要的标签。

1
2
def per_class_iu(hist):
return np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist))

这就是MIOU计算公式(为啥这样算,具体小例子后续会补充):