深度學習圖像分割評測指標MIOU之python代碼詳解

博主剛期末考完,就又又又被抓來幹活了!這次的任務是!幫學長的論文實驗的分割結果與正確的分割結果(ground truth)做個對比,給出測評結果。於是,我就找了目前幾個主流的評測指標如下:

  • Pixel Accuracy(PA,像素精度):這是最簡單的度量,爲標記正確的像素佔總像素的比例。

  • Mean Pixel Accuracy(MPA,均像素精度):是PA的一種簡單提升,計算每個類內被正確分類像素數的比例,之後求所有類的平均。

  • Mean Intersection over Union(MIoU,均交併比):爲語義分割的標準度量。其計算兩個集合的交集和並集之比,在語義分割的問題中,這兩個集合爲真實值(ground truth)和預測值(predicted segmentation)。這個比例可以變形爲正真數(intersection)比上真正、假負、假正(並集)之和。在每個類上計算IoU,之後平均。(最常用)

  • Frequency Weighted Intersection over Union(FWIoU,頻權交併比):爲MIoU的一種提升,這種方法根據每個類出現的頻率爲其設置權重。

    我們選用的評測度量是MIOU,下面介紹一下MIOU衡量的具體思想。
    如圖所示,A代表ground truth,B代表預測樣本。針對預測值和真實值之間的關係,我們可以將樣本分成以下四部分(這是論文中常見的四個名稱,看了一學期的論文現在我才頓悟啊!真的菜!!)

  • 真正值(TP):預測值爲1,真實值爲1;橙色,A∩B

  • 真負值(TN):預測值爲0,真實值爲0;白色,~(A∪B)

  • 假正值(FP):預測值爲1,真實值爲0;黃色,B-(A∩B)

  • 假負值(FN):預測值爲0,真實值爲1;綠色,A-(A∩B)
    MIOU=TP/(FP+FN+TP)
    在這裏插入圖片描述
    接下來給出計算MIOU的核心代碼,基於此篇博客進行更加詳細地說明和相應的改寫(因爲實驗所用的數據集是VOC2011,不是CITYSCAPES)(https://blog.csdn.net/jiongnima/article/details/84750819)

#!/usr/bin/python
# -*- coding: utf-8 -*-

import numpy as np
import argparse
import json
from PIL import Image
from os.path import join

#設標籤寬W,長H
def fast_hist(a, b, n):#a是轉化成一維數組的標籤,形狀(H×W,);b是轉化成一維數組的標籤,形狀(H×W,);n是類別數目,實數(在這裏爲19)
    '''
	核心代碼
	'''
    k = (a >= 0) & (a < n) #k是一個一維bool數組,形狀(H×W,);目的是找出標籤中需要計算的類別(去掉了背景) k=0或1
    return np.bincount(n * a[k].astype(int) + b[k], minlength=n ** 2).reshape(n, n)#np.bincount計算了從0到n**2-1這n**2個數中每個數出現的次數,返回值形狀(n, n)


def per_class_iu(hist):#分別爲每個類別(在這裏是19類)計算mIoU,hist的形狀(n, n)
    '''
	核心代碼
	'''
    return np.diag(hist) / (hist.sum(1) + hist.sum(0) - np.diag(hist))#矩陣的對角線上的值組成的一維數組/矩陣的所有元素之和,返回值形狀(n,)
#hist.sum(0)=按列相加  hist.sum(1)按行相加


#def label_mapping(input, mapping):#主要是因爲CityScapes標籤裏面原類別太多,這樣做把其他類別轉換成算法需要的類別(共19類)和背景(標註爲255)
#    output = np.copy(input)#先複製一下輸入圖像
#    for ind in range(len(mapping)):
#        output[input == mapping[ind][0]] = mapping[ind][1]#進行類別映射,最終得到的標籤裏面之後0-18這19個數加255(背景)
#    return np.array(output, dtype=np.int64)#返回映射的標籤
'''
  compute_mIoU函數原始以CityScapes圖像分割驗證集爲例來計算mIoU值的(可以根據自己數據集的不同更改類別數num_classes及類別名稱name_classes),本函數除了最主要的計算mIoU的代碼之外,還完成了一些其他操作,比如進行數據讀取,因爲原文是做圖像分割遷移方面的工作,因此還進行了標籤映射的相關工作,在這裏筆者都進行註釋。大家在使用的時候,可以忽略原作者的數據讀取過程,只需要注意計算mIoU的時候每張圖片分割結果與標籤要配對。主要留意mIoU指標的計算核心代碼即可。
'''
def compute_mIoU(gt_dir, pred_dir, devkit_dir):#計算mIoU的函數
    """
    Compute IoU given the predicted colorized images and 
    """
    with open('/home/ubuntu/DeepLab/datasets/VOCdevkit/VOC2012/ImageSets/Segmentation/info.json', 'r') as fp: 
        #讀取info.json,裏面記錄了類別數目,類別名稱。(我們數據集是VOC2011,相應地改了josn文件)
        info = json.load(fp) 
    num_classes = np.int(info['classes'])#讀取類別數目,這裏是20類
    print('Num classes', num_classes)#打印一下類別數目
    name_classes = np.array(info['label'], dtype=np.str)#讀取類別名稱
    #mapping = np.array(info['label2train'], dtype=np.int)#讀取標籤映射方式,詳見博客中附加的info.json文件
    hist = np.zeros((num_classes, num_classes))#hist初始化爲全零,在這裏的hist的形狀是[20, 20]
    '''
    原代碼是有進行類別映射,所以通過json文件來存放類別數目、類別名稱、 標籤映射方式。而我們只需要讀取類別數目和類別名稱即可,可以按下面這段代碼將其寫死
    num_classes=20
    print('Num classes', num_classes)
    name_classes = ["aeroplane","bicycle","bird","boat","bottle","bus","car", "cat","chair","cow","diningtable","dog","horse","motobike","person","pottedplant","sheep","sofa","train","tvmonitor"]
    hist = np.zeros((num_classes, num_classes))
    '''
    image_path_list = join(devkit_dir, 'val2.txt')#在這裏打開記錄分割圖片名稱的txt
    label_path_list = join(devkit_dir, 'val2.txt')#ground truth和自己的分割結果txt一樣
    gt_imgs = open(label_path_list, 'r').read().splitlines()#獲得驗證集標籤名稱列表
    gt_imgs = [join(gt_dir, x) for x in gt_imgs]#獲得驗證集標籤路徑列表,方便直接讀取
    pred_imgs = open(image_path_list, 'r').read().splitlines()#獲得驗證集圖像分割結果名稱列表
    pred_imgs = [join(pred_dir, x) for x in pred_imgs]
    #pred_imgs = [join(pred_dir, x.split('/')[-1]) for x in pred_imgs]#獲得驗證集圖像分割結果路徑列表,方便直接讀取


    for ind in range(len(gt_imgs)):#讀取每一個(圖片-標籤)對
        pred = np.array(Image.open(pred_imgs[ind]))#讀取一張圖像分割結果,轉化成numpy數組
        label = np.array(Image.open(gt_imgs[ind]))#讀取一張對應的標籤,轉化成numpy數組
        #print pred.shape
        #print label.shape
        #label = label_mapping(label, mapping)#進行標籤映射(因爲沒有用到全部類別,因此捨棄某些類別),可忽略
        if len(label.flatten()) != len(pred.flatten()):#如果圖像分割結果與標籤的大小不一樣,這張圖片就不計算
            print('Skipping: len(gt) = {:d}, len(pred) = {:d}, {:s}, {:s}'.format(len(label.flatten()), len(pred.flatten()), gt_imgs[ind], pred_imgs[ind]))
            continue
        hist += fast_hist(label.flatten(), pred.flatten(), num_classes)#對一張圖片計算19×19的hist矩陣,並累加
        if ind > 0 and ind % 10 == 0:#每計算10張就輸出一下目前已計算的圖片中所有類別平均的mIoU值
            print('{:d} / {:d}: {:0.2f}'.format(ind, len(gt_imgs), 100*np.mean(per_class_iu(hist))))
            print(per_class_iu(hist))
    
    mIoUs = per_class_iu(hist)#計算所有驗證集圖片的逐類別mIoU值
    for ind_class in range(num_classes):#逐類別輸出一下mIoU值
        print('===>' + name_classes[ind_class] + ':\t' + str(round(mIoUs[ind_class] * 100, 2)))
    print('===> mIoU: ' + str(round(np.nanmean(mIoUs) * 100, 2)))#在所有驗證集圖像上求所有類別平均的mIoU值,計算時忽略NaN值
    return mIoUs


compute_mIoU('/home/ubuntu/DeepLab/datasets/VOCdevkit/VOC2012/SegmentationClass/',
                 '/home/ubuntu/DeepLab/datasets/VOCdevkit/VOC2012/zhuosetu/',
                 '/home/ubuntu/DeepLab/datasets/VOCdevkit/VOC2012/ImageSets/Segmentation/'
                )#執行主函數 三個路徑分別爲 ‘ground truth’,'自己的實驗分割結果',‘分割圖片名稱txt文件’

這是測評結果
在這裏插入圖片描述

發佈了6 篇原創文章 · 獲贊 13 · 訪問量 5947
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章