##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ## Created by: Hang Zhang ## ECE Department, Rutgers University ## Email: zhang.hang@rutgers.edu ## Copyright (c) 2017 ## ## This source code is licensed under the MIT-style license found in the ## LICENSE file in the root directory of this source tree ##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import threading import numpy as np import torch class SegmentationMetric(object): """Computes pixAcc and mIoU metric scroes """ def __init__(self, nclass): self.nclass = nclass self.lock = threading.Lock() self.reset() def update(self, labels, preds): def evaluate_worker(self, label, pred): correct, labeled = batch_pix_accuracy( pred, label) inter, union = batch_intersection_union( pred, label, self.nclass) with self.lock: self.total_correct += correct self.total_label += labeled self.total_inter += inter self.total_union += union return if isinstance(preds, torch.Tensor): evaluate_worker(self, labels, preds) elif isinstance(preds, (list, tuple)): threads = [threading.Thread(target=evaluate_worker, args=(self, label, pred), ) for (label, pred) in zip(labels, preds)] for thread in threads: thread.start() for thread in threads: thread.join() else: raise NotImplemented def get(self): pixAcc = 1.0 * self.total_correct / (np.spacing(1) + self.total_label) IoU = 1.0 * self.total_inter / (np.spacing(1) + self.total_union) mIoU = IoU.mean() return pixAcc, mIoU def reset(self): self.total_inter = 0 self.total_union = 0 self.total_correct = 0 self.total_label = 0 return def batch_pix_accuracy(output, target): """Batch Pixel Accuracy Args: predict: input 4D tensor target: label 3D tensor """ _, predict = torch.max(output, 1) predict = predict.cpu().numpy().astype('int64') + 1 target = target.cpu().numpy().astype('int64') + 1 pixel_labeled = np.sum(target > 0) pixel_correct = np.sum((predict == target)*(target > 0)) assert pixel_correct <= pixel_labeled, \ "Correct area should be smaller than Labeled" return pixel_correct, pixel_labeled def batch_intersection_union(output, target, nclass): """Batch Intersection of Union Args: predict: input 4D tensor target: label 3D tensor nclass: number of categories (int) """ _, predict = torch.max(output, 1) mini = 1 maxi = nclass nbins = nclass predict = predict.cpu().numpy().astype('int64') + 1 target = target.cpu().numpy().astype('int64') + 1 predict = predict * (target > 0).astype(predict.dtype) intersection = predict * (predict == target) # areas of intersection and union area_inter, _ = np.histogram(intersection, bins=nbins, range=(mini, maxi)) area_pred, _ = np.histogram(predict, bins=nbins, range=(mini, maxi)) area_lab, _ = np.histogram(target, bins=nbins, range=(mini, maxi)) area_union = area_pred + area_lab - area_inter assert (area_inter <= area_union).all(), \ "Intersection area should be smaller than Union area" return area_inter, area_union # ref https://github.com/CSAILVision/sceneparsing/blob/master/evaluationCode/utils_eval.py def pixel_accuracy(im_pred, im_lab): im_pred = np.asarray(im_pred) im_lab = np.asarray(im_lab) # Remove classes from unlabeled pixels in gt image. # We should not penalize detections in unlabeled portions of the image. pixel_labeled = np.sum(im_lab > 0) pixel_correct = np.sum((im_pred == im_lab) * (im_lab > 0)) #pixel_accuracy = 1.0 * pixel_correct / pixel_labeled return pixel_correct, pixel_labeled def intersection_and_union(im_pred, im_lab, num_class): im_pred = np.asarray(im_pred) im_lab = np.asarray(im_lab) # Remove classes from unlabeled pixels in gt image. im_pred = im_pred * (im_lab > 0) # Compute area intersection: intersection = im_pred * (im_pred == im_lab) area_inter, _ = np.histogram(intersection, bins=num_class-1, range=(1, num_class - 1)) # Compute area union: area_pred, _ = np.histogram(im_pred, bins=num_class-1, range=(1, num_class - 1)) area_lab, _ = np.histogram(im_lab, bins=num_class-1, range=(1, num_class - 1)) area_union = area_pred + area_lab - area_inter return area_inter, area_union