SentinelWatch / utils /metrics.py
VishaliniS456's picture
Upload 8 files
9875bf8 verified
"""Advanced comparison metrics for change detection evaluation."""
import numpy as np
from typing import Dict, Optional
from .evaluation import calculate_metrics
def calculate_change_statistics(
change_mask: np.ndarray,
change_confidence: np.ndarray
) -> Dict:
"""
Calculate statistics from a 2D change mask and confidence map.
Args:
change_mask: Binary 2D array (H, W), 1 = changed
change_confidence: Float 2D array (H, W) in [0, 1]
Returns:
Dict with total_pixels, changed_pixels, unchanged_pixels,
change_percentage, mean_confidence, min_confidence, max_confidence,
change_confidence_mean (mean conf among changed pixels only)
"""
# Ensure 2D arrays
mask = change_mask.astype(np.uint8)
conf = change_confidence.astype(np.float32)
total_pixels = int(mask.size)
changed_pixels = int(np.sum(mask == 1))
unchanged_pixels = total_pixels - changed_pixels
change_percentage = 100.0 * changed_pixels / total_pixels if total_pixels > 0 else 0.0
mean_confidence = float(conf.mean())
min_confidence = float(conf.min())
max_confidence = float(conf.max())
# Mean confidence among changed pixels only
if changed_pixels > 0:
change_confidence_mean = float(conf[mask == 1].mean())
else:
change_confidence_mean = 0.0
return {
"total_pixels": total_pixels,
"changed_pixels": changed_pixels,
"unchanged_pixels": unchanged_pixels,
"change_percentage": change_percentage,
"mean_confidence": mean_confidence,
"min_confidence": min_confidence,
"max_confidence": max_confidence,
"change_confidence_mean": change_confidence_mean,
}
def compare_with_without_masking(
pred_with_mask: np.ndarray,
pred_without_mask: np.ndarray,
gt_mask: Optional[np.ndarray] = None
) -> Dict:
"""
Compare detection results with and without cloud masking.
Args:
pred_with_mask: Change mask produced WITH cloud masking (H, W)
pred_without_mask: Change mask produced WITHOUT cloud masking (H, W)
gt_mask: Optional ground truth mask for metric computation (H, W)
Returns:
Dict with pixel-level comparison and optional metric differences
"""
agreement = int(np.sum(pred_with_mask == pred_without_mask))
total = int(pred_with_mask.size)
agreement_pct = 100.0 * agreement / total if total > 0 else 0.0
result = {
"agreement_pixels": agreement,
"total_pixels": total,
"agreement_percentage": agreement_pct,
"changed_with_mask": int(np.sum(pred_with_mask)),
"changed_without_mask": int(np.sum(pred_without_mask)),
}
if gt_mask is not None:
metrics_with = calculate_metrics(pred_with_mask, gt_mask)
metrics_without = calculate_metrics(pred_without_mask, gt_mask)
result["iou_with_mask"] = metrics_with["iou"]
result["iou_without_mask"] = metrics_without["iou"]
result["iou_improvement"] = metrics_with["iou"] - metrics_without["iou"]
result["f1_with_mask"] = metrics_with["f1"]
result["f1_without_mask"] = metrics_without["f1"]
result["f1_improvement"] = metrics_with["f1"] - metrics_without["f1"]
return result