File size: 9,614 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | #!/usr/bin/env python
"""T&T root-cause probe — read-only, symmetric PASS vs FAIL."""
import os, sys, inspect, struct
import numpy as np
from PIL import Image, ImageOps
from plyfile import PlyData
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")]
def sec(title):
print("\n" + "=" * 70)
print(f" {title}")
print("=" * 70)
def build_args(source_path, img_dir):
"""与 render_single.py 完全一致的 args 构造逻辑"""
from scene.dataset_readers import readColmapSceneInfo
sig = inspect.signature(readColmapSceneInfo)
args_list = []
for i, (k, p) in enumerate(sig.parameters.items()):
if i == 0: args_list.append(source_path)
elif i == 1: args_list.append(img_dir)
elif k == "eval": args_list.append(True)
elif k == "train_test_exp": args_list.append(False)
else: args_list.append(p.default if p.default != inspect.Parameter.empty else "")
return sig, args_list
# ---------- Probe 1 ----------
def probe_signature():
sec("PROBE 1 — readColmapSceneInfo signature & args mapping")
from scene.dataset_readers import readColmapSceneInfo
sig = inspect.signature(readColmapSceneInfo)
print(f"Full signature: {sig}")
print()
print("按 render_single.py 的位置策略喂参 (source_path='<SRC>', img_dir='<IMG>'):")
for i, (k, p) in enumerate(sig.parameters.items()):
default = p.default if p.default != inspect.Parameter.empty else "<REQUIRED>"
if i == 0: fed = "<SRC>"
elif i == 1: fed = "<IMG>"
elif k == "eval": fed = "True"
elif k == "train_test_exp": fed = "False"
else: fed = f"default({default})"
warn = ""
if k in ("eval", "train_test_exp") and i < 2:
warn = " <<< WARNING: positional slot collides with keyword logic"
print(f" [{i}] {k:<20} default={default!s:<20} -> fed: {fed}{warn}")
# ---------- Probe 2 ----------
def probe_native_gt(scene):
sec(f"PROBE 2 — Native GT manifest [{scene}]")
cell = os.path.join(OUTPUT_ROOT, f"vanilla_3dgs_{scene}")
candidates = [
os.path.join(cell, "gt_test_30000"),
os.path.join(cell, "renders_test_30000", "gt"),
os.path.join(cell, "test", "ours_30000", "gt"),
]
gt_dir = next((c for c in candidates if os.path.isdir(c)), None)
if gt_dir is None:
print(f"[!] GT dir not found. Tried: {candidates}")
return None
files = sorted([f for f in os.listdir(gt_dir) if f.lower().endswith(('.jpg', '.png', '.jpeg'))])
print(f"GT dir : {gt_dir}")
print(f"Count : {len(files)}")
print(f"First 5: {files[:5]}")
print(f"Last 3: {files[-3:]}")
if files:
img = Image.open(os.path.join(gt_dir, files[0]))
print(f"Sample dims (first GT): {img.size} (W x H)")
return set(os.path.splitext(f)[0] for f in files)
# ---------- Probe 3 ----------
def probe_our_split(scene):
sec(f"PROBE 3 — Our eval=True split [{scene}]")
from scene.dataset_readers import readColmapSceneInfo
source_path = os.path.join(DATASET_ROOT, scene)
img_dir = "images_2" if os.path.exists(os.path.join(source_path, "images_2")) else "images"
print(f"Picked img_dir: {img_dir}")
sig, args_list = build_args(source_path, img_dir)
print(f"Args positionally passed:")
for i, (k, _) in enumerate(sig.parameters.items()):
print(f" [{i}] {k} = {args_list[i]!r}")
try:
scene_info = readColmapSceneInfo(*args_list)
except Exception as e:
print(f"[!] readColmapSceneInfo crashed: {type(e).__name__}: {e}")
return None
tr, te = scene_info.train_cameras, scene_info.test_cameras
print(f"Train cams : {len(tr)}")
print(f"Test cams : {len(te)}")
if te:
names = sorted(c.image_name for c in te)
print(f"First 5 test names: {names[:5]}")
print(f"Last 3 test names: {names[-3:]}")
c0 = te[0]
print(f"First test cam dims: W={c0.width} H={c0.height} FovX={c0.FovX:.4f} FovY={c0.FovY:.4f}")
return set(os.path.splitext(c.image_name)[0] for c in te)
# ---------- Probe 4 ----------
def probe_split_diff(scene, native_set, ours_set):
sec(f"PROBE 4 — Split diff [{scene}]")
if native_set is None or ours_set is None:
print("[!] skipped (missing input)")
return
only_n = sorted(native_set - ours_set)
only_o = sorted(ours_set - native_set)
print(f"|Native|={len(native_set)} |Ours|={len(ours_set)} |intersection|={len(native_set & ours_set)}")
print(f"In Native only ({len(only_n)}): {only_n[:5]}")
print(f"In Ours only ({len(only_o)}): {only_o[:5]}")
if not only_n and not only_o:
print("[OK] Test split fully aligned.")
else:
print("[!!] SPLIT MISMATCH — eval=True 没把我们带到和 Native 一样的测试集上")
# ---------- Probe 5 ----------
def probe_camera_model(scene):
sec(f"PROBE 5 — COLMAP camera model [{scene}]")
src = os.path.join(DATASET_ROOT, scene, "sparse", "0")
txt, binf = os.path.join(src, "cameras.txt"), os.path.join(src, "cameras.bin")
if os.path.exists(txt):
with open(txt) as f:
for line in f:
if line.startswith('#') or not line.strip(): continue
p = line.strip().split()
print(f" id={p[0]} model={p[1]} size={p[2]}x{p[3]} params={p[4:]}")
break
elif os.path.exists(binf):
MODELS = {0:"SIMPLE_PINHOLE",1:"PINHOLE",2:"SIMPLE_RADIAL",3:"RADIAL",
4:"OPENCV",5:"OPENCV_FISHEYE",6:"FULL_OPENCV",7:"FOV",
8:"SIMPLE_RADIAL_FISHEYE",9:"RADIAL_FISHEYE",10:"THIN_PRISM_FISHEYE"}
with open(binf, "rb") as f:
num = struct.unpack("<Q", f.read(8))[0]
cam_id = struct.unpack("<I", f.read(4))[0]
model_id = struct.unpack("<i", f.read(4))[0]
width = struct.unpack("<Q", f.read(8))[0]
height = struct.unpack("<Q", f.read(8))[0]
model_name = MODELS.get(model_id, f"UNKNOWN({model_id})")
print(f" cameras.bin count={num} id={cam_id} model={model_name} size={width}x{height}")
if model_name not in ("SIMPLE_PINHOLE", "PINHOLE"):
print(f" [!!] Non-pinhole model — 畸变系数没被 gsplat 内参 K 建模!")
else:
print(f"[!] no cameras.txt or cameras.bin under {src}")
# ---------- Probe 6 ----------
def probe_exif(scene):
sec(f"PROBE 6 — EXIF orientation [{scene}]")
for folder in ["images", "images_2"]:
d = os.path.join(DATASET_ROOT, scene, folder)
if not os.path.isdir(d):
continue
files = sorted([f for f in os.listdir(d) if f.lower().endswith(('.jpg', '.png', '.jpeg'))])
if not files:
continue
img = Image.open(os.path.join(d, files[0]))
raw = img.size
tag = None
try:
ex = img._getexif()
if ex: tag = ex.get(274, None) # 274 = Orientation
except Exception:
pass
rot = ImageOps.exif_transpose(img).size
flag = "[FLIPPED by exif_transpose]" if raw != rot else ""
print(f" {folder:<10} {files[0]:<30} raw={raw} exif_tag={tag} after={rot} {flag}")
# ---------- Probe 7 ----------
def probe_ply_z(scene):
sec(f"PROBE 7 — PLY Z distribution in camera space [{scene}]")
ply = os.path.join(OUTPUT_ROOT, f"vanilla_3dgs_{scene}",
"point_cloud", "iteration_30000", "point_cloud.ply")
if not os.path.exists(ply):
print(f"[!] PLY missing: {ply}")
return
v = PlyData.read(ply)['vertex']
means = np.stack((v['x'], v['y'], v['z']), axis=-1).astype(np.float32)
print(f"PLY gaussians : {len(means)}")
print(f"World mean : {means.mean(0)}")
print(f"World std : {means.std(0)}")
from scene.dataset_readers import readColmapSceneInfo
from utils.graphics_utils import getWorld2View2
source_path = os.path.join(DATASET_ROOT, scene)
img_dir = "images_2" if os.path.exists(os.path.join(source_path, "images_2")) else "images"
_, args_list = build_args(source_path, img_dir)
te = readColmapSceneInfo(*args_list).test_cameras
if not te:
print("[!] no test cams")
return
c = te[0]
W2V = getWorld2View2(np.array(c.R), np.array(c.T)).astype(np.float32)
xyz_h = np.concatenate([means, np.ones((len(means), 1), dtype=np.float32)], axis=1)
z = (xyz_h @ W2V.T)[:, 2]
print(f"Viewed from test[0] = {c.image_name}")
print(f"Z percentiles (camera +Z forward):")
for q in [1, 5, 50, 95, 99, 99.9, 100]:
print(f" {q:5.1f}% : {np.percentile(z, q):10.2f}")
print(f"Gaussians Z>50 : {(z>50).sum():>8} ({100*(z>50).mean():.2f}%)")
print(f"Gaussians Z>100 : {(z>100).sum():>8} ({100*(z>100).mean():.2f}%)")
print(f"Gaussians Z>500 : {(z>500).sum():>8} ({100*(z>500).mean():.2f}%)")
def main():
probe_signature()
for scene, label in SCENES:
print(f"\n\n{'#'*70}\n# SCENE: {scene} [{label}]\n{'#'*70}")
native = probe_native_gt(scene)
ours = probe_our_split(scene)
probe_split_diff(scene, native, ours)
probe_camera_model(scene)
probe_exif(scene)
probe_ply_z(scene)
if __name__ == "__main__":
main()
|