Upload via Autoresearch export_data
Browse files- .gitattributes +12 -0
- experiments/fourier-splatting/FOURIER_SPLATTING_REPORT.md +92 -0
- experiments/fourier-splatting/README.md +70 -0
- experiments/fourier-splatting/THIRD_PARTY_NOTICES.md +13 -0
- experiments/fourier-splatting/fourier_results.json +143 -0
- experiments/fourier-splatting/licenses/ANYSPLAT_LICENSE +21 -0
- experiments/fourier-splatting/licenses/FOURIER_SPLATTING_LICENSE.md +83 -0
- experiments/fourier-splatting/models/apartment/all_args.json +69 -0
- experiments/fourier-splatting/models/apartment/chkpnt6000.pth +3 -0
- experiments/fourier-splatting/models/apartment/lod/fourier_k1.ply +3 -0
- experiments/fourier-splatting/models/apartment/lod/fourier_k2.ply +3 -0
- experiments/fourier-splatting/models/apartment/lod/fourier_k3.ply +3 -0
- experiments/fourier-splatting/models/apartment/lod/fourier_k4.ply +3 -0
- experiments/fourier-splatting/models/apartment/lod/fourier_k5.ply +3 -0
- experiments/fourier-splatting/models/apartment/lod/fourier_k6.ply +3 -0
- experiments/fourier-splatting/models/apartment/lod_metrics.json +54 -0
- experiments/fourier-splatting/models/kitchen/all_args.json +69 -0
- experiments/fourier-splatting/models/kitchen/chkpnt6000.pth +3 -0
- experiments/fourier-splatting/models/kitchen/lod/fourier_k1.ply +3 -0
- experiments/fourier-splatting/models/kitchen/lod/fourier_k2.ply +3 -0
- experiments/fourier-splatting/models/kitchen/lod/fourier_k3.ply +3 -0
- experiments/fourier-splatting/models/kitchen/lod/fourier_k4.ply +3 -0
- experiments/fourier-splatting/models/kitchen/lod/fourier_k5.ply +3 -0
- experiments/fourier-splatting/models/kitchen/lod/fourier_k6.ply +3 -0
- experiments/fourier-splatting/models/kitchen/lod_metrics.json +54 -0
- experiments/fourier-splatting/scripts/eval_lod_sweep.py +51 -0
- experiments/fourier-splatting/scripts/eval_nvs_vgg.py +65 -0
- experiments/fourier-splatting/scripts/export_lod_ply.py +34 -0
- experiments/fourier-splatting/scripts/make_fourier_scene.py +93 -0
- experiments/fourier-splatting/scripts/make_fourier_scene_generic.py +64 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,15 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
experiments/fourier-splatting/models/kitchen/lod/fourier_k5.ply filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
experiments/fourier-splatting/models/kitchen/lod/fourier_k6.ply filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
experiments/fourier-splatting/models/kitchen/lod/fourier_k3.ply filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
experiments/fourier-splatting/models/kitchen/lod/fourier_k4.ply filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
experiments/fourier-splatting/models/kitchen/lod/fourier_k1.ply filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
experiments/fourier-splatting/models/kitchen/lod/fourier_k2.ply filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
experiments/fourier-splatting/models/apartment/lod/fourier_k5.ply filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
experiments/fourier-splatting/models/apartment/lod/fourier_k6.ply filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
experiments/fourier-splatting/models/apartment/lod/fourier_k3.ply filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
experiments/fourier-splatting/models/apartment/lod/fourier_k4.ply filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
experiments/fourier-splatting/models/apartment/lod/fourier_k1.ply filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
experiments/fourier-splatting/models/apartment/lod/fourier_k2.ply filter=lfs diff=lfs merge=lfs -text
|
experiments/fourier-splatting/FOURIER_SPLATTING_REPORT.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Fourier Splatting × AnySplat — Experiment Report
|
| 2 |
+
|
| 3 |
+
**Date:** 2026-10-13
|
| 4 |
+
**Hardware:** existing SF Compute H100 80 GB node; no new node created
|
| 5 |
+
**Status:** two-scene prototype, 6,000 scene-optimization iterations per scene
|
| 6 |
+
|
| 7 |
+
## Question
|
| 8 |
+
|
| 9 |
+
Can the existing AnySplat video-to-3D pipeline be improved by converting its context-only geometry into Fourier-encoded planar primitives and optimizing the scene, instead of retaining a dense Gaussian cloud?
|
| 10 |
+
|
| 11 |
+
This experiment adapts the upstream Fourier Splatting implementation (https://github.com/mbjurca/FourierSplatting) to AnySplat geometry. It is a downstream, per-scene refinement experiment—not a new feed-forward model.
|
| 12 |
+
|
| 13 |
+
## Evaluation protocol
|
| 14 |
+
|
| 15 |
+
- Apartment: 24 frames total; 12 context views for geometry initialization/training and 12 held-out target views.
|
| 16 |
+
- Kitchen: 32 frames total; 16 context views for geometry initialization/training and 16 held-out target views.
|
| 17 |
+
- All images are center-cropped and resized to 448×448.
|
| 18 |
+
- AnySplat context-only inference initializes the Fourier scene. All-frame AnySplat inference is used to obtain target camera poses and align them into the context reconstruction coordinate frame, matching the evaluation protocol. Held-out target pixels are not used for geometry initialization or Fourier training.
|
| 19 |
+
- Initial seed selection uses AnySplat opacity × maximum scale. Fourier Splatting uses K=6 frequencies with staged activation at iterations 300, 600, 900, 1,200, and 1,500.
|
| 20 |
+
- Each scene is optimized for 6,000 iterations. The final LOD sweep changes active Fourier frequency count while keeping primitive count fixed.
|
| 21 |
+
- Metrics: PSNR (higher is better), SSIM (higher is better), and VGG LPIPS (lower is better). Baseline and Fourier metrics use the same VGG LPIPS implementation.
|
| 22 |
+
|
| 23 |
+
## Main held-out results
|
| 24 |
+
|
| 25 |
+
| Scene | Method | PSNR ↑ | SSIM ↑ | VGG LPIPS ↓ | Primitives |
|
| 26 |
+
|---|---|---:|---:|---:|---:|
|
| 27 |
+
| Apartment | AnySplat | 19.1235 | 0.6612 | 0.3154 | 1,858,896 |
|
| 28 |
+
| Apartment | Fourier, K=6 | 20.3469 | 0.7041 | 0.3173 | 99,483 |
|
| 29 |
+
| Kitchen | AnySplat | 21.6155 | 0.6660 | 0.2678 | 1,983,229 |
|
| 30 |
+
| Kitchen | Fourier, K=6 | 25.3078 | 0.8212 | 0.2586 | 120,000 |
|
| 31 |
+
|
| 32 |
+
### Changes vs AnySplat
|
| 33 |
+
|
| 34 |
+
- **Apartment:** +1.2234 dB PSNR, +0.0429 SSIM, LPIPS worsens by 0.0019; 18.68× fewer primitives.
|
| 35 |
+
- **Kitchen:** +3.6923 dB PSNR, +0.1552 SSIM, LPIPS improves by 0.0092; 16.53× fewer primitives.
|
| 36 |
+
|
| 37 |
+
The result is encouraging but not uniform across all metrics: apartment LPIPS is marginally worse, while kitchen improves on all three metrics. These are two indoor scenes, not a benchmark sufficient to claim state of the art.
|
| 38 |
+
|
| 39 |
+
## Fourier level-of-detail sweep
|
| 40 |
+
|
| 41 |
+
Primitive count is held constant within each scene. Truncating Fourier frequencies reduces PLY size and trades off perceptual quality.
|
| 42 |
+
|
| 43 |
+
### Apartment (99,483 primitives)
|
| 44 |
+
|
| 45 |
+
| K | PLY size (MB) | PSNR ↑ | SSIM ↑ | VGG LPIPS ↓ |
|
| 46 |
+
|---:|---:|---:|---:|---:|
|
| 47 |
+
| 1 | 25.07 | 19.9081 | 0.7048 | 0.3831 |
|
| 48 |
+
| 2 | 25.87 | 20.0612 | 0.7056 | 0.3802 |
|
| 49 |
+
| 3 | 26.66 | 20.3420 | 0.7078 | 0.3551 |
|
| 50 |
+
| 4 | 27.46 | 20.3825 | 0.7071 | 0.3374 |
|
| 51 |
+
| 5 | 28.25 | 20.3771 | 0.7056 | 0.3236 |
|
| 52 |
+
| 6 | 29.05 | 20.3469 | 0.7041 | 0.3173 |
|
| 53 |
+
|
| 54 |
+
### Kitchen (120,000 primitives)
|
| 55 |
+
|
| 56 |
+
| K | PLY size (MB) | PSNR ↑ | SSIM ↑ | VGG LPIPS ↓ |
|
| 57 |
+
|---:|---:|---:|---:|---:|
|
| 58 |
+
| 1 | 30.24 | 21.9698 | 0.7638 | 0.3696 |
|
| 59 |
+
| 2 | 31.20 | 23.3089 | 0.7772 | 0.3599 |
|
| 60 |
+
| 3 | 32.16 | 24.3752 | 0.8006 | 0.3186 |
|
| 61 |
+
| 4 | 33.12 | 24.9584 | 0.8137 | 0.2889 |
|
| 62 |
+
| 5 | 34.08 | 25.1819 | 0.8190 | 0.2696 |
|
| 63 |
+
| 6 | 35.04 | 25.3078 | 0.8212 | 0.2586 |
|
| 64 |
+
|
| 65 |
+
**Takeaway from LOD:** K=1–3 is substantially worse on LPIPS. K=5 is a useful compromise; K=6 gives the best perceptual metric on both scenes. The K=1 to K=6 PLY-size increase is about 15–16%, while the scene representation remains 16–19× smaller in primitive count than the AnySplat output. This report does not claim an equivalent 16–19× reduction in serialized bytes; those AnySplat PLY bytes were not measured in this run.
|
| 66 |
+
|
| 67 |
+
## Interpretation and limitations
|
| 68 |
+
|
| 69 |
+
1. **Promising direction:** Fourier Splatting looks useful as an offline scene-refinement / compact-representation stage after AnySplat.
|
| 70 |
+
2. **Not feed-forward:** every scene requires 6,000 optimization iterations. This has not yet been integrated into a low-latency video-to-3D inference path.
|
| 71 |
+
3. **No SOTA claim:** only two indoor scenes have been tested. More scenes, motion, occlusion, camera-path diversity, runtime and byte-level comparisons are required.
|
| 72 |
+
4. **Research-code license:** the upstream Fourier Splatting repository states non-commercial research/evaluation terms. Review licensing before using its implementation in a commercial release.
|
| 73 |
+
|
| 74 |
+
## Reproduction commands
|
| 75 |
+
|
| 76 |
+
From the vendored Fourier Splatting repository, with the project environment and compiled rasterizer installed:
|
| 77 |
+
|
| 78 |
+
~~~bash
|
| 79 |
+
python train.py \
|
| 80 |
+
--source_path /path/to/fourier_scene \
|
| 81 |
+
--model_path /path/to/output \
|
| 82 |
+
--eval --iterations 6000 \
|
| 83 |
+
--test_iterations 3000 4500 6000 \
|
| 84 |
+
--save_iterations 3000 4500 6000 \
|
| 85 |
+
--checkpoint_iterations 3000 4500 6000 \
|
| 86 |
+
--densify_until_iter 0 --densification_interval 100000 \
|
| 87 |
+
--fourier_K 6 \
|
| 88 |
+
--fourier_freq_schedule 300,600,900,1200,1500 \
|
| 89 |
+
--no_dome --resolution 448
|
| 90 |
+
~~~
|
| 91 |
+
|
| 92 |
+
The bundle contains the scene transforms and images, seed point clouds, final checkpoints, Fourier PLYs, LOD variants, evaluation outputs, and the preparation/evaluation scripts. Training uses context-only geometry; target images are reserved for held-out evaluation.
|
experiments/fourier-splatting/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
library_name: pytorch
|
| 4 |
+
pipeline_tag: image-to-3d
|
| 5 |
+
tags:
|
| 6 |
+
- 3d-reconstruction
|
| 7 |
+
- gaussian-splatting
|
| 8 |
+
- fourier-splatting
|
| 9 |
+
- anysplat
|
| 10 |
+
- novel-view-synthesis
|
| 11 |
+
- research
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# Polymorphic: Fourier Splatting refinement for AnySplat
|
| 15 |
+
|
| 16 |
+
Experimental video-to-3D reconstruction workflow. AnySplat initializes scene geometry; Fourier Splatting then optimizes a representation for that specific scene. This release contains scene-specific checkpoints, LOD PLY exports, evaluation results, and scripts.
|
| 17 |
+
|
| 18 |
+
**Important:** This is not a feed-forward model that instantly converts arbitrary video into a 3D asset. New scenes require per-scene optimization. The PLYs are renderable 3D Gaussian/surfel-style scene representations, not watertight meshes or game-ready GLB files.
|
| 19 |
+
|
| 20 |
+
## Held-out novel-view results
|
| 21 |
+
|
| 22 |
+
PSNR and SSIM are higher-is-better; VGG LPIPS is lower-is-better. Baseline and Fourier results use the same VGG LPIPS implementation.
|
| 23 |
+
|
| 24 |
+
| Scene | Method | PSNR | SSIM | VGG LPIPS | Primitives |
|
| 25 |
+
|---|---|---:|---:|---:|---:|
|
| 26 |
+
| Apartment | AnySplat | 19.1235 | 0.6612 | 0.3154 | 1,858,896 |
|
| 27 |
+
| Apartment | Fourier K=6 | 20.3469 | 0.7041 | 0.3173 | 99,483 |
|
| 28 |
+
| Kitchen | AnySplat | 21.6155 | 0.6660 | 0.2678 | 1,983,229 |
|
| 29 |
+
| Kitchen | Fourier K=6 | 25.3078 | 0.8212 | 0.2586 | 120,000 |
|
| 30 |
+
|
| 31 |
+
- Apartment: +1.22 dB PSNR, +0.0429 SSIM; LPIPS worsened slightly by 0.0019.
|
| 32 |
+
- Kitchen: +3.69 dB PSNR, +0.1552 SSIM; LPIPS improved by 0.0092.
|
| 33 |
+
- Primitive counts fell by about 18.7× (apartment) and 16.5× (kitchen). This does not establish the same reduction in serialized bytes; baseline PLY sizes were not measured.
|
| 34 |
+
|
| 35 |
+
## LOD exports
|
| 36 |
+
|
| 37 |
+
Each scene includes K=1–6 PLY files. Higher K generally improves perceptual quality while increasing file size; K=5 is a possible size/quality compromise, while K=6 had the best LPIPS on both scenes.
|
| 38 |
+
|
| 39 |
+
| Scene | K | PLY MB | PSNR | SSIM | VGG LPIPS |
|
| 40 |
+
|---|---:|---:|---:|---:|---:|
|
| 41 |
+
| Apartment | 1 | 25.071 | 19.9081 | 0.7048 | 0.3831 |
|
| 42 |
+
| Apartment | 2 | 25.867 | 20.0612 | 0.7056 | 0.3802 |
|
| 43 |
+
| Apartment | 3 | 26.663 | 20.3420 | 0.7078 | 0.3551 |
|
| 44 |
+
| Apartment | 4 | 27.459 | 20.3825 | 0.7071 | 0.3374 |
|
| 45 |
+
| Apartment | 5 | 28.255 | 20.3771 | 0.7056 | 0.3236 |
|
| 46 |
+
| Apartment | 6 | 29.051 | 20.3469 | 0.7041 | 0.3173 |
|
| 47 |
+
| Kitchen | 1 | 30.242 | 21.9698 | 0.7638 | 0.3696 |
|
| 48 |
+
| Kitchen | 2 | 31.202 | 23.3089 | 0.7772 | 0.3599 |
|
| 49 |
+
| Kitchen | 3 | 32.162 | 24.3752 | 0.8006 | 0.3186 |
|
| 50 |
+
| Kitchen | 4 | 33.122 | 24.9584 | 0.8137 | 0.2889 |
|
| 51 |
+
| Kitchen | 5 | 34.082 | 25.1819 | 0.8190 | 0.2696 |
|
| 52 |
+
| Kitchen | 6 | 35.042 | 25.3078 | 0.8212 | 0.2586 |
|
| 53 |
+
|
| 54 |
+
## Method and evaluation protocol
|
| 55 |
+
|
| 56 |
+
AnySplat inference on context frames initializes geometry. Fourier Splatting is optimized for 6,000 iterations at 448×448 resolution with staged Fourier-frequency activation. Apartment: 24 frames (12 context, 12 held-out targets). Kitchen: 32 frames (16 context, 16 held-out targets). Held-out target pixels were not used to initialize or train the scene representation; all-frame inference was used only for target camera poses and alignment into the context coordinate frame.
|
| 57 |
+
|
| 58 |
+
## Contents
|
| 59 |
+
|
| 60 |
+
- `models/apartment/` and `models/kitchen/`: checkpoints, metrics, and K=1–6 PLY exports.
|
| 61 |
+
- `scripts/`: preparation, evaluation, and LOD export scripts.
|
| 62 |
+
- `FOURIER_SPLATTING_REPORT.md`: detailed protocol and findings.
|
| 63 |
+
- `fourier_results.json`: machine-readable metrics.
|
| 64 |
+
- `THIRD_PARTY_NOTICES.md`: upstream project/license notes.
|
| 65 |
+
|
| 66 |
+
## Limitations and license
|
| 67 |
+
|
| 68 |
+
Only two indoor scenes were evaluated; this does not establish generalization or state-of-the-art performance. Reconstruction requires per-scene optimization and depends on camera coverage, scene motion, and AnySplat initialization. The apartment perceptual metric regressed slightly.
|
| 69 |
+
|
| 70 |
+
AnySplat and Fourier Splatting have separate upstream license terms. Fourier Splatting specifies non-commercial research/evaluation restrictions. Review the bundled license files and obtain required permissions before commercial use or redistribution. This release does not grant broader rights to upstream code, weights, or datasets.
|
experiments/fourier-splatting/THIRD_PARTY_NOTICES.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Third-party notices
|
| 2 |
+
|
| 3 |
+
## Fourier Splatting / Gaussian Splatting code
|
| 4 |
+
|
| 5 |
+
The upstream Fourier Splatting repository distributes its software under the license included at `licenses/FOURIER_SPLATTING_LICENSE.md`. It permits research and evaluation use and restricts commercial use without prior explicit consent from the licensors. The license also requires redistribution under the same license with the full license text and attribution notices retained. Review it before using or redistributing the code, checkpoints, or derivatives.
|
| 6 |
+
|
| 7 |
+
## AnySplat
|
| 8 |
+
|
| 9 |
+
AnySplat's MIT license is included at `licenses/ANYSPLAT_LICENSE`. Retain the copyright and permission notice when redistributing relevant code.
|
| 10 |
+
|
| 11 |
+
## Scope
|
| 12 |
+
|
| 13 |
+
This repository is an experimental integration and evaluation release. The notices above do not grant rights to third-party datasets or any assets that may have separate terms.
|
experiments/fourier-splatting/fourier_results.json
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"protocol": {
|
| 3 |
+
"resolution": [
|
| 4 |
+
448,
|
| 5 |
+
448
|
| 6 |
+
],
|
| 7 |
+
"apartment": {
|
| 8 |
+
"context_views": 12,
|
| 9 |
+
"heldout_views": 12,
|
| 10 |
+
"iterations": 6000
|
| 11 |
+
},
|
| 12 |
+
"kitchen": {
|
| 13 |
+
"context_views": 16,
|
| 14 |
+
"heldout_views": 16,
|
| 15 |
+
"iterations": 6000
|
| 16 |
+
},
|
| 17 |
+
"lpips": "VGG"
|
| 18 |
+
},
|
| 19 |
+
"main": [
|
| 20 |
+
{
|
| 21 |
+
"scene": "apartment",
|
| 22 |
+
"method": "AnySplat",
|
| 23 |
+
"psnr": 19.123464584350586,
|
| 24 |
+
"ssim": 0.6611772179603577,
|
| 25 |
+
"lpips_vgg": 0.3154071271419525,
|
| 26 |
+
"primitives": 1858896
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"scene": "apartment",
|
| 30 |
+
"method": "Fourier K=6",
|
| 31 |
+
"psnr": 20.346883535385132,
|
| 32 |
+
"ssim": 0.7040959944327673,
|
| 33 |
+
"lpips_vgg": 0.3172961249947548,
|
| 34 |
+
"primitives": 99483
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"scene": "kitchen",
|
| 38 |
+
"method": "AnySplat",
|
| 39 |
+
"psnr": 21.61551284790039,
|
| 40 |
+
"ssim": 0.6659506559371948,
|
| 41 |
+
"lpips_vgg": 0.26777008175849915,
|
| 42 |
+
"primitives": 1983229
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"scene": "kitchen",
|
| 46 |
+
"method": "Fourier K=6",
|
| 47 |
+
"psnr": 25.307796597480774,
|
| 48 |
+
"ssim": 0.8211556077003479,
|
| 49 |
+
"lpips_vgg": 0.25855699460953474,
|
| 50 |
+
"primitives": 120000
|
| 51 |
+
}
|
| 52 |
+
],
|
| 53 |
+
"lod": {
|
| 54 |
+
"apartment": [
|
| 55 |
+
{
|
| 56 |
+
"K": 1,
|
| 57 |
+
"bytes": 25071277,
|
| 58 |
+
"PSNR": 19.908107837041218,
|
| 59 |
+
"SSIM": 0.704794724782308,
|
| 60 |
+
"LPIPS_VGG": 0.38314763208230335
|
| 61 |
+
},
|
| 62 |
+
{
|
| 63 |
+
"K": 2,
|
| 64 |
+
"bytes": 25867197,
|
| 65 |
+
"PSNR": 20.061190923055012,
|
| 66 |
+
"SSIM": 0.705554003516833,
|
| 67 |
+
"LPIPS_VGG": 0.38020747403303784
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"K": 3,
|
| 71 |
+
"bytes": 26663117,
|
| 72 |
+
"PSNR": 20.342023849487305,
|
| 73 |
+
"SSIM": 0.7078102082014084,
|
| 74 |
+
"LPIPS_VGG": 0.3551347454388936
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"K": 4,
|
| 78 |
+
"bytes": 27459037,
|
| 79 |
+
"PSNR": 20.382510662078857,
|
| 80 |
+
"SSIM": 0.7071239352226257,
|
| 81 |
+
"LPIPS_VGG": 0.3374286914865176
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"K": 5,
|
| 85 |
+
"bytes": 28254957,
|
| 86 |
+
"PSNR": 20.37706486384074,
|
| 87 |
+
"SSIM": 0.7056431223948797,
|
| 88 |
+
"LPIPS_VGG": 0.32359255105257034
|
| 89 |
+
},
|
| 90 |
+
{
|
| 91 |
+
"K": 6,
|
| 92 |
+
"bytes": 29050877,
|
| 93 |
+
"PSNR": 20.346883535385132,
|
| 94 |
+
"SSIM": 0.7040959944327673,
|
| 95 |
+
"LPIPS_VGG": 0.3172961249947548
|
| 96 |
+
}
|
| 97 |
+
],
|
| 98 |
+
"kitchen": [
|
| 99 |
+
{
|
| 100 |
+
"K": 1,
|
| 101 |
+
"bytes": 30241562,
|
| 102 |
+
"PSNR": 21.96980655193329,
|
| 103 |
+
"SSIM": 0.7638169042766094,
|
| 104 |
+
"LPIPS_VGG": 0.3695769216865301
|
| 105 |
+
},
|
| 106 |
+
{
|
| 107 |
+
"K": 2,
|
| 108 |
+
"bytes": 31201618,
|
| 109 |
+
"PSNR": 23.308930158615112,
|
| 110 |
+
"SSIM": 0.7771734595298767,
|
| 111 |
+
"LPIPS_VGG": 0.3599471654742956
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"K": 3,
|
| 115 |
+
"bytes": 32161674,
|
| 116 |
+
"PSNR": 24.375155806541443,
|
| 117 |
+
"SSIM": 0.8006471283733845,
|
| 118 |
+
"LPIPS_VGG": 0.3186379112303257
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"K": 4,
|
| 122 |
+
"bytes": 33121730,
|
| 123 |
+
"PSNR": 24.958438515663147,
|
| 124 |
+
"SSIM": 0.8136776946485043,
|
| 125 |
+
"LPIPS_VGG": 0.28893621638417244
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"K": 5,
|
| 129 |
+
"bytes": 34081786,
|
| 130 |
+
"PSNR": 25.18192982673645,
|
| 131 |
+
"SSIM": 0.8189537934958935,
|
| 132 |
+
"LPIPS_VGG": 0.26962575130164623
|
| 133 |
+
},
|
| 134 |
+
{
|
| 135 |
+
"K": 6,
|
| 136 |
+
"bytes": 35041842,
|
| 137 |
+
"PSNR": 25.307796597480774,
|
| 138 |
+
"SSIM": 0.8211556077003479,
|
| 139 |
+
"LPIPS_VGG": 0.25855699460953474
|
| 140 |
+
}
|
| 141 |
+
]
|
| 142 |
+
}
|
| 143 |
+
}
|
experiments/fourier-splatting/licenses/ANYSPLAT_LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Lihan Jiang and Yucheng Mao
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
experiments/fourier-splatting/licenses/FOURIER_SPLATTING_LICENSE.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Gaussian-Splatting License
|
| 2 |
+
===========================
|
| 3 |
+
|
| 4 |
+
**Inria** and **the Max Planck Institut for Informatik (MPII)** hold all the ownership rights on the *Software* named **gaussian-splatting**.
|
| 5 |
+
The *Software* is in the process of being registered with the Agence pour la Protection des
|
| 6 |
+
Programmes (APP).
|
| 7 |
+
|
| 8 |
+
The *Software* is still being developed by the *Licensor*.
|
| 9 |
+
|
| 10 |
+
*Licensor*'s goal is to allow the research community to use, test and evaluate
|
| 11 |
+
the *Software*.
|
| 12 |
+
|
| 13 |
+
## 1. Definitions
|
| 14 |
+
|
| 15 |
+
*Licensee* means any person or entity that uses the *Software* and distributes
|
| 16 |
+
its *Work*.
|
| 17 |
+
|
| 18 |
+
*Licensor* means the owners of the *Software*, i.e Inria and MPII
|
| 19 |
+
|
| 20 |
+
*Software* means the original work of authorship made available under this
|
| 21 |
+
License ie gaussian-splatting.
|
| 22 |
+
|
| 23 |
+
*Work* means the *Software* and any additions to or derivative works of the
|
| 24 |
+
*Software* that are made available under this License.
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
## 2. Purpose
|
| 28 |
+
This license is intended to define the rights granted to the *Licensee* by
|
| 29 |
+
Licensors under the *Software*.
|
| 30 |
+
|
| 31 |
+
## 3. Rights granted
|
| 32 |
+
|
| 33 |
+
For the above reasons Licensors have decided to distribute the *Software*.
|
| 34 |
+
Licensors grant non-exclusive rights to use the *Software* for research purposes
|
| 35 |
+
to research users (both academic and industrial), free of charge, without right
|
| 36 |
+
to sublicense.. The *Software* may be used "non-commercially", i.e., for research
|
| 37 |
+
and/or evaluation purposes only.
|
| 38 |
+
|
| 39 |
+
Subject to the terms and conditions of this License, you are granted a
|
| 40 |
+
non-exclusive, royalty-free, license to reproduce, prepare derivative works of,
|
| 41 |
+
publicly display, publicly perform and distribute its *Work* and any resulting
|
| 42 |
+
derivative works in any form.
|
| 43 |
+
|
| 44 |
+
## 4. Limitations
|
| 45 |
+
|
| 46 |
+
**4.1 Redistribution.** You may reproduce or distribute the *Work* only if (a) you do
|
| 47 |
+
so under this License, (b) you include a complete copy of this License with
|
| 48 |
+
your distribution, and (c) you retain without modification any copyright,
|
| 49 |
+
patent, trademark, or attribution notices that are present in the *Work*.
|
| 50 |
+
|
| 51 |
+
**4.2 Derivative Works.** You may specify that additional or different terms apply
|
| 52 |
+
to the use, reproduction, and distribution of your derivative works of the *Work*
|
| 53 |
+
("Your Terms") only if (a) Your Terms provide that the use limitation in
|
| 54 |
+
Section 2 applies to your derivative works, and (b) you identify the specific
|
| 55 |
+
derivative works that are subject to Your Terms. Notwithstanding Your Terms,
|
| 56 |
+
this License (including the redistribution requirements in Section 3.1) will
|
| 57 |
+
continue to apply to the *Work* itself.
|
| 58 |
+
|
| 59 |
+
**4.3** Any other use without of prior consent of Licensors is prohibited. Research
|
| 60 |
+
users explicitly acknowledge having received from Licensors all information
|
| 61 |
+
allowing to appreciate the adequacy between of the *Software* and their needs and
|
| 62 |
+
to undertake all necessary precautions for its execution and use.
|
| 63 |
+
|
| 64 |
+
**4.4** The *Software* is provided both as a compiled library file and as source
|
| 65 |
+
code. In case of using the *Software* for a publication or other results obtained
|
| 66 |
+
through the use of the *Software*, users are strongly encouraged to cite the
|
| 67 |
+
corresponding publications as explained in the documentation of the *Software*.
|
| 68 |
+
|
| 69 |
+
## 5. Disclaimer
|
| 70 |
+
|
| 71 |
+
THE USER CANNOT USE, EXPLOIT OR DISTRIBUTE THE *SOFTWARE* FOR COMMERCIAL PURPOSES
|
| 72 |
+
WITHOUT PRIOR AND EXPLICIT CONSENT OF LICENSORS. YOU MUST CONTACT INRIA FOR ANY
|
| 73 |
+
UNAUTHORIZED USE: stip-sophia.transfert@inria.fr . ANY SUCH ACTION WILL
|
| 74 |
+
CONSTITUTE A FORGERY. THIS *SOFTWARE* IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES
|
| 75 |
+
OF ANY NATURE AND ANY EXPRESS OR IMPLIED WARRANTIES, WITH REGARDS TO COMMERCIAL
|
| 76 |
+
USE, PROFESSIONNAL USE, LEGAL OR NOT, OR OTHER, OR COMMERCIALISATION OR
|
| 77 |
+
ADAPTATION. UNLESS EXPLICITLY PROVIDED BY LAW, IN NO EVENT, SHALL INRIA OR THE
|
| 78 |
+
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
| 79 |
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
| 80 |
+
GOODS OR SERVICES, LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION)
|
| 81 |
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
| 82 |
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING FROM, OUT OF OR
|
| 83 |
+
IN CONNECTION WITH THE *SOFTWARE* OR THE USE OR OTHER DEALINGS IN THE *SOFTWARE*.
|
experiments/fourier-splatting/models/apartment/all_args.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_params": {
|
| 3 |
+
"sh_degree": 3,
|
| 4 |
+
"source_path": "/home/dev/polymorphic/fourier_scene_apt",
|
| 5 |
+
"model_path": "/home/dev/polymorphic/runs/fourier_apt_6000",
|
| 6 |
+
"images": "images",
|
| 7 |
+
"resolution": 448,
|
| 8 |
+
"white_background": false,
|
| 9 |
+
"data_device": "cuda",
|
| 10 |
+
"eval": true,
|
| 11 |
+
"render_items": [
|
| 12 |
+
"RGB",
|
| 13 |
+
"Alpha",
|
| 14 |
+
"Normal",
|
| 15 |
+
"Depth",
|
| 16 |
+
"Edge",
|
| 17 |
+
"Curvature"
|
| 18 |
+
],
|
| 19 |
+
"fourier_K": 6,
|
| 20 |
+
"fourier_sigma": 1.16,
|
| 21 |
+
"fourier_freq_schedule": "300,600,900,1200,1500",
|
| 22 |
+
"lambda_fourier_reg": 0.0001,
|
| 23 |
+
"no_dome": true,
|
| 24 |
+
"beta_color": false,
|
| 25 |
+
"sb_lobes": 2
|
| 26 |
+
},
|
| 27 |
+
"optimization_params": {
|
| 28 |
+
"iterations": 6000,
|
| 29 |
+
"position_lr_init": 0.00016,
|
| 30 |
+
"position_lr_final": 1.6e-06,
|
| 31 |
+
"position_lr_delay_mult": 0.01,
|
| 32 |
+
"position_lr_max_steps": 30000,
|
| 33 |
+
"feature_lr": 0.0025,
|
| 34 |
+
"opacity_lr": 0.014,
|
| 35 |
+
"scaling_lr": 0.005,
|
| 36 |
+
"rotation_lr": 0.001,
|
| 37 |
+
"sigma_lr": 0.0008,
|
| 38 |
+
"lambda_dssim": 0.2,
|
| 39 |
+
"lambda_dist": 0.0,
|
| 40 |
+
"lambda_normal": 0.05,
|
| 41 |
+
"lambda_freq": 0.01,
|
| 42 |
+
"freq_cutoff_ratio": 0.5,
|
| 43 |
+
"opacity_reg": 0.0,
|
| 44 |
+
"scale_reg": 0.005,
|
| 45 |
+
"densification_interval": 100000,
|
| 46 |
+
"densify_from_iter": 500,
|
| 47 |
+
"densify_until_iter": 0,
|
| 48 |
+
"mcmc_opacity_threshold": 0.005,
|
| 49 |
+
"mcmc_importance_threshold": 0.04,
|
| 50 |
+
"mcmc_min_views": 2,
|
| 51 |
+
"mcmc_birth_rate": 0.3,
|
| 52 |
+
"mcmc_max_primitives": 5000000,
|
| 53 |
+
"mcmc_sgld_noise_lr": 1e-05,
|
| 54 |
+
"mcmc_apply_sgld": true,
|
| 55 |
+
"mcmc_sgld_noise_lr_pos": 500.0,
|
| 56 |
+
"mcmc_split_from_iter": 3000,
|
| 57 |
+
"mcmc_split_grad_threshold": 0.0004,
|
| 58 |
+
"mcmc_split_screen_threshold": 40.0,
|
| 59 |
+
"mcmc_split_screen_size": 100.0,
|
| 60 |
+
"mcmc_splitter_checkpoint": "checkpoints/lobe_splitter",
|
| 61 |
+
"mcmc_max_screen_size": 1400.0
|
| 62 |
+
},
|
| 63 |
+
"pipeline_params": {
|
| 64 |
+
"convert_SHs_python": false,
|
| 65 |
+
"compute_cov3D_python": false,
|
| 66 |
+
"depth_ratio": 0.0,
|
| 67 |
+
"debug": false
|
| 68 |
+
}
|
| 69 |
+
}
|
experiments/fourier-splatting/models/apartment/chkpnt6000.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:124f90c4184cd5a3318cbb21c401e4ecdd6ab8797f21fb982117f9636db1eca1
|
| 3 |
+
size 28655349
|
experiments/fourier-splatting/models/apartment/lod/fourier_k1.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7c1ad989efc80ecec67be220bb45ee5123b3dfe16a3249e8889c51038fe482b5
|
| 3 |
+
size 25071277
|
experiments/fourier-splatting/models/apartment/lod/fourier_k2.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0330ec57672a038c50123fc990f5facbfc9fc1a9c04879ecaa186530b11c4f4c
|
| 3 |
+
size 25867197
|
experiments/fourier-splatting/models/apartment/lod/fourier_k3.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c5d3bbe9035fb40529108efdce8619630c49291e752a412947437f878cb5995e
|
| 3 |
+
size 26663117
|
experiments/fourier-splatting/models/apartment/lod/fourier_k4.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:50a8b4f7e9694c6fc1c90c4ddababeb7c2d0aabd2129f3a35ef90c44e71b0dce
|
| 3 |
+
size 27459037
|
experiments/fourier-splatting/models/apartment/lod/fourier_k5.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5f501f029aa57e8c29de90b1303f0e1afdcd9b5fc4ec6da60994e75748796f84
|
| 3 |
+
size 28254957
|
experiments/fourier-splatting/models/apartment/lod/fourier_k6.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3b8083821b5bd6e03f6cc6139893f9087cb0f4ec33bc1e9a7d588b15815684d4
|
| 3 |
+
size 29050877
|
experiments/fourier-splatting/models/apartment/lod_metrics.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"checkpoint_iteration": 6000,
|
| 3 |
+
"scene": "vrnerf_apartment",
|
| 4 |
+
"results": [
|
| 5 |
+
{
|
| 6 |
+
"K": 1,
|
| 7 |
+
"PSNR": 19.908107837041218,
|
| 8 |
+
"SSIM": 0.704794724782308,
|
| 9 |
+
"LPIPS_VGG": 0.38314763208230335,
|
| 10 |
+
"primitives": 99483,
|
| 11 |
+
"heldout_views": 12
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"K": 2,
|
| 15 |
+
"PSNR": 20.061190923055012,
|
| 16 |
+
"SSIM": 0.705554003516833,
|
| 17 |
+
"LPIPS_VGG": 0.38020747403303784,
|
| 18 |
+
"primitives": 99483,
|
| 19 |
+
"heldout_views": 12
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"K": 3,
|
| 23 |
+
"PSNR": 20.342023849487305,
|
| 24 |
+
"SSIM": 0.7078102082014084,
|
| 25 |
+
"LPIPS_VGG": 0.3551347454388936,
|
| 26 |
+
"primitives": 99483,
|
| 27 |
+
"heldout_views": 12
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"K": 4,
|
| 31 |
+
"PSNR": 20.382510662078857,
|
| 32 |
+
"SSIM": 0.7071239352226257,
|
| 33 |
+
"LPIPS_VGG": 0.3374286914865176,
|
| 34 |
+
"primitives": 99483,
|
| 35 |
+
"heldout_views": 12
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"K": 5,
|
| 39 |
+
"PSNR": 20.37706486384074,
|
| 40 |
+
"SSIM": 0.7056431223948797,
|
| 41 |
+
"LPIPS_VGG": 0.32359255105257034,
|
| 42 |
+
"primitives": 99483,
|
| 43 |
+
"heldout_views": 12
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"K": 6,
|
| 47 |
+
"PSNR": 20.346883535385132,
|
| 48 |
+
"SSIM": 0.7040959944327673,
|
| 49 |
+
"LPIPS_VGG": 0.3172961249947548,
|
| 50 |
+
"primitives": 99483,
|
| 51 |
+
"heldout_views": 12
|
| 52 |
+
}
|
| 53 |
+
]
|
| 54 |
+
}
|
experiments/fourier-splatting/models/kitchen/all_args.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_params": {
|
| 3 |
+
"sh_degree": 3,
|
| 4 |
+
"source_path": "/home/dev/polymorphic/fourier_scene_kitchen",
|
| 5 |
+
"model_path": "/home/dev/polymorphic/runs/fourier_kitchen_6000",
|
| 6 |
+
"images": "images",
|
| 7 |
+
"resolution": 448,
|
| 8 |
+
"white_background": false,
|
| 9 |
+
"data_device": "cuda",
|
| 10 |
+
"eval": true,
|
| 11 |
+
"render_items": [
|
| 12 |
+
"RGB",
|
| 13 |
+
"Alpha",
|
| 14 |
+
"Normal",
|
| 15 |
+
"Depth",
|
| 16 |
+
"Edge",
|
| 17 |
+
"Curvature"
|
| 18 |
+
],
|
| 19 |
+
"fourier_K": 6,
|
| 20 |
+
"fourier_sigma": 1.16,
|
| 21 |
+
"fourier_freq_schedule": "300,600,900,1200,1500",
|
| 22 |
+
"lambda_fourier_reg": 0.0001,
|
| 23 |
+
"no_dome": true,
|
| 24 |
+
"beta_color": false,
|
| 25 |
+
"sb_lobes": 2
|
| 26 |
+
},
|
| 27 |
+
"optimization_params": {
|
| 28 |
+
"iterations": 6000,
|
| 29 |
+
"position_lr_init": 0.00016,
|
| 30 |
+
"position_lr_final": 1.6e-06,
|
| 31 |
+
"position_lr_delay_mult": 0.01,
|
| 32 |
+
"position_lr_max_steps": 30000,
|
| 33 |
+
"feature_lr": 0.0025,
|
| 34 |
+
"opacity_lr": 0.014,
|
| 35 |
+
"scaling_lr": 0.005,
|
| 36 |
+
"rotation_lr": 0.001,
|
| 37 |
+
"sigma_lr": 0.0008,
|
| 38 |
+
"lambda_dssim": 0.2,
|
| 39 |
+
"lambda_dist": 0.0,
|
| 40 |
+
"lambda_normal": 0.05,
|
| 41 |
+
"lambda_freq": 0.01,
|
| 42 |
+
"freq_cutoff_ratio": 0.5,
|
| 43 |
+
"opacity_reg": 0.0,
|
| 44 |
+
"scale_reg": 0.005,
|
| 45 |
+
"densification_interval": 100000,
|
| 46 |
+
"densify_from_iter": 500,
|
| 47 |
+
"densify_until_iter": 0,
|
| 48 |
+
"mcmc_opacity_threshold": 0.005,
|
| 49 |
+
"mcmc_importance_threshold": 0.04,
|
| 50 |
+
"mcmc_min_views": 2,
|
| 51 |
+
"mcmc_birth_rate": 0.3,
|
| 52 |
+
"mcmc_max_primitives": 5000000,
|
| 53 |
+
"mcmc_sgld_noise_lr": 1e-05,
|
| 54 |
+
"mcmc_apply_sgld": true,
|
| 55 |
+
"mcmc_sgld_noise_lr_pos": 500.0,
|
| 56 |
+
"mcmc_split_from_iter": 3000,
|
| 57 |
+
"mcmc_split_grad_threshold": 0.0004,
|
| 58 |
+
"mcmc_split_screen_threshold": 40.0,
|
| 59 |
+
"mcmc_split_screen_size": 100.0,
|
| 60 |
+
"mcmc_splitter_checkpoint": "checkpoints/lobe_splitter",
|
| 61 |
+
"mcmc_max_screen_size": 1400.0
|
| 62 |
+
},
|
| 63 |
+
"pipeline_params": {
|
| 64 |
+
"convert_SHs_python": false,
|
| 65 |
+
"compute_cov3D_python": false,
|
| 66 |
+
"depth_ratio": 0.0,
|
| 67 |
+
"debug": false
|
| 68 |
+
}
|
| 69 |
+
}
|
experiments/fourier-splatting/models/kitchen/chkpnt6000.pth
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f1da364c8d64f8017a770c6338482ce5abecb546a04bd7ee4fa3d5dc31a69da8
|
| 3 |
+
size 34564597
|
experiments/fourier-splatting/models/kitchen/lod/fourier_k1.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:09b401a466e42dbd2e429a1bf6f6ec75c3ebd0eaebc477c73a1bc01e3d5ea373
|
| 3 |
+
size 30241562
|
experiments/fourier-splatting/models/kitchen/lod/fourier_k2.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:61436222a425b6750fe2eda119b9a6fcbb1917c8fd34b9d89298045430686889
|
| 3 |
+
size 31201618
|
experiments/fourier-splatting/models/kitchen/lod/fourier_k3.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5b4d52e47ad1faa55c0c220cdab29b136bb8ab9fd8e14e07f4506cdc727cf571
|
| 3 |
+
size 32161674
|
experiments/fourier-splatting/models/kitchen/lod/fourier_k4.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:140df0357737cd22b8866f07e2930d253d386de74b42e4a3ec1545f430f69880
|
| 3 |
+
size 33121730
|
experiments/fourier-splatting/models/kitchen/lod/fourier_k5.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:38edaf45c848aea02e5c2f2604d9b446b9e1097953e2cf2b33e83fe28a466cfb
|
| 3 |
+
size 34081786
|
experiments/fourier-splatting/models/kitchen/lod/fourier_k6.ply
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:df2fd67624e902d01aaae025b82e83a008fae37975ccfe5f72da9c8a3e0aeeec
|
| 3 |
+
size 35041842
|
experiments/fourier-splatting/models/kitchen/lod_metrics.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"checkpoint_iteration": 6000,
|
| 3 |
+
"scene": "vrnerf_apartment",
|
| 4 |
+
"results": [
|
| 5 |
+
{
|
| 6 |
+
"K": 1,
|
| 7 |
+
"PSNR": 21.96980655193329,
|
| 8 |
+
"SSIM": 0.7638169042766094,
|
| 9 |
+
"LPIPS_VGG": 0.3695769216865301,
|
| 10 |
+
"primitives": 120000,
|
| 11 |
+
"heldout_views": 16
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"K": 2,
|
| 15 |
+
"PSNR": 23.308930158615112,
|
| 16 |
+
"SSIM": 0.7771734595298767,
|
| 17 |
+
"LPIPS_VGG": 0.3599471654742956,
|
| 18 |
+
"primitives": 120000,
|
| 19 |
+
"heldout_views": 16
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"K": 3,
|
| 23 |
+
"PSNR": 24.375155806541443,
|
| 24 |
+
"SSIM": 0.8006471283733845,
|
| 25 |
+
"LPIPS_VGG": 0.3186379112303257,
|
| 26 |
+
"primitives": 120000,
|
| 27 |
+
"heldout_views": 16
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"K": 4,
|
| 31 |
+
"PSNR": 24.958438515663147,
|
| 32 |
+
"SSIM": 0.8136776946485043,
|
| 33 |
+
"LPIPS_VGG": 0.28893621638417244,
|
| 34 |
+
"primitives": 120000,
|
| 35 |
+
"heldout_views": 16
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"K": 5,
|
| 39 |
+
"PSNR": 25.18192982673645,
|
| 40 |
+
"SSIM": 0.8189537934958935,
|
| 41 |
+
"LPIPS_VGG": 0.26962575130164623,
|
| 42 |
+
"primitives": 120000,
|
| 43 |
+
"heldout_views": 16
|
| 44 |
+
},
|
| 45 |
+
{
|
| 46 |
+
"K": 6,
|
| 47 |
+
"PSNR": 25.307796597480774,
|
| 48 |
+
"SSIM": 0.8211556077003479,
|
| 49 |
+
"LPIPS_VGG": 0.25855699460953474,
|
| 50 |
+
"primitives": 120000,
|
| 51 |
+
"heldout_views": 16
|
| 52 |
+
}
|
| 53 |
+
]
|
| 54 |
+
}
|
experiments/fourier-splatting/scripts/eval_lod_sweep.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse, json, os, sys, time
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
from PIL import Image
|
| 6 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
| 7 |
+
from arguments import ModelParams, PipelineParams, OptimizationParams
|
| 8 |
+
from scene import Scene, FourierModel
|
| 9 |
+
from gaussian_renderer import render
|
| 10 |
+
from utils.image_utils import psnr
|
| 11 |
+
from utils.loss_utils import ssim
|
| 12 |
+
from lpipsPyTorch import lpips as compute_lpips
|
| 13 |
+
|
| 14 |
+
parser = argparse.ArgumentParser(description='Fourier Splatting LOD sweep')
|
| 15 |
+
lp = ModelParams(parser)
|
| 16 |
+
op = OptimizationParams(parser)
|
| 17 |
+
pp = PipelineParams(parser)
|
| 18 |
+
parser.add_argument('--load_iteration', type=int, default=6000)
|
| 19 |
+
parser.add_argument('--lod_values', type=str, default='1,2,3,4,5,6')
|
| 20 |
+
args = parser.parse_args()
|
| 21 |
+
dataset, opt, pipe = lp.extract(args), op.extract(args), pp.extract(args)
|
| 22 |
+
gaussians = FourierModel(dataset.sh_degree, fourier_K=dataset.fourier_K,
|
| 23 |
+
use_beta_color=dataset.beta_color, sb_lobes=dataset.sb_lobes)
|
| 24 |
+
scene = Scene(dataset, gaussians, load_iteration=args.load_iteration, shuffle=False)
|
| 25 |
+
bg = torch.tensor([1,1,1] if dataset.white_background else [0,0,0], dtype=torch.float32, device='cuda')
|
| 26 |
+
lod_values = [int(x) for x in args.lod_values.split(',')]
|
| 27 |
+
results = []
|
| 28 |
+
vis_dir = Path(dataset.model_path) / 'lod_visuals'
|
| 29 |
+
vis_dir.mkdir(parents=True, exist_ok=True)
|
| 30 |
+
with torch.inference_mode():
|
| 31 |
+
for k in lod_values:
|
| 32 |
+
gaussians.active_K = min(k, gaussians.fourier_K)
|
| 33 |
+
pvals, svals, lvals = [], [], []
|
| 34 |
+
for j, cam in enumerate(scene.getTestCameras()):
|
| 35 |
+
pred = torch.clamp(render(cam, gaussians, pipe, bg)['render'], 0, 1).unsqueeze(0)
|
| 36 |
+
gt = torch.clamp(cam.original_image.cuda(), 0, 1).unsqueeze(0)
|
| 37 |
+
pvals.append(float(psnr(pred, gt).item()))
|
| 38 |
+
svals.append(float(ssim(pred, gt).item()))
|
| 39 |
+
lvals.append(float(compute_lpips(pred, gt, net_type='vgg').item()))
|
| 40 |
+
if j in (0, len(scene.getTestCameras())//2):
|
| 41 |
+
side = torch.cat([gt[0], pred[0]], dim=2).permute(1,2,0).cpu().numpy()
|
| 42 |
+
Image.fromarray((side*255).clip(0,255).astype(np.uint8)).save(vis_dir / f'k{k}_view{j}.png')
|
| 43 |
+
row = {'K': k, 'PSNR': float(np.mean(pvals)), 'SSIM': float(np.mean(svals)),
|
| 44 |
+
'LPIPS_VGG': float(np.mean(lvals)), 'primitives': int(gaussians.get_xyz.shape[0]),
|
| 45 |
+
'heldout_views': len(scene.getTestCameras())}
|
| 46 |
+
print(json.dumps(row), flush=True)
|
| 47 |
+
results.append(row)
|
| 48 |
+
out = {'checkpoint_iteration': args.load_iteration, 'scene': 'vrnerf_apartment', 'results': results}
|
| 49 |
+
path = Path(dataset.model_path) / 'lod_metrics.json'
|
| 50 |
+
path.write_text(json.dumps(out, indent=2))
|
| 51 |
+
print('WROTE', path, flush=True)
|
experiments/fourier-splatting/scripts/eval_nvs_vgg.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Held-out novel-view evaluation for pose-free feed-forward splatting.
|
| 2 |
+
context = even frames, targets = odd frames. Target poses come from a joint run on all frames,
|
| 3 |
+
aligned to the context-only reconstruction with a similarity transform (Umeyama) on camera centres."""
|
| 4 |
+
import sys, os, time, json, glob, subprocess
|
| 5 |
+
sys.path.insert(0, os.path.expanduser('~/polymorphic/vendor/AnySplat'))
|
| 6 |
+
import numpy as np, torch, lpips
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from PIL import Image
|
| 9 |
+
import torchvision.transforms.functional as TF
|
| 10 |
+
from skimage.metrics import structural_similarity
|
| 11 |
+
from src.model.model.anysplat import AnySplat
|
| 12 |
+
|
| 13 |
+
def umeyama(src, dst):
|
| 14 |
+
mu_s, mu_d = src.mean(0), dst.mean(0); xs, xd = src-mu_s, dst-mu_d
|
| 15 |
+
cov = xd.T @ xs / len(src); U,S,Vt = np.linalg.svd(cov)
|
| 16 |
+
D = np.eye(3); D[2,2] = np.sign(np.linalg.det(U)*np.linalg.det(Vt))
|
| 17 |
+
R = U @ D @ Vt; s = (S*np.diag(D)).sum() / xs.var(0).sum(); t = mu_d - s*R@mu_s
|
| 18 |
+
return s, R, t
|
| 19 |
+
|
| 20 |
+
def load(f, mode, long_edge):
|
| 21 |
+
im = Image.open(f).convert('RGB'); w,h = im.size
|
| 22 |
+
if mode == 'square':
|
| 23 |
+
s = long_edge/ min(w,h); im = im.resize((round(w*s), round(h*s)), Image.BICUBIC); w,h = im.size
|
| 24 |
+
l,t = (w-long_edge)//2, (h-long_edge)//2; im = im.crop((l,t,l+long_edge,t+long_edge))
|
| 25 |
+
else:
|
| 26 |
+
s = long_edge/max(w,h); im = im.resize((max(14,round(w*s/14)*14), max(14,round(h*s/14)*14)), Image.BICUBIC)
|
| 27 |
+
return TF.to_tensor(im)
|
| 28 |
+
|
| 29 |
+
def main(video, total, mode, long_edge, outdir):
|
| 30 |
+
out = Path(outdir); fr = out/'frames'; fr.mkdir(parents=True, exist_ok=True)
|
| 31 |
+
for f in fr.glob('*.jpg'): f.unlink()
|
| 32 |
+
dur = float(subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration','-of','csv=p=0',video]).decode())
|
| 33 |
+
subprocess.run(['ffmpeg','-hide_banner','-loglevel','error','-i',video,'-vf',f'fps={total/dur}','-q:v','2','-frames:v',str(total),str(fr/'%04d.jpg'),'-y'],check=True)
|
| 34 |
+
files = sorted(glob.glob(str(fr/'*.jpg'))); ctx_i = list(range(0,len(files),2)); tgt_i = list(range(1,len(files),2))
|
| 35 |
+
imgs = torch.stack([load(f, mode, long_edge) for f in files]).cuda()
|
| 36 |
+
model = AnySplat.from_pretrained('lhjiang/anysplat').cuda().eval()
|
| 37 |
+
for p in model.parameters(): p.requires_grad=False
|
| 38 |
+
with torch.no_grad():
|
| 39 |
+
gA, pA = model.inference(imgs[ctx_i].unsqueeze(0)); gB, pB = model.inference(imgs.unsqueeze(0))
|
| 40 |
+
eA, eB, iB = pA['extrinsic'][0].double().cpu().numpy(), pB['extrinsic'][0].double().cpu().numpy(), pB['intrinsic'][0].float()
|
| 41 |
+
s, R, t = umeyama(eB[ctx_i,:3,3], eA[:,:3,3])
|
| 42 |
+
resid = np.linalg.norm((s*(R@eB[ctx_i,:3,3].T)).T + t - eA[:,:3,3], axis=1)
|
| 43 |
+
ext_t = []
|
| 44 |
+
for i in tgt_i:
|
| 45 |
+
E = np.eye(4); E[:3,:3] = R @ eB[i,:3,:3]; E[:3,3] = s*R@eB[i,:3,3] + t; ext_t.append(E)
|
| 46 |
+
ext_t = torch.tensor(np.stack(ext_t), dtype=torch.float32, device='cuda').unsqueeze(0); intr_t = iB[tgt_i].unsqueeze(0).cuda()
|
| 47 |
+
H,W = imgs.shape[-2:]
|
| 48 |
+
with torch.no_grad():
|
| 49 |
+
r = model.decoder.forward(gA, ext_t, intr_t, torch.ones(1,len(tgt_i),device='cuda')*.1, torch.ones(1,len(tgt_i),device='cuda')*100,(H,W)).color[0].clamp(0,1)
|
| 50 |
+
gt = imgs[tgt_i]
|
| 51 |
+
# compare on central square so square/rect modes are scored on the same pixels
|
| 52 |
+
m = min(H,W); t0,l0 = (H-m)//2,(W-m)//2
|
| 53 |
+
rc, gc = r[:,:,t0:t0+m,l0:l0+m], gt[:,:,t0:t0+m,l0:l0+m]
|
| 54 |
+
psnr = (-10*torch.log10(((rc-gc)**2).mean(dim=(1,2,3)))).cpu().numpy()
|
| 55 |
+
ssim = np.array([structural_similarity(a.permute(1,2,0).cpu().numpy(), b.permute(1,2,0).cpu().numpy(), channel_axis=2, data_range=1.0) for a,b in zip(rc,gc)])
|
| 56 |
+
lp = lpips.LPIPS(net='vgg').cuda().eval()
|
| 57 |
+
with torch.no_grad(): lpv = lp(rc*2-1, gc*2-1).flatten().cpu().numpy()
|
| 58 |
+
res = dict(video=Path(video).name, mode=mode, long_edge=long_edge, input_hw=[H,W], n_ctx=len(ctx_i), n_tgt=len(tgt_i), gaussians=int(gA.means.shape[1]),
|
| 59 |
+
align_resid_mean=float(resid.mean()), PSNR=float(psnr.mean()), SSIM=float(ssim.mean()), LPIPS=float(lpv.mean()))
|
| 60 |
+
print(json.dumps(res)); (out/f'eval_{mode}_{long_edge}.json').write_text(json.dumps(res, indent=1))
|
| 61 |
+
for k in (0, len(tgt_i)//2):
|
| 62 |
+
Image.fromarray((torch.cat([gc[k],rc[k]],-1).permute(1,2,0).cpu().numpy()*255).astype(np.uint8)).save(out/f'nvs_{mode}_{long_edge}_{k}.png')
|
| 63 |
+
|
| 64 |
+
if __name__ == '__main__':
|
| 65 |
+
main(sys.argv[1], int(sys.argv[2]), sys.argv[3], int(sys.argv[4]), sys.argv[5])
|
experiments/fourier-splatting/scripts/export_lod_ply.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse, json
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import torch
|
| 4 |
+
from arguments import ModelParams, PipelineParams, OptimizationParams
|
| 5 |
+
from scene import Scene, FourierModel
|
| 6 |
+
|
| 7 |
+
parser = argparse.ArgumentParser()
|
| 8 |
+
lp = ModelParams(parser); op = OptimizationParams(parser); pp = PipelineParams(parser)
|
| 9 |
+
parser.add_argument('--load_iteration', type=int, default=6000)
|
| 10 |
+
args = parser.parse_args()
|
| 11 |
+
dataset = lp.extract(args)
|
| 12 |
+
gaussians = FourierModel(dataset.sh_degree, fourier_K=dataset.fourier_K,
|
| 13 |
+
use_beta_color=dataset.beta_color, sb_lobes=dataset.sb_lobes)
|
| 14 |
+
scene = Scene(dataset, gaussians, load_iteration=args.load_iteration, shuffle=False)
|
| 15 |
+
out = Path(dataset.model_path) / 'lod_models'
|
| 16 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 17 |
+
full_r = gaussians._radii_logits.detach().clone()
|
| 18 |
+
full_p = gaussians._phases_raw.detach().clone()
|
| 19 |
+
full_k = gaussians.fourier_K
|
| 20 |
+
rows = []
|
| 21 |
+
for k in range(1, full_k+1):
|
| 22 |
+
gaussians._radii_logits = torch.nn.Parameter(full_r[:, :k].clone().requires_grad_(False))
|
| 23 |
+
gaussians._phases_raw = torch.nn.Parameter(full_p[:, :k].clone().requires_grad_(False))
|
| 24 |
+
gaussians.fourier_K = k
|
| 25 |
+
gaussians.active_K = k
|
| 26 |
+
path = out / f'fourier_k{k}.ply'
|
| 27 |
+
gaussians.save_ply(str(path))
|
| 28 |
+
rows.append({'K':k,'path':str(path),'bytes':path.stat().st_size,'primitives':int(gaussians.get_xyz.shape[0])})
|
| 29 |
+
gaussians._radii_logits = torch.nn.Parameter(full_r)
|
| 30 |
+
gaussians._phases_raw = torch.nn.Parameter(full_p)
|
| 31 |
+
gaussians.fourier_K = full_k
|
| 32 |
+
gaussians.active_K = full_k
|
| 33 |
+
(out/'sizes.json').write_text(json.dumps(rows,indent=2))
|
| 34 |
+
print(json.dumps(rows,indent=2))
|
experiments/fourier-splatting/scripts/make_fourier_scene.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json, math, sys, time
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
from PIL import Image
|
| 6 |
+
from plyfile import PlyData, PlyElement
|
| 7 |
+
sys.path.insert(0, '/home/dev/polymorphic/vendor/AnySplat')
|
| 8 |
+
from src.model.model.anysplat import AnySplat
|
| 9 |
+
from src.utils.image import process_image
|
| 10 |
+
|
| 11 |
+
ROOT = Path('/home/dev/polymorphic')
|
| 12 |
+
OUT = ROOT / 'fourier_scene_apt'
|
| 13 |
+
FRAMES = sorted((ROOT / 'exp_apt' / 'frames').glob('*.jpg'))
|
| 14 |
+
OUT.mkdir(parents=True, exist_ok=True)
|
| 15 |
+
(OUT / 'images').mkdir(exist_ok=True)
|
| 16 |
+
assert len(FRAMES) == 24, len(FRAMES)
|
| 17 |
+
device = 'cuda'
|
| 18 |
+
imgs = torch.stack([process_image(str(f)) for f in FRAMES]).unsqueeze(0).to(device)
|
| 19 |
+
model = AnySplat.from_pretrained('lhjiang/anysplat').to(device).eval()
|
| 20 |
+
for p in model.parameters(): p.requires_grad_(False)
|
| 21 |
+
ctx_i = list(range(0, len(FRAMES), 2))
|
| 22 |
+
tgt_i = list(range(1, len(FRAMES), 2))
|
| 23 |
+
with torch.inference_mode():
|
| 24 |
+
g_ctx, p_ctx = model.inference((imgs[:, ctx_i] + 1) * 0.5)
|
| 25 |
+
g_all, p_all = model.inference((imgs + 1) * 0.5)
|
| 26 |
+
|
| 27 |
+
# Align the all-frame predicted target cameras into the context-only reconstruction frame,
|
| 28 |
+
# matching the held-out AnySplat evaluation protocol.
|
| 29 |
+
src = p_all['extrinsic'][0, ctx_i, :3, 3].double().cpu().numpy()
|
| 30 |
+
dst = p_ctx['extrinsic'][0, :, :3, 3].double().cpu().numpy()
|
| 31 |
+
mu_s, mu_d = src.mean(0), dst.mean(0)
|
| 32 |
+
xs, xd = src-mu_s, dst-mu_d
|
| 33 |
+
cov = xd.T @ xs / len(src)
|
| 34 |
+
U,S,Vt = np.linalg.svd(cov)
|
| 35 |
+
D = np.eye(3); D[2,2] = np.sign(np.linalg.det(U)*np.linalg.det(Vt))
|
| 36 |
+
R = U @ D @ Vt
|
| 37 |
+
scale = (S*np.diag(D)).sum() / (xs**2).sum(axis=1).mean()
|
| 38 |
+
trans = mu_d - scale * R @ mu_s
|
| 39 |
+
|
| 40 |
+
H, W = imgs.shape[-2:]
|
| 41 |
+
fovxs = []
|
| 42 |
+
def blender_matrix(c2w):
|
| 43 |
+
# FourierSplatting's Blender loader flips camera Y/Z. Pre-flip so that
|
| 44 |
+
# loading recovers the original AnySplat camera-to-world matrix.
|
| 45 |
+
return (c2w @ np.diag([1., -1., -1., 1.])).tolist()
|
| 46 |
+
|
| 47 |
+
train_frames, test_frames = [], []
|
| 48 |
+
for i, f in enumerate(FRAMES):
|
| 49 |
+
im = Image.open(f).convert('RGB')
|
| 50 |
+
w, h = im.size
|
| 51 |
+
side = min(w, h)
|
| 52 |
+
left, top = (w-side)//2, (h-side)//2
|
| 53 |
+
im = im.crop((left, top, left+side, top+side)).resize((W,H), Image.Resampling.LANCZOS)
|
| 54 |
+
name = f'{i+1:04d}.png'
|
| 55 |
+
im.save(OUT / 'images' / name)
|
| 56 |
+
if i in ctx_i:
|
| 57 |
+
j = ctx_i.index(i)
|
| 58 |
+
c2w = p_ctx['extrinsic'][0,j].float().cpu().numpy()
|
| 59 |
+
intr = p_ctx['intrinsic'][0,j].float().cpu().numpy()
|
| 60 |
+
rec = {'file_path':f'images/{name[:-4]}','transform_matrix':blender_matrix(c2w),'index':i}
|
| 61 |
+
train_frames.append(rec)
|
| 62 |
+
else:
|
| 63 |
+
c2w0 = p_all['extrinsic'][0,i].float().cpu().numpy()
|
| 64 |
+
c2w = np.eye(4, dtype=np.float32)
|
| 65 |
+
c2w[:3,:3] = R @ c2w0[:3,:3]
|
| 66 |
+
c2w[:3,3] = scale * R @ c2w0[:3,3] + trans
|
| 67 |
+
intr = p_all['intrinsic'][0,i].float().cpu().numpy()
|
| 68 |
+
rec = {'file_path':f'images/{name[:-4]}','transform_matrix':blender_matrix(c2w),'index':i}
|
| 69 |
+
test_frames.append(rec)
|
| 70 |
+
fovxs.append(2.0 * math.atan(1.0 / (2.0 * float(intr[0,0]))))
|
| 71 |
+
|
| 72 |
+
fovx = float(np.median(fovxs))
|
| 73 |
+
(OUT/'transforms_train.json').write_text(json.dumps({'camera_angle_x':fovx,'frames':train_frames},indent=2))
|
| 74 |
+
(OUT/'transforms_test.json').write_text(json.dumps({'camera_angle_x':fovx,'frames':test_frames},indent=2))
|
| 75 |
+
|
| 76 |
+
# Seed from context-only AnySplat primitives; no target-frame geometry enters initialization.
|
| 77 |
+
means = g_ctx.means[0].float()
|
| 78 |
+
scales = g_ctx.scales[0].float()
|
| 79 |
+
opacity = g_ctx.opacities[0].float().reshape(-1)
|
| 80 |
+
harm = g_ctx.harmonics[0].float()
|
| 81 |
+
score = opacity * scales.max(dim=1).values.clamp_min(1e-7)
|
| 82 |
+
n = min(120_000, len(score))
|
| 83 |
+
idx = torch.topk(score, n, sorted=False).indices
|
| 84 |
+
xyz = means[idx].cpu().numpy().astype(np.float32)
|
| 85 |
+
rgb = (0.5 + 0.28209479177387814 * harm[idx, :, 0]).clamp(0,1).cpu().numpy()
|
| 86 |
+
verts = np.empty(n,dtype=[('x','f4'),('y','f4'),('z','f4'),('nx','f4'),('ny','f4'),('nz','f4'),('red','u1'),('green','u1'),('blue','u1')])
|
| 87 |
+
verts['x'],verts['y'],verts['z'] = xyz.T
|
| 88 |
+
verts['nx'],verts['ny'],verts['nz'] = 0,0,0
|
| 89 |
+
verts['red'],verts['green'],verts['blue'] = (rgb*255).astype(np.uint8).T
|
| 90 |
+
PlyData([PlyElement.describe(verts,'vertex')]).write(str(OUT/'points3d.ply'))
|
| 91 |
+
meta={'source':'AnySplat context-only geometry initialization','total_video_frames':len(FRAMES),'context_views':len(train_frames),'heldout_views':len(test_frames),'seed_primitives':n,'image_hw':[H,W],'camera_fov_x':fovx,'pose_alignment_scale':float(scale),'pose_alignment_translation_norm':float(np.linalg.norm(trans)),'heldout_geometry_used_for_initialization':False}
|
| 92 |
+
(OUT/'seed_metadata.json').write_text(json.dumps(meta,indent=2))
|
| 93 |
+
print(json.dumps(meta,indent=2),flush=True)
|
experiments/fourier-splatting/scripts/make_fourier_scene_generic.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json, math, sys
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
from PIL import Image
|
| 6 |
+
from plyfile import PlyData, PlyElement
|
| 7 |
+
sys.path.insert(0, '/home/dev/polymorphic/vendor/AnySplat')
|
| 8 |
+
from src.model.model.anysplat import AnySplat
|
| 9 |
+
from src.utils.image import process_image
|
| 10 |
+
|
| 11 |
+
if len(sys.argv) != 3:
|
| 12 |
+
raise SystemExit('usage: make_fourier_scene_generic.py FRAMES_DIR OUTPUT_DIR')
|
| 13 |
+
ROOT = Path('/home/dev/polymorphic')
|
| 14 |
+
FRAMES = sorted(Path(sys.argv[1]).glob('*.jpg'))
|
| 15 |
+
OUT = Path(sys.argv[2])
|
| 16 |
+
OUT.mkdir(parents=True, exist_ok=True)
|
| 17 |
+
(OUT/'images').mkdir(exist_ok=True)
|
| 18 |
+
assert len(FRAMES) >= 8 and len(FRAMES) % 2 == 0, f'Need even frame count >=8, got {len(FRAMES)}'
|
| 19 |
+
imgs = torch.stack([process_image(str(f)) for f in FRAMES]).unsqueeze(0).cuda()
|
| 20 |
+
model = AnySplat.from_pretrained('lhjiang/anysplat').cuda().eval()
|
| 21 |
+
for p in model.parameters(): p.requires_grad_(False)
|
| 22 |
+
ctx_i = list(range(0,len(FRAMES),2)); tgt_i = list(range(1,len(FRAMES),2))
|
| 23 |
+
with torch.inference_mode():
|
| 24 |
+
g_ctx, p_ctx = model.inference((imgs[:,ctx_i]+1)*0.5)
|
| 25 |
+
g_all, p_all = model.inference((imgs+1)*0.5)
|
| 26 |
+
src = p_all['extrinsic'][0,ctx_i,:3,3].double().cpu().numpy()
|
| 27 |
+
dst = p_ctx['extrinsic'][0,:,:3,3].double().cpu().numpy()
|
| 28 |
+
mu_s,mu_d = src.mean(0),dst.mean(0)
|
| 29 |
+
xs,xd = src-mu_s,dst-mu_d
|
| 30 |
+
cov = xd.T@xs/len(src)
|
| 31 |
+
U,S,Vt = np.linalg.svd(cov)
|
| 32 |
+
D=np.eye(3); D[2,2]=np.sign(np.linalg.det(U)*np.linalg.det(Vt))
|
| 33 |
+
R=U@D@Vt
|
| 34 |
+
scale=(S*np.diag(D)).sum()/(xs**2).sum(axis=1).mean()
|
| 35 |
+
trans=mu_d-scale*R@mu_s
|
| 36 |
+
H,W=imgs.shape[-2:]
|
| 37 |
+
flip=np.diag([1.,-1.,-1.,1.])
|
| 38 |
+
train_frames=[]; test_frames=[]; fovxs=[]
|
| 39 |
+
for i,f in enumerate(FRAMES):
|
| 40 |
+
im=Image.open(f).convert('RGB'); w,h=im.size; side=min(w,h)
|
| 41 |
+
im=im.crop(((w-side)//2,(h-side)//2,(w+side)//2,(h+side)//2)).resize((W,H),Image.Resampling.LANCZOS)
|
| 42 |
+
name=f'{i+1:04d}.png'; im.save(OUT/'images'/name)
|
| 43 |
+
if i in ctx_i:
|
| 44 |
+
j=ctx_i.index(i); c2w=p_ctx['extrinsic'][0,j].float().cpu().numpy()
|
| 45 |
+
intr=p_ctx['intrinsic'][0,j].float().cpu().numpy()
|
| 46 |
+
train_frames.append({'file_path':f'images/{name[:-4]}','transform_matrix':(c2w@flip).tolist(),'index':i})
|
| 47 |
+
else:
|
| 48 |
+
c0=p_all['extrinsic'][0,i].float().cpu().numpy()
|
| 49 |
+
c2w=np.eye(4,dtype=np.float32); c2w[:3,:3]=R@c0[:3,:3]; c2w[:3,3]=scale*R@c0[:3,3]+trans
|
| 50 |
+
intr=p_all['intrinsic'][0,i].float().cpu().numpy()
|
| 51 |
+
test_frames.append({'file_path':f'images/{name[:-4]}','transform_matrix':(c2w@flip).tolist(),'index':i})
|
| 52 |
+
fovxs.append(2*math.atan(1/(2*float(intr[0,0]))))
|
| 53 |
+
fovx=float(np.median(fovxs))
|
| 54 |
+
(OUT/'transforms_train.json').write_text(json.dumps({'camera_angle_x':fovx,'frames':train_frames},indent=2))
|
| 55 |
+
(OUT/'transforms_test.json').write_text(json.dumps({'camera_angle_x':fovx,'frames':test_frames},indent=2))
|
| 56 |
+
means=g_ctx.means[0].float(); scales=g_ctx.scales[0].float(); opacity=g_ctx.opacities[0].float().reshape(-1); harm=g_ctx.harmonics[0].float()
|
| 57 |
+
score=opacity*scales.max(dim=1).values.clamp_min(1e-7); n=min(120_000,len(score)); idx=torch.topk(score,n,sorted=False).indices
|
| 58 |
+
xyz=means[idx].cpu().numpy().astype(np.float32); rgb=(0.5+0.28209479177387814*harm[idx,:,0]).clamp(0,1).cpu().numpy()
|
| 59 |
+
verts=np.empty(n,dtype=[('x','f4'),('y','f4'),('z','f4'),('nx','f4'),('ny','f4'),('nz','f4'),('red','u1'),('green','u1'),('blue','u1')])
|
| 60 |
+
verts['x'],verts['y'],verts['z']=xyz.T; verts['nx'],verts['ny'],verts['nz']=0,0,0
|
| 61 |
+
verts['red'],verts['green'],verts['blue']=(rgb*255).astype(np.uint8).T
|
| 62 |
+
PlyData([PlyElement.describe(verts,'vertex')]).write(str(OUT/'points3d.ply'))
|
| 63 |
+
meta={'source':'AnySplat context-only geometry initialization','total_video_frames':len(FRAMES),'context_views':len(train_frames),'heldout_views':len(test_frames),'seed_primitives':n,'image_hw':[H,W],'camera_fov_x':fovx,'pose_alignment_scale':float(scale),'heldout_geometry_used_for_initialization':False}
|
| 64 |
+
(OUT/'seed_metadata.json').write_text(json.dumps(meta,indent=2)); print(json.dumps(meta,indent=2),flush=True)
|