Files
appRobotRender/benchmark/camera_count/analyze.py
2026-06-02 23:35:45 +02:00

119 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""
benchmark/camera_count/analyze.py
===================================
Liest camera_study.json und erstellt:
- Konsolentabelle: k → Mittelwert / Median / Std / Min / Max des Fehlers
- Beste und schlechteste Kamera-Subsets je k
- Boxplot: Anzahl Kameras vs. Gelenkwinkelfehler (PNG)
Aufruf:
python benchmark/camera_count/analyze.py
python benchmark/camera_count/analyze.py --input benchmark/camera_count/results/camera_study.json
python benchmark/camera_count/analyze.py --metric max_abs_deg
"""
from __future__ import annotations
import argparse
import json
import statistics
from pathlib import Path
RESULTS_DIR = Path(__file__).resolve().parent / "results"
def load(path: str) -> list[dict]:
data = json.loads(Path(path).read_text(encoding="utf-8"))
if not data:
print("[ERROR] Keine Ergebnisse in der Datei.")
return data
def group_by_k(data: list[dict], metric: str) -> dict[int, list[float]]:
by_k: dict[int, list[float]] = {}
for row in data:
v = row.get(metric)
if v is None:
continue
by_k.setdefault(row["k"], []).append(v)
return by_k
def print_table(by_k: dict[int, list[float]], metric: str) -> None:
print(f"\nKamera-Anzahl vs. {metric}")
print(f"{'k':>4} | {'n':>5} | {'Mittel':>8} | {'Median':>8} | "
f"{'Std':>8} | {'Min':>8} | {'Max':>8}")
print("-" * 65)
for k in sorted(by_k):
vals = by_k[k]
med = statistics.median(vals)
std = statistics.pstdev(vals)
print(f"{k:>4} | {len(vals):>5} | "
f"{statistics.mean(vals):8.3f} | {med:8.3f} | "
f"{std:8.3f} | {min(vals):8.3f} | {max(vals):8.3f}")
def print_best_worst(data: list[dict], metric: str) -> None:
ks = sorted({r["k"] for r in data})
print(f"\nBeste / schlechteste Subsets je k ({metric}):")
for k in ks:
rows_k = [r for r in data if r["k"] == k and r.get(metric) is not None]
if not rows_k:
continue
best = min(rows_k, key=lambda r: r[metric])
worst = max(rows_k, key=lambda r: r[metric])
print(f" k={k}: best [{best['scene']}] {best['subset']} "
f"({best[metric]:.3f}) "
f"worst [{worst['scene']}] {worst['subset']} ({worst[metric]:.3f})")
def plot(by_k: dict[int, list[float]], metric: str, out_path: str) -> None:
try:
import matplotlib.pyplot as plt
except ImportError:
print("\n[INFO] matplotlib nicht installiert — Plot übersprungen.")
return
ks = sorted(by_k)
data_plot = [by_k[k] for k in ks]
fig, ax = plt.subplots(figsize=(8, 5))
ax.boxplot(data_plot, labels=[str(k) for k in ks], patch_artist=True)
ax.set_xlabel("Anzahl Kameras")
ax.set_ylabel(f"{metric}")
ax.set_title("Anzahl Kameras vs. Pose-Schätzfehler")
ax.grid(True, axis="y", alpha=0.35)
fig.tight_layout()
fig.savefig(out_path, dpi=150)
print(f"\n[INFO] Plot gespeichert: {out_path}")
def main() -> None:
ap = argparse.ArgumentParser(description="Auswertung der Kamera-Anzahl-Studie")
ap.add_argument("--input", default=str(RESULTS_DIR / "camera_study.json"))
ap.add_argument("--metric",
choices=["mean_abs_deg", "max_abs_deg", "mean_abs_mm", "max_abs_mm",
"finger_error_mm"],
default="finger_error_mm",
help="Metrik für Tabelle und Plot (Standard: finger_error_mm)")
ap.add_argument("--out-plot",
default=str(RESULTS_DIR / "camera_count_vs_error.png"))
args = ap.parse_args()
data = load(args.input)
if not data:
return
by_k = group_by_k(data, args.metric)
if not by_k:
print(f"[ERROR] Keine Werte für Metrik '{args.metric}' gefunden.")
return
print_table(by_k, args.metric)
print_best_worst(data, args.metric)
plot(by_k, args.metric, args.out_plot)
if __name__ == "__main__":
main()