import os, json, numpy as np from datetime import datetime, timezone OUTPUT_ROOT = "/root/autodl-tmp/SplatAtlas/outputs/error_responsibility" PROF_DIR = os.path.join(OUTPUT_ROOT, "_profiles") METHODS = ['vanilla_3dgs', 'analyticsplatting', 'erankgs', 'ges', 'lightgaussian', 'minisplatting', 'opti3dgs', 'pgsr', 'steepgs'] SCENES = ['bicycle','bonsai','counter','flowers','garden','kitchen','room','stump','treehill', 'auditorium','ballroom','barn','caterpillar','courtroom','lighthouse','museum','palace','playground','temple','train','truck', 'DrJohnson','Playroom', 'Chair','Drums','Ficus','Hotdog','Lego','Materials','Mic','Ship'] METRICS = ["psnr_full", "sh_net_effect", "sh_corruption_rate", "opacity_net_effect", "coverage", "coverage_error_fraction", "residual_error"] # 1. 抓取所有 9 个方法在 31 个场景上的中位数 (Cross-Scene Median) method_medians = {m: {} for m in METHODS} for m in METHODS: for metric in METRICS: vals = [] for s in SCENES: p = os.path.join(OUTPUT_ROOT, f"{m}_{s}", "counterfactual_summary.json") if os.path.exists(p): with open(p) as f: d = json.load(f) if d.get(metric) is not None: vals.append(d[metric]) method_medians[m][metric] = float(np.median(vals)) if vals else 0.0 # 2. 计算相对 IQR 统计量 (跨 9 种方法) def calc_iqr_stats(vals_list): if not vals_list: return None, None, None med = float(np.median(vals_list)) abs_iqr = float(np.percentile(vals_list, 75) - np.percentile(vals_list, 25)) rel_iqr = (abs_iqr / abs(med) * 100) if med != 0 else None return med, abs_iqr, rel_iqr cross_method_analysis = {"n_methods": len(METHODS), "methods_included": METHODS, "metrics": {}} # 提取 PSNR 基础倍率 psnr_vals = [method_medians[m]["psnr_full"] for m in METHODS] _, _, psnr_rel_iqr = calc_iqr_stats(psnr_vals) for metric in METRICS: vals = [method_medians[m][metric] for m in METHODS] med, abs_iqr, rel_iqr = calc_iqr_stats(vals) ratio_rel = (rel_iqr / psnr_rel_iqr) if (rel_iqr is not None and psnr_rel_iqr) else None cross_method_analysis["metrics"][metric] = { "median_of_9_methods": med, "relative_iqr_pct": rel_iqr, "ratio_vs_psnr_relative": ratio_rel } # 3. 输出报告 md_lines = [ "# Phase 2 Part 3b — Final Heterogeneous Array Findings", f"\n**Scope**: {len(METHODS)} methods (Vanilla + 8 heterogeneous), {len(SCENES)} scenes.", "\n## Ultimate Discriminative Power (Relative IQR)", f"> **Baseline**: PSNR Relative IQR across 9 methods = **{psnr_rel_iqr:.2f}%**", "\n| Metric | Relative IQR (%) | Ratio vs PSNR (Discriminative Multiplier) |", "|---|---|---|" ] for metric in METRICS: if metric == "psnr_full": continue d = cross_method_analysis["metrics"][metric] rel_str = f"{d['relative_iqr_pct']:.2f}%" if d['relative_iqr_pct'] is not None else "N/A" ratio_str = f"**{d['ratio_vs_psnr_relative']:.1f}x**" if d['ratio_vs_psnr_relative'] is not None else "N/A" md_lines.append(f"| {metric} | {rel_str} | {ratio_str} |") md_content = "\n".join(md_lines) with open(os.path.join(PROF_DIR, "part3b_findings.md"), "w") as f: f.write(md_content) print(md_content)