| """Evaluate semantic predictions and render a compact comparison image.""" |
|
|
| import json |
| from pathlib import Path |
|
|
| import matplotlib.pyplot as plt |
| import numpy as np |
| import yaml |
|
|
|
|
| ROOT = Path(__file__).resolve().parents[1] |
|
|
|
|
| def main(): |
| with (ROOT / "conf" / "config.yaml").open(encoding="utf-8") as handle: |
| config = yaml.safe_load(handle) |
| input_dir = ROOT / config["paths"]["inference_dir"] |
| required = [input_dir / "predictions.npy", input_dir / "targets.npy"] |
| missing = [str(path.relative_to(ROOT)) for path in required if not path.exists()] |
| if missing: |
| raise FileNotFoundError( |
| f"Missing inference outputs: {missing}. Run `python scripts/inference.py` first." |
| ) |
| predictions = np.load(input_dir / "predictions.npy") |
| targets = np.load(input_dir / "targets.npy") |
| classes = config["data"]["num_classes"] |
| intersections = np.zeros(classes, dtype=np.float64) |
| unions = np.zeros(classes, dtype=np.float64) |
| for class_id in range(classes): |
| predicted = predictions == class_id |
| expected = targets == class_id |
| intersections[class_id] = np.logical_and(predicted, expected).sum() |
| unions[class_id] = np.logical_or(predicted, expected).sum() |
| per_class_iou = np.divide(intersections, unions, out=np.zeros_like(intersections), where=unions > 0) |
| metadata_path = input_dir / "metadata.npz" |
| metadata = np.load(metadata_path) if metadata_path.exists() else None |
| metrics = { |
| "pixel_accuracy": float((predictions == targets).mean()), |
| "mean_iou": float(per_class_iou.mean()), |
| "per_class_iou": per_class_iou.tolist(), |
| "samples": int(len(predictions)), |
| "data_source": str(metadata["data_source"]) if metadata is not None else "unknown", |
| "protocol": str(metadata["protocol"]) if metadata is not None else "unknown", |
| } |
| output_dir = ROOT / config["paths"]["evaluation_dir"] |
| output_dir.mkdir(parents=True, exist_ok=True) |
| with (output_dir / "metrics.json").open("w", encoding="utf-8") as handle: |
| json.dump(metrics, handle, indent=2) |
| figure, axes = plt.subplots(2, 2, figsize=(7, 7)) |
| for index, axis in enumerate(axes.flat): |
| sample = index // 2 |
| image = targets[sample] if index % 2 == 0 else predictions[sample] |
| axis.imshow(image, vmin=0, vmax=classes - 1, cmap="terrain") |
| axis.set_title(("Target" if index % 2 == 0 else "Prediction") + f" {sample}") |
| axis.axis("off") |
| figure.tight_layout() |
| figure.savefig(output_dir / "comparison.png", dpi=120) |
| plt.close(figure) |
| print(f"pixel_accuracy={metrics['pixel_accuracy']:.6f} mean_iou={metrics['mean_iou']:.6f}") |
| print(f"evaluation={output_dir.relative_to(ROOT)}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|