语义分割的评价指标——MIoU

语义分割的评价指标——MIoU

  • 前言
    • 代码

前言

MIoU(Mean Intersection over Union)是语义分割的一个评价指标,表示平均交并比,即数据集上每一个类别的IoU值的平均。
那么什么是IoU呢,以下面这张图直观地说一下:

图中,左边的圆表示某个类的真实标签,右边的圆表示其预测结果,

  • TP表示真实值为Positive,预测为Positive,称作是正确的Positive(True Positive=TP)
  • FN表示真实值为Positive,预测为Negative,称作是错误的Negative(False Negative=FN)
  • FP表示真实值为Negative,预测为Positive,称作是错误的Positive(False Positive=FP)
  • TN表示真实值为Negative,预测为Negative,称作是正确的Negative(True Negative=TN)

中间的TP部分即为真实值和预测值的交,FN+FP+TP部分即为真实值和预测值的并,IoU就是交与并的比值: I o U = T P F N + F P + T P IoU = \frac{TP}{FN+FP+TP} IoU=FN+FP+TPTP

MIoU的具体计算公式如下:
设i表示真实值,j表示预测值, p i j p_{i j} pij表示将i预测为j,
M I o U = 1 k + 1 ∑ i = 0 k p i i ∑ j = 0 k p i j + ∑ j = 0 k p j i − p i i M I o U=\frac{1}{k+1} \sum_{i=0}^{k} \frac{p_{i i}}{\sum_{j=0}^{k} p_{i j}+\sum_{j=0}^{k} p_{j i}-p_{i i}} MIoU=k+11i=0kj=0kpij+j=0kpjipiipii这个公式和
M I o U = 1 k + 1 ∑ i = 0 k T P F N + F P + T P M I o U=\frac{1}{k+1} \sum_{i=0}^{k} \frac{TP}{FN+FP+TP} MIoU=k+11i=0kFN+FP+TPTP是等价的,实际上,分母中 ∑ j = 0 k p i j \sum_{j=0}^{k} p_{i j} j=0kpij表示i的所有预测结果,即FN+TP, ∑ j = 0 k p j i \sum_{j=0}^{k} p_{j i} j=0kpji表示所有预测为i的值,即FP+TP,最后减去一个重合的 p i i p_{i i} pii即TP。
那么TP、FN、FP怎么计算呢?这就涉及到混淆矩阵了。

假设有一张图片,其所有的像素分属于4个类别。在预测分割之后,我们可以根据真实值和预测值获得一个混淆矩阵,形如:
语义分割的评价指标——MIoU_第1张图片
每一行表示某一类真实值,每一列表示预测为某一类, p i j p_{i j} pij表示将i预测为j的像素数量。以类1为例, p 11 p_{1 1} p11表示实际为类1且预测为类1,即TP; p 12 + p 13 + p 14 p_{1 2}+p_{1 3}+p_{1 4} p12+p13+p14表示实际为类1却预测为其他类,即FN; p 21 + p 31 + p 41 p_{2 1}+p_{3 1}+p_{4 1} p21+p31+p41表示错误地预测为类1,即FP;其余为TN。其他的类别可以类比。
所以,IoU的交就是混淆矩阵对角线上的值,
IoU的并就是将混淆矩阵的每一行的和( ∑ j = 0 k p i j \sum_{j=0}^{k} p_{i j} j=0kpij)加上每一列的和( ∑ j = 0 k p j i \sum_{j=0}^{k} p_{j i} j=0kpji),再减去对角线上的值( p i i p_{i i} pii)。

总结一下,计算MIoU的三个步骤:

  1. 计算混淆矩阵
  2. 计算每个类别的IoU
  3. 对每个类别的IoU取平均

代码

下面给出计算MIoU的代码:
首先是计算混淆矩阵:

def _fast_hist(label_true, label_pred, n_class):
    """
    label_true是转化为一维数组的真实标签,label_pred是转化为一维数组的预测结果,n_class是类别数
    hist是一个混淆矩阵
    hist是一个二维数组,可以写成hist[label_true][label_pred]的形式
    最后得到的这个数组的意义就是行下标表示的类别预测成列下标类别的数量
    """
    # mask在和label_true相对应的索引的位置上填入true或者false
    # label_true[mask]会把mask中索引为true的元素输出
    mask = (label_true >= 0) & (label_true < n_class)
    # n_class * label_true[mask].astype(int) + label_pred[mask]计算得到的是二维数组元素
    # 变成一位数组元素的时候的地址取值(每个元素大小为1),返回的是一个numpy的list
    # np.bincount()会给出索引对应的元素个数
    hist = np.bincount(n_class * label_true[mask].astype(int) + label_pred[mask],
                       minlength=n_class ** 2).reshape(n_class, n_class)
    return hist

接着是每个类别的IoU:

def per_class_iu(hist):
    # 矩阵的对角线上的值组成的一维数组/(矩阵的每行求和+每列求和-对角线上的值),返回值形状(n,)
    return np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist))

然后对每个类别的IoU取平均,得到mIoU:

np.mean(per_class_iu(hist))

Reference
https://blog.csdn.net/weixin_44791964/article/details/107687970

你可能感兴趣的:(语义分割)