| import numpy as np | |
| from scipy import stats | |
| import math | |
| class AverageMeter(object): | |
| def __init__(self): | |
| self.initialized = False | |
| self.val = None | |
| self.avg = None | |
| self.sum = None | |
| self.count = None | |
| def initialize(self, val, weight): | |
| self.val = val | |
| self.avg = val | |
| self.sum = val * weight | |
| self.count = weight | |
| self.initialized = True | |
| def update(self, val, weight=1): | |
| if not self.initialized: | |
| self.initialize(val, weight) | |
| else: | |
| self.add(val, weight) | |
| def add(self, val, weight): | |
| self.val = val | |
| self.sum += val * weight | |
| self.count += weight | |
| self.avg = self.sum / self.count | |
| def value(self): | |
| return self.val | |
| def average(self): | |
| return self.avg | |
| def get_scores(self): | |
| scores_dict = cm2score(self.sum) | |
| return scores_dict | |
| def clear(self): | |
| self.initialized = False | |
| class ConfuseMatrixMeter(AverageMeter): | |
| def __init__(self, n_class): | |
| super(ConfuseMatrixMeter, self).__init__() | |
| self.n_class = n_class | |
| def update_cm(self, pr, gt, weight=1): | |
| val = get_confuse_matrix(num_classes=self.n_class, label_gts=gt, label_preds=pr) | |
| self.update(val, weight) | |
| current_score = cm2F1(val) | |
| return current_score | |
| def get_scores(self): | |
| scores_dict = cm2score(self.sum) | |
| return scores_dict | |
| def harmonic_mean(xs): | |
| harmonic_mean = len(xs) / sum((x+1e-6)**-1 for x in xs) | |
| return harmonic_mean | |
| def cm2F1(confusion_matrix): | |
| hist = confusion_matrix | |
| n_class = hist.shape[0] | |
| tp = np.diag(hist) | |
| sum_a1 = hist.sum(axis=1) | |
| sum_a0 = hist.sum(axis=0) | |
| acc = tp.sum() / (hist.sum() + np.finfo(np.float32).eps) | |
| recall = tp / (sum_a1 + np.finfo(np.float32).eps) | |
| precision = tp / (sum_a0 + np.finfo(np.float32).eps) | |
| F1 = 2 * recall * precision / (recall + precision + np.finfo(np.float32).eps) | |
| mean_F1 = np.nanmean(F1) | |
| return mean_F1 | |
| def cm2score(confusion_matrix): | |
| hist = confusion_matrix | |
| n_class = hist.shape[0] | |
| if n_class > 2: | |
| hist_fg = hist[1:, 1:] | |
| c2hist = np.zeros((2, 2)) | |
| c2hist[0][0] = hist[0][0] | |
| c2hist[0][1] = hist.sum(1)[0] - hist[0][0] | |
| c2hist[1][0] = hist.sum(0)[0] - hist[0][0] | |
| c2hist[1][1] = hist_fg.sum() | |
| hist_n0 = hist.copy() | |
| hist_n0[0][0] = 0 | |
| kappa_n0 = cal_kappa(hist_n0) | |
| iu_scd = np.nan_to_num(np.diag(c2hist) / (c2hist.sum(1) + c2hist.sum(0) - np.diag(c2hist))) | |
| IoU_fg = iu_scd[1] | |
| IoU_mean = (iu_scd[0] + iu_scd[1]) / 2 | |
| Sek = (kappa_n0 * math.exp(IoU_fg)) / math.e | |
| pixel_sum = hist.sum() | |
| change_pred_sum = pixel_sum - hist.sum(1)[0].sum() | |
| change_label_sum = pixel_sum - hist.sum(0)[0].sum() | |
| change_ratio = change_label_sum / pixel_sum | |
| SC_TP = np.diag(hist[1:, 1:]).sum() | |
| SC_Precision = np.nan_to_num(SC_TP / change_pred_sum) + np.finfo(np.float32).eps | |
| SC_Recall = np.nan_to_num(SC_TP / change_label_sum) + np.finfo(np.float32).eps | |
| Fscd = stats.hmean([SC_Precision, SC_Recall]) | |
| tp = np.diag(hist) | |
| sum_a1 = hist.sum(axis=1) | |
| sum_a0 = hist.sum(axis=0) | |
| acc = tp.sum() / (hist.sum() + np.finfo(np.float32).eps) | |
| recall = tp / (sum_a1 + np.finfo(np.float32).eps) | |
| precision = tp / (sum_a0 + np.finfo(np.float32).eps) | |
| F1 = 2*recall * precision / (recall + precision + np.finfo(np.float32).eps) | |
| mean_F1 = np.nanmean(F1) | |
| iu = tp / (sum_a1 + hist.sum(axis=0) - tp + np.finfo(np.float32).eps) | |
| mean_iu = np.nanmean(iu) | |
| freq = sum_a1 / (hist.sum() + np.finfo(np.float32).eps) | |
| fwavacc = (freq[freq > 0] * iu[freq > 0]).sum() | |
| cls_iou = dict(zip(['iou_'+str(i) for i in range(n_class)], iu)) | |
| cls_precision = dict(zip(['precision_'+str(i) for i in range(n_class)], precision)) | |
| cls_recall = dict(zip(['recall_'+str(i) for i in range(n_class)], recall)) | |
| cls_F1 = dict(zip(['F1_'+str(i) for i in range(n_class)], F1)) | |
| if n_class > 2: | |
| score_dict = {'acc': acc, 'miou': mean_iu, 'mf1':mean_F1, 'SCD_Sek':Sek, 'Fscd':Fscd, 'SCD_IoU_mean':IoU_mean} | |
| else: | |
| score_dict = {'acc': acc, 'miou': mean_iu, 'mf1':mean_F1} | |
| score_dict.update(cls_iou) | |
| score_dict.update(cls_F1) | |
| score_dict.update(cls_precision) | |
| score_dict.update(cls_recall) | |
| return score_dict | |
| def get_confuse_matrix(num_classes, label_gts, label_preds): | |
| def __fast_hist(label_gt, label_pred): | |
| mask = (label_gt >= 0) & (label_gt < num_classes) | |
| hist = np.bincount(num_classes * label_gt[mask].astype(int) + label_pred[mask], | |
| minlength=num_classes**2).reshape(num_classes, num_classes) | |
| return hist | |
| confusion_matrix = np.zeros((num_classes, num_classes)) | |
| for lt, lp in zip(label_gts, label_preds): | |
| confusion_matrix += __fast_hist(lt.flatten(), lp.flatten()) | |
| return confusion_matrix | |
| def get_mIoU(num_classes, label_gts, label_preds): | |
| confusion_matrix = get_confuse_matrix(num_classes, label_gts, label_preds) | |
| score_dict = cm2score(confusion_matrix) | |
| return score_dict['miou'] | |
| def fast_hist(a, b, n): | |
| k = (a >= 0) & (a < n) | |
| return np.bincount(n * a[k].astype(int) + b[k], minlength=n ** 2).reshape(n, n) | |
| def get_hist(image, label, num_class): | |
| hist = np.zeros((num_class, num_class)) | |
| hist += fast_hist(image.flatten(), label.flatten(), num_class) | |
| return hist | |
| def cal_kappa(hist): | |
| if hist.sum() == 0: | |
| po = 0 | |
| pe = 1 | |
| kappa = 0 | |
| else: | |
| po = np.diag(hist).sum() / hist.sum() | |
| pe = np.matmul(hist.sum(1), hist.sum(0).T) / hist.sum() ** 2 | |
| if pe == 1: | |
| kappa = 0 | |
| else: | |
| kappa = (po - pe) / (1 - pe) | |
| return kappa | |
| def SCDD_eval_all(preds, labels, num_class): | |
| hist = np.zeros((num_class, num_class)) | |
| for pred, label in zip(preds, labels): | |
| infer_array = np.array(pred) | |
| unique_set = set(np.unique(infer_array)) | |
| assert unique_set.issubset(set([0, 1, 2, 3, 4, 5, 6])), "unrecognized label number" | |
| label_array = np.array(label) | |
| assert infer_array.shape == label_array.shape, "The size of prediction and target must be the same" | |
| hist += get_hist(infer_array, label_array, num_class) | |
| hist_fg = hist[1:, 1:] | |
| c2hist = np.zeros((2, 2)) | |
| c2hist[0][0] = hist[0][0] | |
| c2hist[0][1] = hist.sum(1)[0] - hist[0][0] | |
| c2hist[1][0] = hist.sum(0)[0] - hist[0][0] | |
| c2hist[1][1] = hist_fg.sum() | |
| hist_n0 = hist.copy() | |
| hist_n0[0][0] = 0 | |
| kappa_n0 = cal_kappa(hist_n0) | |
| iu = np.diag(c2hist) / (c2hist.sum(1) + c2hist.sum(0) - np.diag(c2hist)) | |
| IoU_fg = iu[1] | |
| IoU_mean = (iu[0] + iu[1]) / 2 | |
| Sek = (kappa_n0 * math.exp(IoU_fg)) / math.e | |
| pixel_sum = hist.sum() | |
| change_pred_sum = pixel_sum - hist.sum(1)[0].sum() | |
| change_label_sum = pixel_sum - hist.sum(0)[0].sum() | |
| SC_TP = np.diag(hist[1:, 1:]).sum() | |
| SC_Precision = SC_TP / change_pred_sum | |
| SC_Recall = SC_TP / change_label_sum | |
| Fscd = stats.hmean([SC_Precision, SC_Recall]) | |
| return Fscd, IoU_mean, Sek |