|
|
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} |
|
|
|
|
|
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: |
|
|
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 |
|
|
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): |
|
|
|
|
|
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() |
|
|
|
|
|
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): |
|
|
|
|
|
|
|
|
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 |
|
|
|