File size: 7,137 Bytes
23e73f9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | #!/usr/bin/env python
"""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 # reverse-lookup 降采样尺寸
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
# ---------- Probe 11: Reverse lookup Native GT → images_2 ----------
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]) # (N, H, W, 3)
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]'}")
# 对比 Ours split
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 顺序不同")
# 检查是否 ordering 差异
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
# ---------- Probe 12: Baseline JSON 内容 ----------
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)")
# ---------- Probe 13: enumerate 顺序与 LLFF hold 对齐 ----------
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 sweep
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()
|