RustCoSeg / evaluator.py
HirraA's picture
Upload 30 files
168ec29 verified
import os
import time
import json
import numpy as np
from scipy.io import savemat
import torch
from torchvision import transforms
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
class Eval_thread():
def __init__(self, loader, method='', dataset='', output_dir='', epoch='', cuda=True):
self.loader = loader
self.method = method
self.dataset = dataset
self.cuda = cuda
self.output_dir = output_dir
self.epoch = epoch.split('ep')[-1]
self.logfile = os.path.join(output_dir, 'result.txt')
self.dataset2smeasure_bottom_bound = {'CoCA': 0.673, 'CoSOD3k': 0.802, 'CoSal2015': 0.845} # S_measures of GCoNet
def run(self, AP=False, AUC=False, save_metrics=False, continue_eval=True):
Res = {}
start_time = time.time()
if continue_eval:
s = self.Eval_Smeasure()
if s > self.dataset2smeasure_bottom_bound[self.dataset]:
mae = self.Eval_mae()
Em = self.Eval_Emeasure()
max_e = Em.max().item()
mean_e = Em.mean().item()
Em = Em.cpu().numpy()
Fm, prec, recall = self.Eval_fmeasure()
max_f = Fm.max().item()
mean_f = Fm.mean().item()
Fm = Fm.cpu().numpy()
else:
mae = 1
Em = torch.zeros(255).cpu().numpy()
max_e = 0
mean_e = 0
Fm, prec, recall = 0, 0, 0
max_f = 0
mean_f = 0
continue_eval = False
else:
s = 0
mae = 1
Em = torch.zeros(255).cpu().numpy()
max_e = 0
mean_e = 0
Fm, prec, recall = 0, 0, 0
max_f = 0
mean_f = 0
continue_eval = False
if AP:
prec = prec.cpu().numpy()
recall = recall.cpu().numpy()
avg_p = self.Eval_AP(prec, recall)
if AUC:
auc, TPR, FPR = self.Eval_auc()
TPR = TPR.cpu().numpy()
FPR = FPR.cpu().numpy()
if save_metrics:
os.makedirs(os.path.join(self.output_dir, self.method, self.epoch), exist_ok=True)
Res['Sm'] = s
if s > self.dataset2smeasure_bottom_bound[self.dataset]:
Res['MAE'] = mae
Res['MaxEm'] = max_e
Res['MeanEm'] = mean_e
Res['Em'] = Em
Res['Fm'] = Fm
else:
Res['MAE'] = 1
Res['MaxEm'] = 0
Res['MeanEm'] = 0
Res['Em'] = torch.zeros(255).cpu().numpy()
Res['Fm'] = 0
if AP:
Res['MaxFm'] = max_f
Res['MeanFm'] = mean_f
Res['AP'] = avg_p
Res['Prec'] = prec
Res['Recall'] = recall
if AUC:
Res['AUC'] = auc
Res['TPR'] = TPR
Res['FPR'] = FPR
os.makedirs(os.path.join(self.output_dir, self.method, self.epoch), exist_ok=True)
savemat(os.path.join(self.output_dir, self.method, self.epoch, self.dataset + '.mat'), Res)
info = '{} ({}): {:.4f} max-Emeasure || {:.4f} S-measure || {:.4f} max-fm || {:.4f} mae || {:.4f} mean-Emeasure || {:.4f} mean-fm'.format(
self.dataset, self.method+'-ep{}'.format(self.epoch), max_e, s, max_f, mae, mean_e, mean_f
)
if AP:
info += ' || {:.4f} AP'.format(avg_p)
if AUC:
info += ' || {:.4f} AUC'.format(auc)
info += '.'
self.LOG(info + '\n')
return '[cost:{:.4f}s] '.format(time.time() - start_time) + info, continue_eval
def Eval_mae(self):
if self.epoch:
print('Evaluating MAE...')
avg_mae, img_num = 0.0, 0.0
with torch.no_grad():
trans = transforms.Compose([transforms.ToTensor()])
for pred, gt in self.loader:
if self.cuda:
pred = trans(pred).cuda()
gt = trans(gt).cuda()
else:
pred = trans(pred)
gt = trans(gt)
mea = torch.abs(pred - gt).mean()
if mea == mea: # for Nan
avg_mae += mea
img_num += 1.0
avg_mae /= img_num
return avg_mae.item()
def Eval_fmeasure(self):
print('Evaluating FMeasure...')
beta2 = 0.3
avg_f, avg_p, avg_r, img_num = 0.0, 0.0, 0.0, 0.0
with torch.no_grad():
trans = transforms.Compose([transforms.ToTensor()])
for pred, gt in self.loader:
if self.cuda:
pred = trans(pred).cuda()
gt = trans(gt).cuda()
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
else:
pred = trans(pred)
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt)
prec, recall = self._eval_pr(pred, gt, 255)
f_score = (1 + beta2) * prec * recall / (beta2 * prec + recall)
f_score[f_score != f_score] = 0 # for Nan
avg_f += f_score
avg_p += prec
avg_r += recall
img_num += 1.0
Fm = avg_f / img_num
avg_p = avg_p / img_num
avg_r = avg_r / img_num
return Fm, avg_p, avg_r
def Eval_auc(self):
print('Evaluating AUC...')
avg_tpr, avg_fpr, avg_auc, img_num = 0.0, 0.0, 0.0, 0.0
with torch.no_grad():
trans = transforms.Compose([transforms.ToTensor()])
for pred, gt in self.loader:
if self.cuda:
pred = trans(pred).cuda()
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt).cuda()
else:
pred = trans(pred)
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt)
TPR, FPR = self._eval_roc(pred, gt, 255)
avg_tpr += TPR
avg_fpr += FPR
img_num += 1.0
avg_tpr = avg_tpr / img_num
avg_fpr = avg_fpr / img_num
sorted_idxes = torch.argsort(avg_fpr)
avg_tpr = avg_tpr[sorted_idxes]
avg_fpr = avg_fpr[sorted_idxes]
avg_auc = torch.trapz(avg_tpr, avg_fpr)
return avg_auc.item(), avg_tpr, avg_fpr
def Eval_Emeasure(self):
print('Evaluating EMeasure...')
avg_e, img_num = 0.0, 0.0
with torch.no_grad():
trans = transforms.Compose([transforms.ToTensor()])
Em = torch.zeros(255)
if self.cuda:
Em = Em.cuda()
for pred, gt in self.loader:
if self.cuda:
pred = trans(pred).cuda()
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt).cuda()
else:
pred = trans(pred)
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt)
Em += self._eval_e(pred, gt, 255)
img_num += 1.0
Em /= img_num
return Em
def select_by_Smeasure(self, bar=0.9, loader_comp=None, bar_comp=0.1):
print('Evaluating SMeasure...')
good_ones = []
good_ones_comp = []
good_ones_gt = []
alpha, avg_q, img_num = 0.5, 0.0, 0.0
with torch.no_grad():
trans = transforms.Compose([transforms.ToTensor()])
for (pred, gt, predpath, gtpath), (pred_comp, gt_comp, predpath_comp) in zip(self.loader, loader_comp):
# pred X gt
if self.cuda:
pred = trans(pred).cuda()
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt).cuda()
else:
pred = trans(pred)
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt)
y = gt.mean()
if y == 0:
x = pred.mean()
Q = 1.0 - x
elif y == 1:
x = pred.mean()
Q = x
else:
gt[gt >= 0.5] = 1
gt[gt < 0.5] = 0
Q = alpha * self._S_object(
pred, gt) + (1 - alpha) * self._S_region(pred, gt)
if Q.item() < 0:
Q = torch.FloatTensor([0.0])
img_num += 1.0
avg_q += Q.item()
# pred_comp X gt
if self.cuda:
pred_comp = trans(pred_comp).cuda()
pred_comp = (pred_comp - torch.min(pred_comp)) / (torch.max(pred_comp) -
torch.min(pred_comp) + 1e-20)
gt_comp = trans(gt_comp).cuda()
else:
pred_comp = trans(pred_comp)
pred_comp = (pred_comp - torch.min(pred_comp)) / (torch.max(pred_comp) -
torch.min(pred_comp) + 1e-20)
gt_comp = trans(gt_comp)
y = gt_comp.mean()
if y == 0:
x = pred_comp.mean()
Q_comp = 1.0 - x
elif y == 1:
x = pred_comp.mean()
Q_comp = x
else:
gt_comp[gt_comp >= 0.5] = 1
gt_comp[gt_comp < 0.5] = 0
Q_comp = alpha * self._S_object(
pred_comp, gt_comp) + (1 - alpha) * self._S_region(pred_comp, gt_comp)
if Q_comp.item() < 0:
Q_comp = torch.FloatTensor([0.0])
if Q.item() > bar and (Q.item() - Q_comp.item()) > bar_comp:
good_ones.append(predpath)
good_ones_comp.append(predpath_comp)
good_ones_gt.append(gtpath)
avg_q /= img_num
return avg_q, good_ones, good_ones_comp, good_ones_gt
def Eval_Smeasure(self):
print('Evaluating SMeasure...')
alpha, avg_q, img_num = 0.5, 0.0, 0.0
with torch.no_grad():
trans = transforms.Compose([transforms.ToTensor()])
for pred, gt in self.loader:
if self.cuda:
pred = trans(pred).cuda()
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt).cuda()
else:
pred = trans(pred)
pred = (pred - torch.min(pred)) / (torch.max(pred) -
torch.min(pred) + 1e-20)
gt = trans(gt)
y = gt.mean()
if y == 0:
x = pred.mean()
Q = 1.0 - x
elif y == 1:
x = pred.mean()
Q = x
else:
gt[gt >= 0.5] = 1
gt[gt < 0.5] = 0
Q = alpha * self._S_object(
pred, gt) + (1 - alpha) * self._S_region(pred, gt)
if Q.item() < 0:
Q = torch.FloatTensor([0.0])
img_num += 1.0
avg_q += Q.item()
avg_q /= img_num
return avg_q
def LOG(self, output):
os.makedirs(self.output_dir, exist_ok=True)
with open(self.logfile, 'a') as f:
f.write(output)
def _eval_e(self, y_pred, y, num):
if self.cuda:
score = torch.zeros(num).cuda()
thlist = torch.linspace(0, 1 - 1e-10, num).cuda()
else:
score = torch.zeros(num)
thlist = torch.linspace(0, 1 - 1e-10, num)
for i in range(num):
y_pred_th = (y_pred >= thlist[i]).float()
fm = y_pred_th - y_pred_th.mean()
gt = y - y.mean()
align_matrix = 2 * gt * fm / (gt * gt + fm * fm + 1e-20)
enhanced = ((align_matrix + 1) * (align_matrix + 1)) / 4
score[i] = torch.sum(enhanced) / (y.numel() - 1 + 1e-20)
return score
def _eval_pr(self, y_pred, y, num):
if self.cuda:
prec, recall = torch.zeros(num).cuda(), torch.zeros(num).cuda()
thlist = torch.linspace(0, 1 - 1e-10, num).cuda()
else:
prec, recall = torch.zeros(num), torch.zeros(num)
thlist = torch.linspace(0, 1 - 1e-10, num)
for i in range(num):
y_temp = (y_pred >= thlist[i]).float()
tp = (y_temp * y).sum()
prec[i], recall[i] = tp / (y_temp.sum() + 1e-20), tp / (y.sum() + 1e-20)
return prec, recall
def _eval_roc(self, y_pred, y, num):
if self.cuda:
TPR, FPR = torch.zeros(num).cuda(), torch.zeros(num).cuda()
thlist = torch.linspace(0, 1 - 1e-10, num).cuda()
else:
TPR, FPR = torch.zeros(num), torch.zeros(num)
thlist = torch.linspace(0, 1 - 1e-10, num)
for i in range(num):
y_temp = (y_pred >= thlist[i]).float()
tp = (y_temp * y).sum()
fp = (y_temp * (1 - y)).sum()
tn = ((1 - y_temp) * (1 - y)).sum()
fn = ((1 - y_temp) * y).sum()
TPR[i] = tp / (tp + fn + 1e-20)
FPR[i] = fp / (fp + tn + 1e-20)
return TPR, FPR
def _S_object(self, pred, gt):
fg = torch.where(gt == 0, torch.zeros_like(pred), pred)
bg = torch.where(gt == 1, torch.zeros_like(pred), 1 - pred)
o_fg = self._object(fg, gt)
o_bg = self._object(bg, 1 - gt)
u = gt.mean()
Q = u * o_fg + (1 - u) * o_bg
return Q
def _object(self, pred, gt):
temp = pred[gt == 1]
x = temp.mean()
sigma_x = temp.std()
score = 2.0 * x / (x * x + 1.0 + sigma_x + 1e-20)
return score
def _S_region(self, pred, gt):
X, Y = self._centroid(gt)
gt1, gt2, gt3, gt4, w1, w2, w3, w4 = self._divideGT(gt, X, Y)
p1, p2, p3, p4 = self._dividePrediction(pred, X, Y)
Q1 = self._ssim(p1, gt1)
Q2 = self._ssim(p2, gt2)
Q3 = self._ssim(p3, gt3)
Q4 = self._ssim(p4, gt4)
Q = w1 * Q1 + w2 * Q2 + w3 * Q3 + w4 * Q4
return Q
def _centroid(self, gt):
rows, cols = gt.size()[-2:]
gt = gt.view(rows, cols)
if gt.sum() == 0:
if self.cuda:
X = torch.eye(1).cuda() * round(cols / 2)
Y = torch.eye(1).cuda() * round(rows / 2)
else:
X = torch.eye(1) * round(cols / 2)
Y = torch.eye(1) * round(rows / 2)
else:
total = gt.sum()
if self.cuda:
i = torch.from_numpy(np.arange(0, cols)).cuda().float()
j = torch.from_numpy(np.arange(0, rows)).cuda().float()
else:
i = torch.from_numpy(np.arange(0, cols)).float()
j = torch.from_numpy(np.arange(0, rows)).float()
X = torch.round((gt.sum(dim=0) * i).sum() / total + 1e-20)
Y = torch.round((gt.sum(dim=1) * j).sum() / total + 1e-20)
return X.long(), Y.long()
def _divideGT(self, gt, X, Y):
h, w = gt.size()[-2:]
area = h * w
gt = gt.view(h, w)
LT = gt[:Y, :X]
RT = gt[:Y, X:w]
LB = gt[Y:h, :X]
RB = gt[Y:h, X:w]
X = X.float()
Y = Y.float()
w1 = X * Y / area
w2 = (w - X) * Y / area
w3 = X * (h - Y) / area
w4 = 1 - w1 - w2 - w3
return LT, RT, LB, RB, w1, w2, w3, w4
def _dividePrediction(self, pred, X, Y):
h, w = pred.size()[-2:]
pred = pred.view(h, w)
LT = pred[:Y, :X]
RT = pred[:Y, X:w]
LB = pred[Y:h, :X]
RB = pred[Y:h, X:w]
return LT, RT, LB, RB
def _ssim(self, pred, gt):
gt = gt.float()
h, w = pred.size()[-2:]
N = h * w
x = pred.mean()
y = gt.mean()
sigma_x2 = ((pred - x) * (pred - x)).sum() / (N - 1 + 1e-20)
sigma_y2 = ((gt - y) * (gt - y)).sum() / (N - 1 + 1e-20)
sigma_xy = ((pred - x) * (gt - y)).sum() / (N - 1 + 1e-20)
aplha = 4 * x * y * sigma_xy
beta = (x * x + y * y) * (sigma_x2 + sigma_y2)
if aplha != 0:
Q = aplha / (beta + 1e-20)
elif aplha == 0 and beta == 0:
Q = 1.0
else:
Q = 0
return Q
def Eval_AP(self, prec, recall):
# Ref:
# https://github.com/facebookresearch/Detectron/blob/05d04d3a024f0991339de45872d02f2f50669b3d/lib/datasets/voc_eval.py#L54
print('Evaluating AP...')
ap_r = np.concatenate(([0.], recall, [1.]))
ap_p = np.concatenate(([0.], prec, [0.]))
sorted_idxes = np.argsort(ap_r)
ap_r = ap_r[sorted_idxes]
ap_p = ap_p[sorted_idxes]
count = ap_r.shape[0]
for i in range(count - 1, 0, -1):
ap_p[i - 1] = max(ap_p[i], ap_p[i - 1])
i = np.where(ap_r[1:] != ap_r[:-1])[0]
ap = np.sum((ap_r[i + 1] - ap_r[i]) * ap_p[i + 1])
return ap