在图像分割任务中,平均交并比(Mean Intersection over Union, mIoU)是一种常用的评估指标。计算 mIoU 的方式有两种:逐张图像计算 mIoU 再取平均值,和将所有图像的分割结果合并后再计算 mIoU。这两种方法在计算结果上可能存在差异。
1. 逐张图像计算 mIoU 再取平均值
步骤
-
计算每张图像的 IoU:
对每张图像,分别计算每个类别的 IoU。
IoU = TP TP + FP + FN \text{IoU} = \frac{\text{TP}}{\text{TP} + \text{FP} + \text{FN}} IoU=TP+FP+FNTP -
求每张图像的 mIoU:
计算每张图像的各个类别 IoU 的平均值,得到该图像的 mIoU。
mIoU image = 1 C ∑ i = 1 C IoU i \text{mIoU}_{\text{image}} = \frac{1}{C} \sum_{i=1}^{C} \text{IoU}_i mIoUimage=C1i=1∑CIoUi
其中 (C) 是类别数。 -
取所有图像的 mIoU 平均值:
将所有图像的 mIoU 取平均值,得到总体的 mIoU。
mIoU mean = 1 N ∑ j = 1 N mIoU image , j \text{mIoU}_{\text{mean}} = \frac{1}{N} \sum_{j=1}^{N} \text{mIoU}_{\text{image}, j} mIoUmean=N1j=1∑NmIoUimage,j
其中 (N) 是图像数。
2. 合并所有图像的分割结果后再计算 mIoU
步骤
-
合并所有图像的分割结果:
对每个类别,将所有图像的分割结果合并为一个大的真值掩码和预测掩码。 -
计算合并后的 IoU:
计算合并后的各个类别的 IoU。
IoU combined , i = ∑ j = 1 N TP i j ∑ j = 1 N ( TP i j + FP i j + FN i j ) \text{IoU}_{\text{combined}, i} = \frac{\sum_{j=1}^{N} \text{TP}_{ij}}{\sum_{j=1}^{N} (\text{TP}_{ij} + \text{FP}_{ij} + \text{FN}_{ij})} IoUcombined,i=∑j=1N(TPij+FPij+FNij)∑j=1NTPij -
求总体 mIoU:
计算所有类别的 IoU 平均值,得到总体的 mIoU。
mIoU combined = 1 C ∑ i = 1 C IoU combined , i \text{mIoU}_{\text{combined}} = \frac{1}{C} \sum_{i=1}^{C} \text{IoU}_{\text{combined}, i} mIoUcombined=C1i=1∑CIoUcombined,i
区别与影响
-
逐张图像计算 mIoU 再取平均值:
- 每张图像的 mIoU 先单独计算,最后取平均。
- 这种方法对每张图像的 mIoU 赋予了同等权重,不考虑每张图像中不同类别的像素分布差异。
- 更加适合用于图像数目较少且各图像内的类别分布较为均匀的情况。
-
合并所有图像的分割结果后再计算 mIoU:
- 先合并所有图像的结果,再计算整体的 IoU。
- 这种方法考虑了所有图像中各类别像素总数的分布,更加准确地反映了整体性能。
- 更加适合用于图像数量多且类别分布不均的情况。
逐张图像计算 mIoU 再取平均值
def compute_iou(pred_mask, true_mask):
intersection = np.logical_and(pred_mask, true_mask)
union = np.logical_or(pred_mask, true_mask)
iou = (np.sum(intersection) + 1e-6) / (np.sum(union) + 1e-6)
return iou
def compute_mean_iou(pred_images, true_images):
num_classes = 3 # 获取类别数
pred_images = np.eye(num_classes)[pred_images] # 进行One-Hot编码
true_images = np.eye(num_classes)[true_images] # 进行One-Hot编码
# print(pred_images.shape,true_images.shape)
mean_iou = 0.0
for i in range(num_classes):
pred_image = pred_images[:, :, i]
true_image = true_images[:, :, i]
iou = compute_iou(pred_image, true_image)
mean_iou += iou
mean_iou /= num_classes
return mean_iou
合并所有图像的分割结果后再计算 mIoU
class ConfusionMatrix(object):
def __init__(self, num_classes):
self.num_classes = num_classes
self.mat = None
def update(self, a, b):
n = self.num_classes
if self.mat is None:
# 创建混淆矩阵
self.mat = np.zeros((n, n), dtype=np.int64)
# 寻找GT中为目标的像素索引
k = (a >= 0) & (a < n)
# 统计像素真实类别a[k]被预测成类别b[k]的个数(这里的做法很巧妙)
inds = n * a[k] + b[k]
self.mat += np.bincount(inds, minlength=n ** 2).reshape(n, n)
def reset(self):
if self.mat is not None:
self.mat = None
def compute(self):
h = self.mat
# 计算全局预测准确率(混淆矩阵的对角线为预测正确的个数)
acc_global = np.diag(h).sum() / h.sum()
# 计算每个类别的准确率
acc = np.diag(h) / h.sum(1)
# 计算每个类别预测与真实目标的iou
iu = np.diag(h) / (h.sum(1) + h.sum(0) - np.diag(h))
return acc_global, acc, iu
def get_miou(self):
acc_global, acc, iu = self.compute()
return iu.mean().item()