| |
| 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"), |
|
|
| |
| "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"), |
|
|
| |
| "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) |
|
|
| |
| 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.") |
|
|