推荐B站up主“霹雳吧啦Wz” 的视频《语义分割前言》 https://www.bilibili.com/video/BV1ev411P7dR/
从视频第10分钟开始,有很形象很直观的演示。
这里我没有使用混淆矩阵(因为没有必要),而是只用了3个1维的向量组,分别是:
图片来源自上文提到的视频的截图
其实从上面这个图就能看出来:
label_count, pred_count,match_count 其实也就分别对应着:
用这三个一维的向量同样可以实现PA, mPA, IoU, mIoU的计算:
1.Numpy实现与使用测试代码
import time
import numpy as np
class ComputeScore:
def __init__(self, num_classes):
self.num_classes = num_classes
self.label_count = np.zeros((num_classes,), dtype='int32')
self.pred_count = np.zeros((num_classes,), dtype='int32')
self.match_count = np.zeros((num_classes,), dtype='int32')
self.PA = 0
self.PA_By_Classes = np.zeros((num_classes,), dtype='float32')
self.mPA = 0
self.IoU = np.zeros((num_classes,), dtype='float32')
self.mIoU = 0
# 防止除数为0 出现nan
self.eps = np.full((num_classes,), 1e-5, dtype='float32')
def addBatch(self, label, pred):
label = label.flatten()
pred = pred.flatten()
batch_label_count = np.bincount(label, minlength=self.num_classes)
batch_pred_count = np.bincount(pred, minlength=self.num_classes)
batch_match_count = np.bincount(label[label == pred], minlength=self.num_classes)
self.label_count = self.label_count + batch_label_count
self.pred_count = self.pred_count + batch_pred_count
self.match_count = self.match_count + batch_match_count
def __compute_PA(self):
self.PA = (np.sum(self.match_count)) / (np.sum(self.label_count + self.eps))
return self.PA
def __compote_PA_By_Classes(self):
self.PA_By_Classes = self.match_count / (self.pred_count + self.eps)
return self.PA_By_Classes
def __compute_mPA(self):
self.mPA = np.mean(self.PA_By_Classes)
return self.mPA
def __compute_IoU(self):
intersection = self.match_count
union = self.pred_count + self.label_count - self.match_count + self.eps
self.IoU = intersection / union
return self.IoU
def __compute_mIoU(self):
self.mIoU = np.mean(self.IoU)
return self.mIoU
def display(self):
self.__update_score()
print('self.label_count', self.label_count)
print('self.pred_count', self.pred_count)
print('self.match_count', self.match_count)
print('self.PA', self.PA)
print('self.PA_By_Classes', self.PA_By_Classes)
print('self.mPA', self.mPA)
print('self.IoU', self.IoU)
print('self.mIoU', self.mIoU)
print('点到为止')
def __update_score(self):
_ = self.__compute_PA()
_ = self.__compote_PA_By_Classes()
_ = self.__compute_mPA()
_ = self.__compute_IoU()
_ = self.__compute_mIoU()
def get_all_score(self):
self.__update_score()
return self.PA, self.mPA, self.IoU, self.mIoU
# 使用方法
if __name__ == '__main__':
# 创建一个计算指标的对象
compute_score = ComputeScore(23)
# 随机生成取值范围为[0,23)内整数,形状为(600,400)的真实标签和预测标签
label = np.random.randint(0, 23, size=(600, 400))
pred = np.random.randint(0, 23, size=(600, 400))
st = time.time()
for i in range(10000):
# 将Ground Truth和预测标签通过addBatch函数,更新计算指标的对象
compute_score.addBatch(label, pred)
et0 = time.time()
compute_score.display()
# 返回语义分割的指标
PA, mPA, IoU, mIoU = compute_score.get_all_score()
print( '-'*10, '\n')
print(PA)
print(mPA)
print(IoU)
print(mIoU)
et1 = time.time()
print( f'总用时{et1 - st}秒, 计算分数用时{et1 - et0}秒' )
2.基于Tensor的实现(多核运行,很快,基于torch)
import time
import numpy as np
import torch
class ComputeScoreTensor:
def __init__(self, num_classes):
self.num_classes = num_classes
self.label_count = torch.zeros((num_classes), dtype=torch.int32)
self.pred_count = torch.zeros((num_classes), dtype=torch.int32)
self.match_count = torch.zeros((num_classes), dtype=torch.int32)
self.PA = torch.zeros(1, dtype=torch.float32)
self.PA_By_Classes = torch.zeros((num_classes), dtype=torch.float32)
self.mPA = torch.zeros(1, dtype=torch.float32)
self.IoU = torch.zeros((num_classes), dtype=torch.float32)
self.mIoU = torch.zeros(1, dtype=torch.float32)
# 防止除数为0
self.eps = torch.zeros((num_classes), dtype=torch.float32)
# TypeError: new_full(): argument 'size' (position 1) must be tuple of SymInts, not int
# self.eps = self.eps.new_full((num_classes), 1e-5)
# (num_classes) 被认为成是一个int 而不是一个数字的元组 (num_classes,) 它就懂了
self.eps = self.eps.new_full((num_classes,), 1e-5)
def addBatch(self, label, pred):
label = label.flatten()
pred = pred.flatten()
batch_label_count = torch.bincount(label, minlength=self.num_classes)
batch_pred_count = torch.bincount(pred, minlength=self.num_classes)
batch_match_count = torch.bincount(label[label == pred], minlength=self.num_classes)
self.label_count = self.label_count + batch_label_count
self.pred_count = self.pred_count + batch_pred_count
self.match_count = self.match_count + batch_match_count
def __compute_PA(self):
self.PA = (torch.sum(self.match_count)) / (torch.sum(self.label_count + self.eps))
return self.PA
def __compote_PA_By_Classes(self):
self.PA_By_Classes = self.match_count / (self.pred_count + self.eps)
return self.PA_By_Classes
def __compute_mPA(self):
self.mPA = torch.mean(self.PA_By_Classes)
return self.mPA
def __compute_IoU(self):
intersection = self.match_count
union = self.pred_count + self.label_count - self.match_count + self.eps
self.IoU = intersection / union
return self.IoU
def __compute_mIoU(self):
self.mIoU = torch.mean(self.IoU)
return self.mIoU
def display(self):
self.__update_score()
print('self.label_count', self.label_count)
print('self.pred_count', self.pred_count)
print('self.match_count', self.match_count)
print('self.PA', self.PA)
print('self.PA_By_Classes', self.PA_By_Classes)
print('self.mPA', self.mPA)
print('self.IoU', self.IoU)
print('self.mIoU', self.mIoU)
print('点到为止')
def __update_score(self):
_ = self.__compute_PA()
_ = self.__compote_PA_By_Classes()
_ = self.__compute_mPA()
_ = self.__compute_IoU()
_ = self.__compute_mIoU()
def get_all_score(self):
self.__update_score()
# return self.PA, self.mPA, self.IoU, self.mIoU
# float()是把tensor的dtype进行转换 返回的仍然是tensor(0.0435)这样的
# tolist()确实把tensor转成了python中的list
# return self.PA.float(), self.mPA.float(), self.IoU.tolist(), self.mIoU.float()
# item()可以满足要求
# IoU是多个元素的tensor item()是会报错的
# ValueError: only one element tensors can be converted to Python scalars
return self.PA.item(), self.mPA.item(), self.IoU.tolist(), self.mIoU.item()
# 使用方法
if __name__ == '__main__':
# 创建一个计算指标的对象
compute_score = ComputeScoreTensor(23)
# 随机生成取值范围为[0,23)内整数,形状为(600,400)的真实标签和预测标签
label = np.random.randint(0, 23, size=(600, 400))
pred = np.random.randint(0, 23, size=(600, 400))
# print(label.dtype) # int32
label = torch.from_numpy(label)
pred = torch.from_numpy(pred)
# print(label.dtype) # torch.int32
st = time.time()
for i in range(10000):
# 将Ground Truth和预测标签通过addBatch函数,更新计算指标的对象
compute_score.addBatch(label, pred)
et0 = time.time()
compute_score.display()
# 返回语义分割的指标
PA, mPA, IoU, mIoU = compute_score.get_all_score()
print( '-'*10, '\n')
print(PA)
print(mPA)
print(IoU)
print(mIoU)
et1 = time.time()
print( f'总用时{et1 - st}秒, 计算分数用时{et1 - et0}秒' )