#!/usr/bin/env python3 import json from pathlib import Path import pandas as pd PROJECT = Path("/root/autodl-tmp/SplatAtlas") OUT_ROOT = PROJECT / "outputs" / "surfel_hypothesis_all" SOURCE_JSON_ROOT = PROJECT / "outputs" / "surfel_hypothesis" RUN_INDEX = OUT_ROOT / "run_index.csv" OUT_ROOT.mkdir(parents=True, exist_ok=True) METHOD_GROUP = { "vanilla_3dgs": "standard_3dgs", "3dgs_dr": "standard_3dgs", "3dgsmcmc": "standard_3dgs", "aaags": "standard_3dgs_nondefault_checkpoint", "absgs": "standard_3dgs", "absgssgf": "standard_3dgs", "analyticsplatting": "standard_3dgs", "atomgs": "standard_3dgs", "cdcgs": "standard_3dgs", "coadaptgs": "standard_3dgs", "conegs": "standard_3dgs", "drkgs": "standard_3dgs", "erankgs": "standard_3dgs", "ges": "standard_3dgs", "ghap": "standard_3dgs", "gslpm": "standard_3dgs", "hogs": "standard_3dgs_nondefault_checkpoint", "lapisgs": "standard_3dgs", "lightgaussian": "standard_3dgs", "minisplatting": "standard_3dgs", "minisplattingsgf": "standard_3dgs", "mipsplatting": "standard_3dgs", "opti3dgs": "standard_3dgs", "pixelgs": "standard_3dgs", "pixelgssgf": "standard_3dgs", "rdogaussian": "standard_3dgs", "reactgs": "standard_3dgs", "steepgs": "standard_3dgs", "tamgs": "standard_3dgs_nondefault_checkpoint", "gaussianpro": "geometry_aware_standard_or_usable", "gof": "geometry_aware_standard_or_usable", "pgsr": "geometry_aware_standard_or_usable", "gsdf": "geometry_aware_standard_or_usable", "gspull": "geometry_aware_standard_or_usable", "2dgs": "surface_primitive_2dgs_disk_normal", "trimgs": "surface_primitive_pca_only", "gaussian_surfel": "gaussian_surfel_usable", "scaffoldgs": "hierarchical_or_anchor_usable", "octree_gs": "hierarchical_or_anchor_usable", "lod_gs": "hierarchical_or_anchor_usable", "sugar": "surface_mesh_coupled_usable", } METHODS = list(METHOD_GROUP.keys()) SCENES = ["barn", "caterpillar", "truck"] def get(d, key): return d.get(key, None) def read_failure_tail(log_path, n=25): p = PROJECT / log_path if not str(log_path).startswith("/") else Path(log_path) if not p.exists(): return "" lines = p.read_text(errors="replace").splitlines() return "\n".join(lines[-n:]) rows = [] failures = [] run_index_df = None if RUN_INDEX.exists(): run_index_df = pd.read_csv(RUN_INDEX) else: run_index_df = pd.DataFrame(columns=["method", "scene", "group", "status", "log_path", "json_path"]) for method in METHODS: for scene in SCENES: p = SOURCE_JSON_ROOT / f"{method}_{scene}" / "surfel_hypothesis_eval.json" idx_rows = run_index_df[ (run_index_df.get("method") == method) & (run_index_df.get("scene") == scene) ] log_path = None run_status = "not_in_run_index" iteration_arg = "" if len(idx_rows) > 0: rr = idx_rows.iloc[-1] log_path = rr.get("log_path", None) run_status = rr.get("status", "unknown") iteration_arg = rr.get("iteration_arg", "") if not p.exists(): failures.append({ "method": method, "scene": scene, "group": METHOD_GROUP[method], "status": run_status, "iteration_arg": iteration_arg, "json_path": str(p), "log_path": log_path, "tail": read_failure_tail(log_path) if log_path else "", }) rows.append({ "method": method, "scene": scene, "group": METHOD_GROUP[method], "status": "missing_json", "iteration_arg": iteration_arg, "json_path": str(p), "log_path": log_path, }) continue try: d = json.loads(p.read_text()) except Exception as e: failures.append({ "method": method, "scene": scene, "group": METHOD_GROUP[method], "status": f"json_read_error: {type(e).__name__}: {e}", "iteration_arg": iteration_arg, "json_path": str(p), "log_path": log_path, "tail": read_failure_tail(log_path) if log_path else "", }) rows.append({ "method": method, "scene": scene, "group": METHOD_GROUP[method], "status": "json_read_error", "iteration_arg": iteration_arg, "json_path": str(p), "log_path": log_path, }) continue row = { "method": method, "scene": scene, "group": METHOD_GROUP[method], "status": "ok", "iteration_arg": iteration_arg, "json_path": str(p), "log_path": log_path, "ply_path": get(d, "ply_path"), "n_gaussians_recon": get(d, "n_gaussians_recon"), "n_recon_after_crop": get(d, "n_recon_after_crop"), "n_recon_eval": get(d, "n_recon_eval"), "n_gt_after_crop": get(d, "n_gt_after_crop"), "n_surface_near": get(d, "n_surface_near"), "surface_near_ratio": get(d, "surface_near_ratio"), "n_normal_eval": get(d, "n_normal_eval"), "tau": get(d, "tau"), "surface_near_threshold": get(d, "surface_near_threshold"), "distance_median": get(d, "distance_recon_to_gt_median"), "distance_mean": get(d, "distance_recon_to_gt_mean"), "has_standard_covariance_shape": get(d, "has_standard_covariance_shape"), "has_v3_normal": get(d, "has_v3_normal"), "has_2dgs_normal": get(d, "has_2dgs_normal"), "has_explicit_normal": get(d, "has_explicit_normal"), # Normal metrics. "center_pca_normal_median_deg": get(d, "center_pca_normal_error_median_deg"), "center_pca_normal_mean_deg": get(d, "center_pca_normal_error_mean_deg"), "center_pca_normal_iqr_deg": get(d, "center_pca_normal_error_iqr_deg"), "v3_normal_median_deg": get(d, "v3_normal_error_median_deg"), "v3_normal_mean_deg": get(d, "v3_normal_error_mean_deg"), "v3_normal_iqr_deg": get(d, "v3_normal_error_iqr_deg"), "delta_v3_minus_center_pca_deg": get(d, "median_delta_v3_minus_center_pca_deg"), "center_pca_better_than_v3": get(d, "center_pca_better_than_v3_by_median"), "two_dgs_normal_median_deg": get(d, "two_dgs_normal_error_median_deg"), "two_dgs_normal_mean_deg": get(d, "two_dgs_normal_error_mean_deg"), "two_dgs_normal_iqr_deg": get(d, "two_dgs_normal_error_iqr_deg"), "delta_2dgs_minus_center_pca_deg": get(d, "median_delta_2dgs_minus_center_pca_deg"), "center_pca_better_than_2dgs": get(d, "center_pca_better_than_2dgs_by_median"), "explicit_normal_median_deg": get(d, "explicit_normal_error_median_deg"), "explicit_normal_mean_deg": get(d, "explicit_normal_error_mean_deg"), "explicit_normal_iqr_deg": get(d, "explicit_normal_error_iqr_deg"), "delta_explicit_minus_center_pca_deg": get(d, "median_delta_explicit_minus_center_pca_deg"), # Surface confidence calibration. "opacity_auc_surface": get(d, "opacity_sigmoid_auroc_surface_near"), "opacity_ap_surface": get(d, "opacity_sigmoid_ap_surface_near"), "opacity_spearman_minus_distance": get(d, "opacity_sigmoid_spearman_vs_minus_distance"), "opacity_median": get(d, "opacity_sigmoid_median"), "cov_log_anisotropy_median": get(d, "cov_log_anisotropy_sigma_median"), "cov_anisotropy_ratio_median": get(d, "cov_anisotropy_sigma_ratio_median"), "cov_anisotropy_auc_surface": get(d, "cov_log_anisotropy_sigma_auroc_surface_near"), "cov_anisotropy_ap_surface": get(d, "cov_log_anisotropy_sigma_ap_surface_near"), "cov_anisotropy_spearman_minus_distance": get(d, "cov_log_anisotropy_sigma_spearman_vs_minus_distance"), "cov_anisotropy_spearman_minus_v3_error": get(d, "cov_log_anisotropy_sigma_spearman_vs_minus_v3_error"), "cov_planarity_median": get(d, "cov_planarity_median"), "cov_planarity_auc_surface": get(d, "cov_planarity_auroc_surface_near"), "cov_planarity_ap_surface": get(d, "cov_planarity_ap_surface_near"), "cov_planarity_spearman_minus_distance": get(d, "cov_planarity_spearman_vs_minus_distance"), "cov_planarity_spearman_minus_v3_error": get(d, "cov_planarity_spearman_vs_minus_v3_error"), "cov_linearity_median": get(d, "cov_linearity_median"), "cov_scattering_median": get(d, "cov_scattering_median"), "spatial_planarity_median": get(d, "spatial_pca_planarity_median"), "spatial_planarity_auc_surface": get(d, "spatial_pca_planarity_auroc_surface_near"), "spatial_planarity_spearman_minus_distance": get(d, "spatial_pca_planarity_spearman_vs_minus_distance"), "spatial_planarity_spearman_minus_center_pca_error": get(d, "spatial_pca_planarity_spearman_vs_minus_center_pca_error"), "spatial_linearity_median": get(d, "spatial_pca_linearity_median"), "spatial_scattering_median": get(d, "spatial_pca_scattering_median"), "gt_local_pca_planarity_median": get(d, "gt_local_pca_planarity_median"), "gt_local_pca_linearity_median": get(d, "gt_local_pca_linearity_median"), "gt_local_pca_scattering_median": get(d, "gt_local_pca_scattering_median"), "wall_time_seconds": get(d, "wall_time_seconds"), } rows.append(row) df = pd.DataFrame(rows) fail_df = pd.DataFrame(failures) aggregate_csv = OUT_ROOT / "surfel_hypothesis_all_aggregate.csv" failure_csv = OUT_ROOT / "surfel_hypothesis_all_failures.csv" method_mean_csv = OUT_ROOT / "surfel_hypothesis_all_method_mean.csv" scene_mean_csv = OUT_ROOT / "surfel_hypothesis_all_scene_mean.csv" report_md = OUT_ROOT / "surfel_hypothesis_all_report.md" df.to_csv(aggregate_csv, index=False) fail_df.to_csv(failure_csv, index=False) ok = df[df["status"] == "ok"].copy() numeric_cols = [ "surface_near_ratio", "distance_median", "center_pca_normal_median_deg", "v3_normal_median_deg", "two_dgs_normal_median_deg", "delta_v3_minus_center_pca_deg", "delta_2dgs_minus_center_pca_deg", "opacity_auc_surface", "cov_anisotropy_auc_surface", "cov_planarity_auc_surface", "spatial_planarity_auc_surface", "cov_anisotropy_spearman_minus_distance", "cov_planarity_spearman_minus_distance", "opacity_spearman_minus_distance", ] if len(ok): method_mean = ok.groupby(["group", "method"], dropna=False)[numeric_cols].mean(numeric_only=True).reset_index() scene_mean = ok.groupby(["scene"], dropna=False)[numeric_cols].mean(numeric_only=True).reset_index() else: method_mean = pd.DataFrame() scene_mean = pd.DataFrame() method_mean.to_csv(method_mean_csv, index=False) scene_mean.to_csv(scene_mean_csv, index=False) # Useful derived summaries. num_expected = len(METHODS) * len(SCENES) num_ok = int((df["status"] == "ok").sum()) num_fail = num_expected - num_ok best_v3 = ok.dropna(subset=["v3_normal_median_deg"]).sort_values("v3_normal_median_deg").head(15) best_2dgs = ok.dropna(subset=["two_dgs_normal_median_deg"]).sort_values("two_dgs_normal_median_deg").head(15) weak_aniso = ok.dropna(subset=["cov_anisotropy_auc_surface"]).copy() with open(report_md, "w") as f: f.write("# Surfel Hypothesis Full Run Report\n\n") f.write(f"- Expected cells: {num_expected}\n") f.write(f"- Successful cells: {num_ok}\n") f.write(f"- Failed/missing cells: {num_fail}\n") f.write(f"- Aggregate CSV: `{aggregate_csv}`\n") f.write(f"- Failure CSV: `{failure_csv}`\n") f.write(f"- Method mean CSV: `{method_mean_csv}`\n") f.write(f"- Scene mean CSV: `{scene_mean_csv}`\n\n") f.write("## Group counts among successful cells\n\n") if len(ok): f.write(ok["group"].value_counts().to_frame("count").to_markdown()) f.write("\n\n") f.write("## Best covariance-derived V3 normals, lower is better\n\n") if len(best_v3): cols = [ "method", "scene", "group", "surface_near_ratio", "v3_normal_median_deg", "v3_normal_iqr_deg", "center_pca_normal_median_deg", "delta_v3_minus_center_pca_deg", ] f.write(best_v3[cols].to_markdown(index=False)) else: f.write("No V3 normal results.\n") f.write("\n\n") f.write("## 2DGS / disk normal results\n\n") if len(best_2dgs): cols = [ "method", "scene", "group", "surface_near_ratio", "two_dgs_normal_median_deg", "two_dgs_normal_iqr_deg", "center_pca_normal_median_deg", "delta_2dgs_minus_center_pca_deg", ] f.write(best_2dgs[cols].to_markdown(index=False)) else: f.write("No 2DGS disk-normal results.\n") f.write("\n\n") f.write("## Mean by method\n\n") if len(method_mean): show_cols = [ "group", "method", "surface_near_ratio", "v3_normal_median_deg", "two_dgs_normal_median_deg", "center_pca_normal_median_deg", "cov_anisotropy_auc_surface", "cov_planarity_auc_surface", "opacity_auc_surface", ] existing = [c for c in show_cols if c in method_mean.columns] f.write(method_mean[existing].to_markdown(index=False)) f.write("\n\n") f.write("## Failures\n\n") if len(fail_df): f.write(fail_df[["method", "scene", "group", "status", "log_path"]].to_markdown(index=False)) else: f.write("No failures.\n") f.write("\n") print("============================================================") print("Aggregation done") print("============================================================") print(f"[WROTE] {aggregate_csv}") print(f"[WROTE] {failure_csv}") print(f"[WROTE] {method_mean_csv}") print(f"[WROTE] {scene_mean_csv}") print(f"[WROTE] {report_md}") print("\n===== successful / expected =====") print(num_ok, "/", num_expected) print("\n===== failures =====") if len(fail_df): print(fail_df[["method", "scene", "group", "status", "log_path"]].to_string(index=False)) else: print("No failures.") print("\n===== best V3 normals =====") if len(best_v3): print(best_v3[[ "method", "scene", "surface_near_ratio", "v3_normal_median_deg", "center_pca_normal_median_deg", "delta_v3_minus_center_pca_deg", ]].to_string(index=False)) else: print("No V3 results.") print("\n===== 2DGS / disk normal results =====") if len(best_2dgs): print(best_2dgs[[ "method", "scene", "surface_near_ratio", "two_dgs_normal_median_deg", "center_pca_normal_median_deg", "delta_2dgs_minus_center_pca_deg", ]].to_string(index=False)) else: print("No 2DGS disk normal results.")