语义分割各种评价指标实现

前言

现存其实已经有很多博客实现了这个代码,但是可能不完整或者不能直接用于测试集的指标计算,这里简单概括一下。

一些概念、代码参考: [1] 憨批的语义分割9——语义分割评价指标mIOU的计算

[2]【语义分割】评价指标:PA、CPA、MPA、IoU、MIoU详细总结和代码实现(零基础从入门到精通系列!)

[3] 【语义分割】评价指标总结及代码实现

混淆矩阵

语义分割的各种评价指标都是基于混淆矩阵来的。

对于一个只有背景0和目标1的语义分割任务来说,混淆矩阵可以简单理解为:

TP(1被认为是1)FP(0被认为是1)
FN(1被认为是0)TN(0被认为是0)

各种指标的计算

1. 像素准确率 PA =(TP+TN)/(TP+TN+FP+FN)

2. 类别像素准确率 CPA = TP / (TP+FP)

3. 类别平均像素准确率 MPA = (CPA1+...+CPAn)/ n

4. 交并比 IoU = TP / (TP+FP+FN) 

5. 平均交并比 MIoU = (IoU1+...+IoUn) / n

6. 频权交并比 FWIoU =  [ (TP+FN) / (TP+FP+TN+FN) ] * [ TP / (TP + FP + FN) ]

代码实现

"""
https://blog.csdn.net/sinat_29047129/article/details/103642140
https://www.cnblogs.com/Trevo/p/11795503.html
refer to https://github.com/jfzhang95/pytorch-deeplab-xception/blob/master/utils/metrics.py
"""
import numpy as np
import os
from PIL import Image
__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) # 混淆矩阵n*n,初始值全0

    # 像素准确率PA,预测正确的像素/总像素
    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

    # 类别像素准确率CPA,返回n*1的值,代表每一类,包括背景
    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) / np.maximum(self.confusionMatrix.sum(axis=1),1)
        return classAcc

    # 类别平均像素准确率MPA,对每一类的像素准确率求平均
    def meanPixelAccuracy(self):
        classAcc = self.classPixelAccuracy()
        meanAcc = np.nanmean(classAcc)
        return meanAcc

    # MIoU
    def meanIntersectionOverUnion(self):
        # Intersection = TP Union = TP + FP + FN
        # IoU = TP / (TP + FP + FN)
        intersection = np.diag(self.confusionMatrix)
        union = np.maximum(np.sum(self.confusionMatrix, axis=1) + np.sum(self.confusionMatrix, axis=0) - np.diag(
            self.confusionMatrix), 1)
        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].astype(int) + 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.confusionMatrix, axis=1) / np.sum(self.confusionMatrix)
        iu = np.diag(self.confusionMatrix) / (
                np.sum(self.confusionMatrix, axis=1) + np.sum(self.confusionMatrix, axis=0) -
                np.diag(self.confusionMatrix))
        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))

def old():
    imgPredict = np.array([0, 0, 0, 1, 2, 2])
    imgLabel = np.array([0, 0, 1, 1, 2, 2])
    metric = SegmentationMetric(3)
    metric.addBatch(imgPredict, imgLabel)
    acc = metric.pixelAccuracy()
    macc = metric.meanPixelAccuracy()
    mIoU = metric.meanIntersectionOverUnion()
    print(acc, macc, mIoU)

def evaluate1(pre_path, label_path):
    acc_list = []
    macc_list = []
    mIoU_list = []
    fwIoU_list = []

    pre_imgs = os.listdir(pre_path)
    lab_imgs = os.listdir(label_path)

    for i, p in enumerate(pre_imgs):
        imgPredict = Image.open(pre_path+p)
        imgPredict = np.array(imgPredict)
        # imgPredict = imgPredict[:,:,0]
        imgLabel = Image.open(label_path+lab_imgs[i])
        imgLabel = np.array(imgLabel)
        # imgLabel = imgLabel[:,:,0]

        metric = SegmentationMetric(2) # 表示分类个数,包括背景
        metric.addBatch(imgPredict, imgLabel)
        acc = metric.pixelAccuracy()
        macc = metric.meanPixelAccuracy()
        mIoU = metric.meanIntersectionOverUnion()
        fwIoU = metric.Frequency_Weighted_Intersection_over_Union()

        acc_list.append(acc)
        macc_list.append(macc)
        mIoU_list.append(mIoU)
        fwIoU_list.append(fwIoU)

        # print('{}: acc={}, macc={}, mIoU={}, fwIoU={}'.format(p, acc, macc, mIoU, fwIoU))

    return acc_list, macc_list, mIoU_list, fwIoU_list

def evaluate2(pre_path, label_path):
    pre_imgs = os.listdir(pre_path)
    lab_imgs = os.listdir(label_path)

    metric = SegmentationMetric(2)  # 表示分类个数,包括背景
    for i, p in enumerate(pre_imgs):
        imgPredict = Image.open(pre_path+p)
        imgPredict = np.array(imgPredict)
        imgLabel = Image.open(label_path+lab_imgs[i])
        imgLabel = np.array(imgLabel)

        metric.addBatch(imgPredict, imgLabel)

    return metric

if __name__ == '__main__':
    pre_path = './pre_path/'
    label_path = './label_path/'

    # 计算测试集每张图片的各种评价指标,最后求平均
    acc_list, macc_list, mIoU_list, fwIoU_list = evaluate1(pre_path, label_path)
    print('final1: acc={:.2f}%, macc={:.2f}%, mIoU={:.2f}%, fwIoU={:.2f}%'
          .format(np.mean(acc_list)*100, np.mean(macc_list)*100,
                  np.mean(mIoU_list)*100, np.mean(fwIoU_list)*100))

    # 加总测试集每张图片的混淆矩阵,对最终形成的这一个矩阵计算各种评价指标
    metric = evaluate2(pre_path, label_path)
    acc = metric.pixelAccuracy()
    macc = metric.meanPixelAccuracy()
    mIoU = metric.meanIntersectionOverUnion()
    fwIoU = metric.Frequency_Weighted_Intersection_over_Union()
    print('final2: acc={:.2f}%, macc={:.2f}%, mIoU={:.2f}%, fwIoU={:.2f}%'
          .format(acc*100, macc*100, mIoU*100, fwIoU*100))

说明

1. 使用上述代码时只需修改pre_path和label_path即可。label_path是真实标签的路径,为8位图;pre_path是训练好模型后,测试集生成的分割结果的路径,也是8位图。

metric = SegmentationMetric(2) 中,2表示的是该分割图的类别总数,包含背景,需对应修改。

2. 上述给出了两种指标的计算方式。

evaluate1是对测试集中产生的每张预测图片都计算对应的各种指标,最后对所有图片的结果进行求均值;

evaluate2是把测试集中产生的每张预测图片的混淆矩阵都加在一起,成为一个整个的混淆矩阵,最后对这一个矩阵求各种指标。

3. 我的测试结果如下:

final1: acc=93.68%, macc=79.05%, mIoU=69.85%, fwIoU=89.09%
final2: acc=93.68%, macc=78.72%, mIoU=70.71%, fwIoU=88.88%

可以看到,两种计算方法的结果在PA上是相等的,因为都是 正确的像素/总像素 ,不管是先加总再除还是先除再取平均结果都是相同的;而其他指标结果值略有不同。

一般论文中使用的是第2种,当图片本身为1600x1200时,无论是直接对原图进行评估还是将其裁剪成12张400x400大小图片进行评估,第2种的计算结果相等,而第1种结果不同。

4. 如果要打印每个类别的IoU或PA,在对应方法中返回即可。

  • 20
    点赞
  • 154
    收藏
    觉得还不错? 一键收藏
  • 78
    评论
### 回答1: 语义分割是图像处理中的一个任务,目的是将图像中的每个像素进行分类,识别出不同的物体或场景。评价指标是用来衡量模型对图像进行分割的准确程度的指标。 常用的语义分割评价指标有IoU(Intersection over Union)和mIoU(Mean Intersection over Union)。 IoU是指预测的分割结果和真实标签之间的交集面积与并集面积之比。具体计算公式为: IoU = (预测结果与真实标签的交集面积) / (预测结果与真实标签的并集面积) mIoU是所有图像预测结果的IoU的平均值。 以下是用Python计算语义分割评价指标的示例代码: ```python import numpy as np def calculate_iou(pred, target): intersection = np.logical_and(pred, target) union = np.logical_or(pred, target) iou_score = np.sum(intersection) / np.sum(union) return iou_score def calculate_miou(preds, targets): miou_scores = [] for pred, target in zip(preds, targets): iou_score = calculate_iou(pred, target) miou_scores.append(iou_score) miou = np.mean(miou_scores) return miou # 假设有5个图像的预测结果和真实标签 preds = [np.array([[1, 1, 0, 0], [1, 1, 0, 0], [0, 0, 1, 1], [0, 0, 1, 1]]), np.array([[1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 0, 0], [1, 1, 0, 0]]), np.array([[0, 0, 1, 1], [0, 0, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1]]), np.array([[1, 1, 1, 0], [1, 1, 1, 0], [0, 0, 0, 1], [0, 0, 0, 1]]), np.array([[0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 1], [0, 0, 0, 1]])] targets = [np.array([[1, 1, 0, 0], [1, 1, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0]]), np.array([[1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 0, 0], [1, 1, 0, 0]]), np.array([[0, 1, 1, 0], [0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1]]), np.array([[1, 1, 1, 0], [1, 1, 1, 0], [0, 0, 1, 1], [0, 0, 1, 1]]), np.array([[0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 1, 1]])] miou = calculate_miou(preds, targets) print("mIoU:", miou) ``` 这段代码中,首先定义了两个函数calculate_iou和calculate_miou,用于计算IoU和mIoU。 然后创建了5个预测结果和5个真实标签的示例数据。 最后调用calculate_miou函数计算mIoU,并输出结果。 该示例只是一个简单的演示,实际应用中可能需要考虑更复杂的情况,例如处理多分类问题或处理整个数据集的评价指标。 ### 回答2: 在语义分割任务中,我们常常需要评估模型的性能。以下是一些常用的语义分割评价指标及其对应的 Python 代码实现: 1. 像素准确率(Pixel Accuracy):计算预测结果中正确分类的像素数目与总像素数目的比值。 ```python def pixel_accuracy(y_true, y_pred): total_pixels = y_true.size correct_pixels = np.sum(y_true == y_pred) accuracy = correct_pixels / total_pixels return accuracy ``` 2. 平均像素准确率(Mean Pixel Accuracy):计算每个类别的像素准确率的平均值。 ```python def mean_pixel_accuracy(y_true, y_pred, num_classes): class_pixels = np.zeros(num_classes) for c in range(num_classes): class_pixels[c] = np.sum(np.logical_and(y_true == c, y_pred == c)) class_accuracy = class_pixels / np.sum(y_true == y_pred, axis=(0, 1)) mean_accuracy = np.mean(class_accuracy) return mean_accuracy ``` 3. 平均交并比(Mean Intersection over Union,mIOU):计算每个类别的交并比的平均值。 ```python def mean_iou(y_true, y_pred, num_classes): class_iou = np.zeros(num_classes) for c in range(num_classes): intersection = np.sum(np.logical_and(y_true == c, y_pred == c)) union = np.sum(np.logical_or(y_true == c, y_pred == c)) class_iou[c] = intersection / union mean_iou = np.mean(class_iou) return mean_iou ``` 以上是语义分割评价指标的一些示例代码。根据实际需求,也可以使用其他指标来评估模型性能。 ### 回答3: Python 语义分割评价指标代码通常用于评估语义分割模型的性能。以下是一个示例代码,用于计算语义分割模型的准确率、精确率、召回率和F1值。 ```python import numpy as np def evaluate_semantic_segmentation(predictions, targets, num_classes): """ 计算语义分割模型的评价指标:准确率、精确率、召回率和F1值 :param predictions: 预测的语义分割结果,形状为[H, W] :param targets: 真实的语义分割标签,形状为[H, W] :param num_classes: 类别数量 :return: 准确率、精确率、召回率和F1值 """ confusion_matrix = np.zeros((num_classes, num_classes), dtype=np.int32) for i in range(predictions.shape[0]): for j in range(predictions.shape[1]): predicted_class = predictions[i, j] target_class = targets[i, j] confusion_matrix[predicted_class, target_class] += 1 tp = np.diag(confusion_matrix) fp = confusion_matrix.sum(axis=0) - tp fn = confusion_matrix.sum(axis=1) - tp accuracy = tp.sum() / confusion_matrix.sum() precision = tp / (tp + fp) recall = tp / (tp + fn) f1 = (2 * precision * recall) / (precision + recall) return accuracy, precision, recall, f1 ``` 这个代码中,我们首先定义了一个大小为num_classes x num_classes的混淆矩阵,用于计算预测的类别与真实的类别之间的匹配情况。然后,我们遍历预测结果和真实标签,并更新混淆矩阵。接着,我们计算真正类别(tp)、假正类别(fp)和假负类别(fn)的数量。最后,我们使用这些信息计算准确率、精确率、召回率和F1值。
评论 78
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值