#!/usr/bin/env bash # SUM Parts - map the per-class IoU array onto class names # # The log prints a bare numpy array. Guessing whether it starts at class 0 or # class 1 by eye is how you end up reporting "car IoU 95.6". Count it. set -uo pipefail LOG="${1:-$HOME/sum-parts/runs/val_ab/val_capped.log}" source "$HOME/miniconda3/etc/profile.d/conda.sh" conda activate sumparts python - "$LOG" <<'PY' import re import sys from pathlib import Path CLASSES = ['unclassified', 'terrain', 'high_vegetation', 'facade_surface', 'water', 'car', 'boat', 'roof_surface', 'chimney', 'dormer', 'balcony', 'roof_installation', 'wall'] text = Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace") m = re.findall(r"iou per cls is:\s*\[([^\]]*)\]", text) if not m: print("no 'iou per cls' line found in", sys.argv[1]) raise SystemExit(1) vals = [float(x) for x in m[-1].split()] print(f"file : {Path(sys.argv[1]).name}") print(f"entries: {len(vals)} (num_classes = {len(CLASSES)})") print() if len(vals) == len(CLASSES): names = CLASSES note = "array covers classes 0..12" elif len(vals) == len(CLASSES) - 1: names = CLASSES[:-1] note = ("array is one short of num_classes. ConfusionMatrix remaps the " "ignore_index into slot num_classes-1, so the last class shares a " "bucket with ignored points and is dropped from the report.") else: names = [f"class_{i}" for i in range(len(vals))] note = "unexpected length -- names are positional only" print(f"note : {note}\n") print(f" {'#':>2} {'class':<20} {'IoU':>7}") for i, (n, v) in enumerate(zip(names, vals)): mark = " <-- 0" if v == 0 else "" print(f" {i:>2} {n:<20} {v:>7.2f}{mark}") nz = [v for v in vals if v > 0] print() print(f" non-zero classes : {len(nz)}/{len(vals)}") print(f" mean over all : {sum(vals)/len(vals):.2f}") PY