| |
| """Round-3 probe — pinpoint whether Native/Ours 挑的是同一批图.""" |
| import os, sys, inspect, json |
| import numpy as np |
| from PIL import Image |
|
|
| sys.path.insert(0, '/root/autodl-tmp/3dgsAtlas_official') |
|
|
| DATASET_ROOT = "/root/autodl-tmp/dataset/tnt" |
| OUTPUT_ROOT = "/root/autodl-tmp/SplatAtlas/outputs" |
| SCENES = [("truck", "PASS"), ("lighthouse", "FAIL")] |
| THUMB = 256 |
|
|
|
|
| def sec(title): |
| print("\n" + "=" * 70) |
| print(f" {title}") |
| print("=" * 70) |
|
|
|
|
| def build_args(source_path, img_dir): |
| from scene.dataset_readers import readColmapSceneInfo |
| sig = inspect.signature(readColmapSceneInfo) |
| args = [] |
| for i, (k, p) in enumerate(sig.parameters.items()): |
| if i == 0: args.append(source_path) |
| elif i == 1: args.append(img_dir) |
| elif k == "eval": args.append(True) |
| elif k == "train_test_exp": args.append(False) |
| else: args.append(p.default if p.default != inspect.Parameter.empty else "") |
| return args |
|
|
|
|
| def load_thumb(path): |
| img = Image.open(path).convert('RGB').resize((THUMB, THUMB), Image.LANCZOS) |
| return np.asarray(img, dtype=np.float32) / 255.0 |
|
|
|
|
| |
| def probe_reverse_lookup(scene): |
| sec(f"PROBE 11 — Reverse-lookup Native GT in full images_2 [{scene}]") |
| cell = os.path.join(OUTPUT_ROOT, f"vanilla_3dgs_{scene}") |
| gt_dir = os.path.join(cell, "gt_test_30000") |
| img_dir = os.path.join(DATASET_ROOT, scene, "images_2") |
|
|
| native_gts = sorted([f for f in os.listdir(gt_dir) if f.lower().endswith(('.png','.jpg'))]) |
| i2_files = sorted([f for f in os.listdir(img_dir) if f.lower().endswith(('.png','.jpg','.jpeg'))]) |
| print(f"Native GT count : {len(native_gts)}") |
| print(f"images_2 count : {len(i2_files)}") |
| print(f"Building {THUMB}x{THUMB} thumb cache for vectorized PSNR match...") |
|
|
| i2_arr = np.stack([load_thumb(os.path.join(img_dir, f)) for f in i2_files]) |
| print(f" cache shape: {i2_arr.shape}") |
|
|
| print(f"\n{'idx':>3} {'native_gt':>12} -> {'best_match':>15} {'best_psnr':>10} {'margin':>8}") |
| print("-" * 60) |
| matches = [] |
| for idx, ng in enumerate(native_gts): |
| ng_arr = load_thumb(os.path.join(gt_dir, ng)) |
| diff = i2_arr - ng_arr[None] |
| mse = (diff * diff).mean(axis=(1, 2, 3)) |
| psnrs = 10 * np.log10(1.0 / np.maximum(mse, 1e-10)) |
| order = np.argsort(-psnrs) |
| best_idx, best_psnr = order[0], psnrs[order[0]] |
| margin = best_psnr - psnrs[order[1]] |
| best_name = i2_files[best_idx] |
| matches.append((ng, best_name, float(best_psnr), float(margin))) |
| if idx < 10: |
| flag = "" if best_psnr > 30 else " [LOW]" |
| print(f"{idx:>3} {ng:>12} -> {best_name:>15} {best_psnr:>10.2f} {margin:>8.2f}{flag}") |
|
|
| if len(matches) > 10: |
| print(f"... (showing first 10 of {len(matches)})") |
|
|
| match_names = [m[1] for m in matches] |
| match_set = set(match_names) |
| low_quality = [m for m in matches if m[2] < 30] |
|
|
| print(f"\n--- Reverse-lookup summary ---") |
| print(f"Unique matches : {len(match_set)} / {len(matches)} Native GT") |
| print(f"Low-quality (<30 dB) matches: {len(low_quality)} " |
| f"→ {'[WARN] Native GT 可能来自一个 Ours 看不到的图源' if low_quality else '[OK]'}") |
|
|
| |
| from scene.dataset_readers import readColmapSceneInfo |
| scene_info = readColmapSceneInfo(*build_args( |
| os.path.join(DATASET_ROOT, scene), "images_2")) |
| ours_set = set(c.image_name for c in scene_info.test_cameras) |
|
|
| overlap = match_set & ours_set |
| n_only = match_set - ours_set |
| o_only = ours_set - match_set |
| print(f"\n--- Compare to Ours eval=True test split ---") |
| print(f"Native真挑 ∩ Ours挑 : {len(overlap):>3}") |
| print(f"Native only : {len(n_only):>3} e.g. {sorted(n_only)[:5]}") |
| print(f"Ours only : {len(o_only):>3} e.g. {sorted(o_only)[:5]}") |
|
|
| if not o_only and not n_only: |
| print(f"\n[VERDICT] 两边挑的是完全同一批图 → 纯粹 enumerate 顺序不同") |
| |
| our_test_names = [c.image_name for c in scene_info.test_cameras] |
| if list(match_names) == our_test_names: |
| print(f" 且顺序也完全一致 — baseline 差异不来自 GT 端") |
| else: |
| print(f" 但 enumerate 顺序不同 — 会影响 render↔GT 配对:") |
| print(f" Native order (first 5): {match_names[:5]}") |
| print(f" Ours order (first 5): {our_test_names[:5]}") |
| elif len(overlap) == 0: |
| print(f"\n[VERDICT] 两边完全不相交 → Native 训练时的 dataset_readers 和我们用的不是同一个版本") |
| else: |
| pct = 100 * len(overlap) / max(len(ours_set), 1) |
| print(f"\n[VERDICT] 部分重合 ({pct:.0f}%) → split 逻辑在某维度上漂移") |
|
|
| return matches, scene_info |
|
|
|
|
| |
| def probe_baseline_json(scene): |
| sec(f"PROBE 12 — Native baseline JSON content [{scene}]") |
| cell = os.path.join(OUTPUT_ROOT, f"vanilla_3dgs_{scene}") |
| for name in ["metrics_test_iter30000.json", "results.json", |
| "cfg_args"]: |
| p = os.path.join(cell, name) |
| if os.path.exists(p): |
| print(f"\n <{name}>") |
| with open(p) as f: |
| content = f.read() |
| print(content[:800]) |
| if len(content) > 800: |
| print(f" ... ({len(content)} bytes total)") |
|
|
|
|
| |
| def probe_enumerate_order(scene, matches, scene_info): |
| sec(f"PROBE 13 — enumerate alignment: Native GT idx i <-> which images_2 file [{scene}]") |
| our_names = [c.image_name for c in scene_info.test_cameras] |
| print(f"{'idx':>3} {'Native→真图':>18} {'Ours test_cam[i]':>18} {'same?':>6}") |
| print("-" * 55) |
| n_align = 0 |
| for i, (ng, bn, bp, _) in enumerate(matches[:min(15, len(matches))]): |
| on = our_names[i] if i < len(our_names) else "—" |
| same = (bn == on) |
| if same: n_align += 1 |
| print(f"{i:>3} {bn:>18} {on:>18} {'Y' if same else 'N':>6}") |
| |
| full_align = sum(1 for i, (_, bn, _, _) in enumerate(matches) |
| if i < len(our_names) and bn == our_names[i]) |
| print(f"\nFull enumerate alignment: {full_align} / {len(matches)} positions match") |
| if full_align == len(matches): |
| print("[OK] Native 和 Ours 的 enumerate 顺序完全一致 — 问题不在 GT/split 层") |
| else: |
| print(f"[!!] Enumerate 顺序不同 — 我们的 render[i] 对的是 Native GT[j],i≠j") |
|
|
|
|
| def main(): |
| for scene, label in SCENES: |
| print(f"\n\n{'#'*70}\n# SCENE: {scene} [{label}]\n{'#'*70}") |
| matches, scene_info = probe_reverse_lookup(scene) |
| probe_baseline_json(scene) |
| probe_enumerate_order(scene, matches, scene_info) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|