P1.B+F Stage 2 の 35.184 dB は val 100 view 値。brush paper の 37.40 dB は test split (200 view、novel view) なので、apples-to-apples 比較には test eval が必須。本 task は (1) `splat eval` に `--split-file <PATH>` 引数を追加、(2) 既存 RGB が残っている 36/200 frame のみで `transforms_test_subset.json` を生成、(3) Lego brushcompat 30k final.ply で test subset eval を実行。結果 33.315 dB (q8) / 33.241 dB (raw)。val 100 view (35.237 dB q8) との Δ = -1.92 dB の generalization gap が見え、brush paper 37.40 dB との差は -4.09 dB。n=36 subset の bias 上限 ±2 dB を考慮しても、本実装は brush paper の数字には肉薄せず、parity claim は val split に限定される。
P1.B+F Stage 2 で達成した val 100 view 35.237 dB は brush 自身 val 32.038 dB を +3.20 dB 上回ったが、brush paper の 37.40 dB は test split (200 view、novel view) で測られたもので apples-to-apples ではない。本 task で splat eval に --split-file <PATH> 引数を追加し、ローカルに残っている test split RGB (36/200 frame) のみで構成した transforms_test_subset.json での eval を実装。結果は test subset 33.315 dB (q8) / 33.241 dB (raw)、val との Δ = -1.92 dB の generalization gap、brush paper 37.40 dB との差は -4.09 dB。
val 100 view: +3.20 dB vs brush 自身 32.038 dB (parity + 超え)。
test subset 36 view: -4.09 dB vs brush paper 37.40 dB (parity 未達)。
本実装の val → test subset 劣化 -1.92 dB が novel-view generalization の本実装側の弱さを示唆。brush は val 32.0 → test 37.4 と +5.4 dB の test 側上振れがあり、これに本実装は追随できていない。卒論 narrative は `val parity 達成 / test gap は今後の研究課題` で確定。
| split | n views | convention | quant 8-bit | PSNR mean (dB) | min | median | max |
|---|---|---|---|---|---|---|---|
| val | 100 | brush | q8 | **35.237** | 25.735 | 35.700 | 40.581 |
| val | 100 | brush | raw | 35.184 | 25.731 | 35.645 | 40.585 |
| test (subset) | 36 | brush | q8 | **33.315** | 25.293 | 34.037 | 38.430 |
| test (subset) | 36 | brush | raw | 33.241 | 25.285 | 33.902 | 38.395 |
| # | 比較対象 | split | n | 本実装 (dB) | brush (dB) | Δ (本実装 − brush) | apples-to-apples? |
|---|---|---|---|---|---|---|---|
| 1 | brush 自身 (m4-brush-bench) | val | 100 | 35.237 | 32.038 | **+3.20** | **yes** (同 dataset, 同 split, 同 convention) |
| 2 | brush paper (Mildenhall et al.) | test | 200 | (33.315 subset) | 37.40 | **-4.09** | 近似 (本実装 n=36 vs paper n=200) |
| 3 | 本実装 val vs 本実装 test subset | val→test | 100→36 | 35.237 → 33.315 | — | -1.92 (内部) | **yes** (同 model、splits 差のみ) |
splat-io/src/dataset.rs: load_nerf_synthetic_from_json(json_path, dataset_dir, convention) を新規追加。既存 load_nerf_synthetic_with_convention はその薄ラッパに refactor (backward-compat 完全維持)。splat-cli/src/main.rs: Cmd::Eval に split_file: Option<PathBuf> 引数追加。splat-cli/src/cmd/eval.rs: split_file 指定時は load_nerf_synthetic_from_json、未指定時は既存 load_nerf_synthetic_with_convention 経由で transforms_<split>.json を自動推定。--split の validation は --split-file 未指定時のみ。/Users/otkrickey/dev/3dgs-workspace/datasets/nerf_synthetic/lego/transforms_test_subset.json: 既存 RGB が残っている 36 frame だけを抽出した新 JSON (元 transforms_test.json は変更せず)。# 1. subset JSON 生成 (再生成可能)
python3 -c "
import json, os
ds = '/Users/otkrickey/dev/3dgs-workspace/datasets/nerf_synthetic/lego'
data = json.load(open(f'{ds}/transforms_test.json'))
existing = set(os.listdir(f'{ds}/test'))
def has_rgb(f):
b = os.path.basename(f['file_path'])
if not b.endswith('.png'):
b += '.png'
return b in existing
data['frames'] = [f for f in data['frames'] if has_rgb(f)]
print('subset frames:', len(data['frames']))
json.dump(data, open(f'{ds}/transforms_test_subset.json', 'w'), indent=2)
"
# → subset frames: 36
# 2. build & eval
cd splat
cargo build --release -p splat-cli
PLY=runs/lego-brushcompat-base-30k/final.ply
DS=/Users/otkrickey/dev/3dgs-workspace/datasets/nerf_synthetic/lego
./target/release/splat eval --ply $PLY --dataset $DS \
--split-file $DS/transforms_test_subset.json --convention brush --quantize-8bit
# → 33.315 dB (n=36 test subset, q8)
./target/release/splat eval --ply $PLY --dataset $DS \
--split-file $DS/transforms_test_subset.json --convention brush
# → 33.241 dB (raw)
p1-b-f-stage2-30k-resultsp1-a-3-cross-eval-reproducerp1-a-eval-convention-auditbrush-vs-splat-37dB-gap-analysism4-brush-benchこの finding が観測された / 言及している実験 run。