From a2ca2fe876d50c2ec8081727ce0ca5934bce23b4 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Thu, 6 Nov 2025 20:43:06 -0300 Subject: [PATCH] [SaliencyEvaluationMetrics][Added] Conditional computation --- src/nodes/nodes_img.py | 99 ++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/src/nodes/nodes_img.py b/src/nodes/nodes_img.py index 0de3c1a..2fab813 100644 --- a/src/nodes/nodes_img.py +++ b/src/nodes/nodes_img.py @@ -770,16 +770,36 @@ class SaliencyEvaluationMetrics: "prediction": ("MASK",), "ground_truth": ("MASK",), }, + "optional": { + "mae_enable": ("BOOLEAN", { + "default": True, + "tooltip": "Compute the MAE"}), + "max_f_mes_enable": ("BOOLEAN", { + "default": True, + "tooltip": "Compute the Max_F-measure"}), + "s_mes_enable": ("BOOLEAN", { + "default": True, + "tooltip": "Compute the S-measure"}), + "e_mes_enable": ("BOOLEAN", { + "default": True, + "tooltip": "Compute the E-measure"}), + "wf_mes_enable": ("BOOLEAN", { + "default": True, + "tooltip": "Compute the Weighted F-measure"}), + }, } - RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT", "FLOAT", "FLOAT") - RETURN_NAMES = ("MAE", "Max_F-measure", "S-measure", "E-measure", "Weighted_F-measure") + RETURN_TYPES = ("DICT", "FLOAT", "FLOAT", "FLOAT", "FLOAT", "FLOAT") + RETURN_NAMES = ("all", "MAE", "Max_F-measure", "S-measure", "E-measure", "Weighted_F-measure") + OUTPUT_IS_LIST = (True, False, False, False, False, False) FUNCTION = "evaluate" CATEGORY = BASE_CATEGORY + "/" + "Analysis" UNIQUE_NAME = "SET_SaliencyEvaluationMetrics" DISPLAY_NAME = "Saliency Evaluation Metrics" - def evaluate(self, prediction: torch.Tensor, ground_truth: torch.Tensor): + def evaluate(self, prediction: torch.Tensor, ground_truth: torch.Tensor, mae_enable: bool = True, + max_f_mes_enable: bool = True, s_mes_enable: bool = True, e_mes_enable: bool = True, + wf_mes_enable: bool = True): # Ensure tensors are on the same device and float type device = get_default_comfy_device() inputs_are_copies = get_canonical_device(prediction.device) != device @@ -799,54 +819,69 @@ class SaliencyEvaluationMetrics: mae_total, f_measure_max_total, s_measure_total, e_measure_total, weighted_f_total = 0, 0, 0, 0, 0 eps = 1e-6 + all = [] for i in range(batch_size): pred_i = pred[i] gt_i = gt[i] + res = {} # 1. Mean Absolute Error (MAE) - mae = torch.mean(torch.abs(pred_i - gt_i)) - logger.debug(f"MAE: {mae}") - mae_total += mae + if mae_enable: + mae = torch.mean(torch.abs(pred_i - gt_i)) + logger.debug(f"MAE: {mae}") + mae_total += mae + res['mae'] = mae # --- Metrics requiring binary ground truth --- - gt_binary = (gt_i >= 0.5).float() + if max_f_mes_enable or s_mes_enable or e_mes_enable or wf_mes_enable: + gt_binary = (gt_i >= 0.5).float() # 2. Max F-measure - f_max = 0.0 - for threshold in torch.linspace(0, 1, 256, device=device): - pred_binary = (pred_i >= threshold).float() + if max_f_mes_enable: + f_max = 0.0 + for threshold in torch.linspace(0, 1, 256, device=device): + pred_binary = (pred_i >= threshold).float() - tp = (pred_binary * gt_binary).sum() + tp = (pred_binary * gt_binary).sum() - if tp == 0: - continue + if tp == 0: + continue - precision = tp / (pred_binary.sum() + eps) - recall = tp / (gt_binary.sum() + eps) + precision = tp / (pred_binary.sum() + eps) + recall = tp / (gt_binary.sum() + eps) - # Using beta^2 = 0.3 as is standard. - beta2 = 0.3 - f_beta = (1 + beta2) * precision * recall / (beta2 * precision + recall + eps) + # Using beta^2 = 0.3 as is standard. + beta2 = 0.3 + f_beta = (1 + beta2) * precision * recall / (beta2 * precision + recall + eps) - if f_beta > f_max: - f_max = f_beta - f_measure_max_total += f_max - logger.debug(f"F_max: {f_max}") + if f_beta > f_max: + f_max = f_beta + f_measure_max_total += f_max + logger.debug(f"F_max: {f_max}") + res['max_f_mes'] = f_max # 3. S-measure - s_measure = _get_s_measure(pred_i, gt_binary) - s_measure_total += s_measure - logger.debug(f"S: {s_measure}") + if s_mes_enable: + s_measure = _get_s_measure(pred_i, gt_binary) + s_measure_total += s_measure + logger.debug(f"S: {s_measure}") + res['s_mes'] = s_measure # 4. E-measure - e_measure = _get_e_measure(pred_i, gt_binary) - e_measure_total += e_measure - logger.debug(f"E: {e_measure}") + if e_mes_enable: + e_measure = _get_e_measure(pred_i, gt_binary) + e_measure_total += e_measure + logger.debug(f"E: {e_measure}") + res['e_mes'] = e_measure # 5. Weighted F-measure - wf = _get_weighted_f_measure(pred_i, gt_binary) - weighted_f_total += wf - logger.debug(f"wF: {wf}") + if wf_mes_enable: + wf = _get_weighted_f_measure(pred_i, gt_binary) + weighted_f_total += wf + logger.debug(f"wF: {wf}") + res['wf_mes'] = wf + + all.append(res) # --- Average metrics over the batch --- mae_avg = mae_total.item() / batch_size @@ -855,7 +890,7 @@ class SaliencyEvaluationMetrics: e_measure_avg = e_measure_total.item() / batch_size weighted_f_avg = weighted_f_total.item() / batch_size - return (mae_avg, f_measure_avg, s_measure_avg, e_measure_avg, weighted_f_avg) + return (all, mae_avg, f_measure_avg, s_measure_avg, e_measure_avg, weighted_f_avg) class CompositeFace: