File size: 1,807 Bytes
a92ee50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""Metrics and reports for paired height arrays."""
import numpy as np

from .calibration import ScaleShift, fit_scale_shift


def evaluate(prediction: np.ndarray, ground_truth: np.ndarray, valid: np.ndarray) -> tuple[ScaleShift, np.ndarray, dict]:
    if prediction.shape != ground_truth.shape or valid.shape != ground_truth.shape:
        raise ValueError("Prediction, ground truth, and validity mask must have matching shapes")
    mask = valid & np.isfinite(prediction) & np.isfinite(ground_truth)
    calibration = fit_scale_shift(prediction, ground_truth, mask)
    aligned = calibration.apply(prediction)
    errors = aligned[mask].astype(np.float64) - ground_truth[mask].astype(np.float64)
    corr = float(np.corrcoef(aligned[mask], ground_truth[mask])[0, 1])
    ss_res = float(np.sum(errors ** 2))
    ss_tot = float(np.sum((ground_truth[mask] - ground_truth[mask].mean()) ** 2))
    metrics = {
        "method": "scale/shift aligned baseline",
        "valid_pixels": int(mask.sum()),
        "scale_coefficient": calibration.scale,
        "shift_coefficient_m": calibration.shift,
        "mae_m": float(np.mean(np.abs(errors))),
        "rmse_m": float(np.sqrt(np.mean(errors ** 2))),
        "pearson_r": corr,
        "r_squared": float(1 - ss_res / ss_tot) if ss_tot > 0 else None,
        "prediction_raw_min": float(np.min(prediction[mask])),
        "prediction_raw_max": float(np.max(prediction[mask])),
        "prediction_raw_mean": float(np.mean(prediction[mask])),
        "ground_truth_min_m": float(np.min(ground_truth[mask])),
        "ground_truth_max_m": float(np.max(ground_truth[mask])),
        "ground_truth_mean_m": float(np.mean(ground_truth[mask])),
        "ground_truth_median_m": float(np.median(ground_truth[mask])),
    }
    return calibration, aligned, metrics