本文記錄了語義分割准確性評價指標的總結以及代碼實現
對於像素級別的分類,最常用的評價指標是Pixel Accuracy(像素准確率)和Mean Inetersection over Union(平均交並比),二者的計算都是建立在混淆矩陣的基礎上的。因此首先來介紹一下混淆矩陣,之后分別介紹PA,MPA,MIoU,FWIoU,最后附上代碼實現。
首先假定數據集中有n+1類(0~n),0通常表示背景。使用Pii表示原本為i類同時預測為i類,即真正(TP)和真負(TN), Pij表示原本為i類被預測為j類,即假正(FP)和假負(FN),如果第i類為正類,i!=j時,那么Pii表示TP,Pjj表示TN,Pij表示FP,Pji表示FN。
像素准確率(PA)
像素准確率是所有分類正確的像素數占像素總數的比例。公式化如下:

利用混淆矩陣計算則為(對角線元素之和除以矩陣所有元素之和)
平均像素准確率(MPA)
平均像素准確率是分別計算每個類別分類正確的像素數占所有預測為該類別像素數的比例,即精確率,然后累加求平均。公式化如下:

利用混淆矩陣計算公式為(每一類的精確率Pi都等於對角線上的TP除以對應列的像素數)

平均交並比(mloU)
平均交並比是對每一類預測的結果和真實值的交集與並集的比值求和平均的結果。公式化如下

IoU利用混淆矩陣計算:
解釋如下:
如圖所示,僅僅針對某一類來說,紅色部分代表真實值,真實值有兩部分組成TP,FN;黃色部分代表預測值,預測值有兩部分組成TP,FP;白色部分代表TN(真負);
所以他們的交集就是TP+FP+FN,並集為TP

頻權交並比(FWloU)
頻權交並比是根據每一類出現的頻率設置權重,權重乘以每一類的IoU並進行求和。公式化如下:

利用混淆矩陣計算:每個類別的真實數目為TP+FN,總數為TP+FP+TN+FN,其中每一類的權重和其IoU的乘積計算公式如下,在將所有類別的求和即可

代碼實現
"""
refer to https://github.com/jfzhang95/pytorch-deeplab-xception/blob/master/utils/metrics.py
"""
import numpy as np
__all__ = ['SegmentationMetric']
"""
confusionMetric
P\L P N
P TP FP
N FN TN
"""
class SegmentationMetric(object):
def __init__(self, numClass):
self.numClass = numClass
self.confusionMatrix = np.zeros((self.numClass,)*2)
def pixelAccuracy(self):
# return all class overall pixel accuracy
# acc = (TP + TN) / (TP + TN + FP + TN)
acc = np.diag(self.confusionMatrix).sum() / self.confusionMatrix.sum()
return acc
def classPixelAccuracy(self):
# return each category pixel accuracy(A more accurate way to call it precision)
# acc = (TP) / TP + FP
classAcc = np.diag(self.confusionMatrix) / self.confusionMatrix.sum(axis=1)
return classAcc
def meanPixelAccuracy(self):
classAcc = self.classPixelAccuracy()
meanAcc = np.nanmean(classAcc)
return meanAcc
def meanIntersectionOverUnion(self):
# Intersection = TP Union = TP + FP + FN
# IoU = TP / (TP + FP + FN)
intersection = np.diag(self.confusionMatrix)
union = np.sum(self.confusionMatrix, axis=1) + np.sum(self.confusionMatrix, axis=0) - np.diag(self.confusionMatrix)
IoU = intersection / union
mIoU = np.nanmean(IoU)
return mIoU
def genConfusionMatrix(self, imgPredict, imgLabel):
# remove classes from unlabeled pixels in gt image and predict
mask = (imgLabel >= 0) & (imgLabel < self.numClass)
label = self.numClass * imgLabel[mask] + imgPredict[mask]
count = np.bincount(label, minlength=self.numClass**2)
confusionMatrix = count.reshape(self.numClass, self.numClass)
return confusionMatrix
def Frequency_Weighted_Intersection_over_Union(self):
# FWIOU = [(TP+FN)/(TP+FP+TN+FN)] *[TP / (TP + FP + FN)]
freq = np.sum(self.confusion_matrix, axis=1) / np.sum(self.confusion_matrix)
iu = np.diag(self.confusion_matrix) / (
np.sum(self.confusion_matrix, axis=1) + np.sum(self.confusion_matrix, axis=0) -
np.diag(self.confusion_matrix))
FWIoU = (freq[freq > 0] * iu[freq > 0]).sum()
return FWIoU
def addBatch(self, imgPredict, imgLabel):
assert imgPredict.shape == imgLabel.shape
self.confusionMatrix += self.genConfusionMatrix(imgPredict, imgLabel)
def reset(self):
self.confusionMatrix = np.zeros((self.numClass, self.numClass))
if __name__ == '__main__':
imgPredict = np.array([0, 0, 1, 1, 2, 2])
imgLabel = np.array([0, 0, 1, 1, 2, 2])
metric = SegmentationMetric(3)
metric.addBatch(imgPredict, imgLabel)
acc = metric.pixelAccuracy()
mIoU = metric.meanIntersectionOverUnion()
print(acc, mIoU)
