在做图像语言分隔的时候,我们往往需要计算出像素精确度,交并比等值来评估我们的算法或者神经网络的表现。本篇文章着重介绍如何用numpy实现像素精确度、平均像素精确度、平均交并比和加权的交并比的计算。   在正式开始之前需要先掌握几个numpy的函数和编程技巧。  
bincount函数
功能:实现个数的统计。 示例:假设有numpy数组:  
x = np.array([0, 1, 1, 3, 2, 1, 7])
  我们想要知道里面0~7这8个数出现的次数是多少,就可以用bincount函数。  
y=np.bincount(x)
  打印出y得到[1 3 1 1 0 0 0 1],意思是我们有0~7这8个数,按照数组下标排列起来,然后对应的数值则为0~7出现的次数。 需要注意的是这里的数值长度是函数自己给的,也就是说它只会统计到我们放进函数的这个数组中最大的那个值。如果我们想要统计0~9这10个数出现的次数,我们可以通过指定minlength参数实现:  
y = np.bincount(x,minlength=10)
  输出则为:[1 3 1 1 0 0 0 1 0 0]
diag函数
功能:取出对角线上的值。 示例:假设有numpy数组:  
x=np.array([(1,0,0,0,0,4),(0,2,0,2,0,0),(0,0,3,0,0,4),(0,0,4,4,0,0),(0,0,5,0,5,4),(0,0,0,2,0,6)])
  [[1 0 0 0 0 4] [0 2 0 2 0 0] [0 0 3 0 0 4] [0 0 4 4 0 0] [0 0 5 0 5 4] [0 0 0 2 0 6]] 我们想要取出对角线上的数,则可以用diag函数。  
y=np.diag(x)
  输出为:[1 2 3 4 5 6] 如果我们想要把对角线上的数加起来,则写成:  
y=np.diag(x).sum()
    输出为:21
zip函数
功能:依据多维数组的第一维进行遍历。 示例:假设有numpy数组:  
x=np.array([[(0,0,0,0,0,4), (0,0,0,2,0,0)],[(0,0,0,0,0,4), (0,0,0,2,0,0)]])
y=np.array([[(0,0,0,0,0,4), (0,0,0,2,0,0)],[(0,0,0,0,0,1), (0,0,0,2,0,0)]])
  它们的形状是:(2, 2, 6) 假设前面的2指代的是不同的图片,而后面的2和6指代的是图片的宽和高 ,而我们希望将图片一张张取出来进行计算,则可以用zip函数来做。  
for onex, oney in zip(x, y):
    print(onex.shape)
    print(oney.shape)
 
flatten函数
  功能:将多维数组直接转化成1维 示例:假设有numpy数组:  
x=np.array([[(0,0,0,0,0,4), (0,0,0,2,0,0)],[(0,0,0,0,0,4), (0,0,0,2,0,0)]])
  我们希望把数组转为1维的,则可以用flatten函数。  
y=x.flatten()
  输出为:[0 0 0 0 0 4 0 0 0 2 0 0 0 0 0 0 0 4 0 0 0 2 0 0]
数组感兴趣区域选取
功能:选取感兴趣值进行计算,其他非感兴趣区域置为0 示例:假设有numpy数组:  
x = np.array([0, 1, 1, 3, 2, 1, 7])
  我们只想计算0~2这个3个数,而无视其他的数,则可以用如下代码实现:  
mask = (x >= 0) & (x <= 2)
x=x[mask]
  输出为:[0 1 1 2 1] 可以看到,非感兴趣区域被删除掉了。 上面就是所有需要先掌握的点,下面直接放出图像语义分隔评估的实现代码  
import numpy as np

# 精确度计算
def _fast_hist(label_true, label_pred, n_class):
    #标注计算类别范围以内的数,不在范围内的不考虑
    mask = (label_true >= 0) & (label_true < n_class)
    # 经过这句话的处理,行数将代表标签的值,而列数将代表预测的值,而具体的值代表个数。
    # 也就是说,对角线上的数分别指代第n个类别中,预测值和标签值相同的个数
    hist = np.bincount(n_class * label_true[mask].astype(int) +label_pred[mask], 
            minlength=n_class ** 2).reshape(n_class, n_class)
    return hist

def label_accuracy_score(label_trues, label_preds, n_class):
    """Returns accuracy score evaluation result.
      - overall accuracy
      - mean accuracy
      - mean IU
      - fwavacc
    """
    hist = np.zeros((n_class, n_class))
    #将标签和预测图像按行放入函数中
    for lt, lp in zip(label_trues, label_preds):
        hist += _fast_hist(lt.flatten(), lp.flatten(), n_class)
    acc = np.diag(hist).sum() / hist.sum()#总体精确度计算
    acc_cls = np.diag(hist) / hist.sum(axis=1)#求得每一类像素精确度的平均值
    acc_cls = np.nanmean(acc_cls)#求得总体平均精确度
    iu = np.diag(hist) / (hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist))#按类别求得交并比
    mean_iu = np.nanmean(iu)#求得总体平均交并比
    freq = hist.sum(axis=1) / hist.sum()#求得标签中各个类别所占的比例
    fwavacc = (freq[freq > 0] * iu[freq > 0]).sum()#按权值计算交并比
    return acc, acc_cls, mean_iu, fwavacc

label_true=np.array([[(0,0,0,0,0,4), (0,0,0,2,0,0)],[(0,0,0,0,0,4), (0,0,0,2,0,0)]])
label_pred=np.array([[(0,0,0,0,0,4), (0,0,0,2,0,0)],[(0,0,0,0,0,1), (0,0,0,2,0,0)]])

train_acc = 0
train_acc_cls = 0
train_mean_iu = 0 
train_fwavacc = 0

#用zip可以遍历多维数组的第一个维度,在本应用中每次返回一张标签和一张预测图像
for lbt, lbp in zip(label_true, label_pred):
    acc, acc_cls, mean_iu, fwavacc = label_accuracy_score(lbt, lbp, 5)#5为要计算的类别总数,也就是0~4
    train_acc += acc
    train_acc_cls += acc_cls
    train_mean_iu += mean_iu
    train_fwavacc += fwavacc