Skip to content

Instantly share code, notes, and snippets.

@egparedes
Last active February 14, 2022 03:42
Show Gist options
  • Save egparedes/b3aa721381a889e3be590711f979688d to your computer and use it in GitHub Desktop.
Save egparedes/b3aa721381a889e3be590711f979688d to your computer and use it in GitHub Desktop.
Python implementation of the Weighted F-beta measure (as proposed in "How to Evaluate Foreground Maps?" [Margolin et al. - CVPR'14])
import numpy as np
import scipy
import scipy.ndimage
def weighted_f_beta_score(candidate, gt, beta=1.0):
"""
Compute the Weighted F-beta measure (as proposed in "How to Evaluate Foreground Maps?" [Margolin et al. - CVPR'14])
Original MATLAB source code from:
[https://cgm.technion.ac.il/Computer-Graphics-Multimedia/Software/FGEval/resources/WFb.m]
:param candidate: FG - Binary/Non binary candidate map with values in the range [0-1]
:param gt: Binary ground truth map
:param beta: attach 'beta' times as much importance to Recall as to Precision (default=1)
:result: the Weighted F-beta score
"""
if np.min(candidate) < 0.0 or np.max(candidate) > 1.0:
raise ValueError("'candidate' values must be inside range [0 - 1]")
if gt.dtype in [np.bool, np.bool_, np.bool8]:
gt_mask = gt
not_gt_mask = np.logical_not(gt_mask)
gt = np.array(gt, dtype=candidate.dtype)
else:
if not np.all(np.isclose(gt, 0) | np.isclose(gt, 1)):
raise ValueError("'gt' must be a 0/1 or boolean array")
gt_mask = np.isclose(gt, 1)
not_gt_mask = np.logical_not(gt_mask)
gt = np.asarray(gt, dtype=candidate.dtype)
E = np.abs(candidate - gt)
dist, idx = scipy.ndimage.morphology.distance_transform_edt(not_gt_mask, return_indices=True)
# Pixel dependency
Et = np.array(E)
# To deal correctly with the edges of the foreground region:
Et[not_gt_mask] = E[idx[0, not_gt_mask], idx[1, not_gt_mask]]
sigma = 5.0
EA = scipy.ndimage.gaussian_filter(Et, sigma=sigma, truncate=3 / sigma,
mode='constant', cval=0.0)
min_E_EA = np.minimum(E, EA, where=gt_mask, out=np.array(E))
# Pixel importance
B = np.ones(gt.shape)
B[not_gt_mask] = 2 - np.exp(np.log(1 - 0.5) / 5 * dist[not_gt_mask])
Ew = min_E_EA * B
# Final metric computation
eps = np.spacing(1)
TPw = np.sum(gt) - np.sum(Ew[gt_mask])
FPw = np.sum(Ew[not_gt_mask])
R = 1 - np.mean(Ew[gt_mask]) # Weighed Recall
P = TPw / (eps + TPw + FPw) # Weighted Precision
# Q = 2 * (R * P) / (eps + R + P) # Beta=1
Q = (1 + beta**2) * (R * P) / (eps + R + (beta * P))
return Q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment