ONNX
Lin-Zhuo commited on
Commit
204754b
Β·
verified Β·
1 Parent(s): 316b583

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +433 -30
README.md CHANGED
@@ -1,6 +1,6 @@
1
- <div align="center">
2
- <img src="assets/teaser.png" width="100%">
3
 
 
4
  <h1>LingBot-Map: Geometric Context Transformer for Streaming 3D Reconstruction</h1>
5
 
6
  Robbyant Team
@@ -32,9 +32,63 @@ LingBot-Map has focused on:
32
 
33
  ---
34
 
35
- # βš™οΈ Quick Start
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- ## Installation
38
 
39
  **1. Create conda environment**
40
 
@@ -46,9 +100,10 @@ conda activate lingbot-map
46
  **2. Install PyTorch (CUDA 12.8)**
47
 
48
  ```bash
49
- pip install torch==2.9.1 torchvision==0.24.1 --index-url https://download.pytorch.org/whl/cu128
50
  ```
51
 
 
52
  > For other CUDA versions, see [PyTorch Get Started](https://pytorch.org/get-started/locally/).
53
 
54
  **3. Install lingbot-map**
@@ -59,15 +114,15 @@ pip install -e .
59
 
60
  **4. Install FlashInfer (recommended)**
61
 
62
- FlashInfer provides paged KV cache attention for efficient streaming inference:
63
 
64
  ```bash
65
- # CUDA 12.8 + PyTorch 2.9
66
- pip install flashinfer-python -i https://flashinfer.ai/whl/cu128/torch2.9/
67
  ```
68
 
69
- > For other CUDA/PyTorch combinations, see [FlashInfer installation](https://docs.flashinfer.ai/installation.html).
70
- > If FlashInfer is not installed, the model falls back to SDPA (PyTorch native attention) via `--use_sdpa`.
 
71
 
72
  **5. Visualization dependencies (optional)**
73
 
@@ -75,43 +130,129 @@ pip install flashinfer-python -i https://flashinfer.ai/whl/cu128/torch2.9/
75
  pip install -e ".[vis]"
76
  ```
77
 
78
- # πŸ“¦ Model Download
79
 
80
  | Model Name | Huggingface Repository | ModelScope Repository | Description |
81
  | :--- | :--- | :--- | :--- |
82
- | lingbot-map | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | Base model checkpoint (4.63 GB) |
 
 
83
 
84
- # 🎬 Demo
85
 
86
- ### Streaming Inference from Images
 
 
87
 
88
  ```bash
89
- python demo.py --model_path /path/to/checkpoint.pt \
90
- --image_folder /path/to/images/
91
  ```
92
 
93
- ### Streaming Inference from Video
 
 
 
 
 
 
94
 
 
95
  ```bash
96
- python demo.py --model_path /path/to/checkpoint.pt \
97
- --video_path video.mp4 --fps 10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  ```
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  ### Streaming with Keyframe Interval
101
 
102
- Use `--keyframe_interval` to reduce KV cache memory by only keeping every N-th frame as a keyframe. Non-keyframe frames still produce predictions but are not stored in the cache. This is useful for long sequences
103
- which excesses 320 frames.
 
 
 
 
104
 
105
  ```bash
106
- python demo.py --model_path /path/to/checkpoint.pt \
107
- --image_folder /path/to/images/ --keyframe_interval 6
 
 
 
 
108
  ```
109
 
 
 
 
 
 
 
 
 
110
  ### Windowed Inference (for long sequences, >3000 frames)
 
111
  ```bash
112
- python demo.py --model_path /path/to/checkpoint.pt \
113
  --video_path video.mp4 --fps 10 \
114
- --mode windowed --window_size 64
115
  ```
116
 
117
 
@@ -137,20 +278,282 @@ python demo.py --model_path /path/to/checkpoint.pt \
137
  --image_folder /path/to/images/ --mask_sky
138
  ```
139
 
140
- Sky masks are cached in `<image_folder>_sky_masks/` so subsequent runs skip regeneration.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
- ### Without FlashInfer (SDPA fallback)
143
 
144
  ```bash
145
  python demo.py --model_path /path/to/checkpoint.pt \
146
  --image_folder /path/to/images/ --use_sdpa
147
  ```
148
 
149
- # πŸ“œ License
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  This project is released under the Apache License 2.0. See [LICENSE](LICENSE.txt) file for details.
152
 
153
- # πŸ“– Citation
154
 
155
  ```bibtex
156
  @article{chen2026geometric,
@@ -161,7 +564,7 @@ This project is released under the Apache License 2.0. See [LICENSE](LICENSE.txt
161
  }
162
  ```
163
 
164
- # ✨ Acknowledgments
165
 
166
  We thank Shangzhan Zhang, Jianyuan Wang, Yudong Jin, Christian Rupprecht, and Xun Cao for their helpful discussions and support.
167
 
 
1
+ ![image](https://cdn-uploads.huggingface.co/production/uploads/685631e960baba83d051fe31/D5dZEyHqlNc4kC-qH2IkI.png)
 
2
 
3
+ <div align="center">
4
  <h1>LingBot-Map: Geometric Context Transformer for Streaming 3D Reconstruction</h1>
5
 
6
  Robbyant Team
 
32
 
33
  ---
34
 
35
+ ## πŸ“‘ Table of Contents
36
+
37
+ <details>
38
+ <summary>Click to expand</summary>
39
+
40
+ - [πŸ“° News](#-news)
41
+ - [πŸ“‹ TODO](#-todo)
42
+ - [βš™οΈ Installation](#️-installation)
43
+ - [πŸ“¦ Model Download](#-model-download)
44
+ - [πŸš€ Quick Start](#-quick-start)
45
+ - [🎬 Interactive Demo (`demo.py`)](#-interactive-demo-demopy)
46
+ - [Try the Example Scenes](#try-the-example-scenes)
47
+ - [Streaming with Keyframe Interval](#streaming-with-keyframe-interval)
48
+ - [Windowed Inference (for long sequences, >3000 frames)](#windowed-inference-for-long-sequences-3000-frames)
49
+ - [Sky Masking](#sky-masking)
50
+ - [Visualization Options](#visualization-options)
51
+ - [Performance & Memory](#performance--memory)
52
+ - [πŸŽ₯ Offline Rendering Pipeline (`demo_render/batch_demo.py`)](#-offline-rendering-pipeline-demo_renderbatch_demopy)
53
+ - [πŸ“œ License](#-license)
54
+ - [πŸ“– Citation](#-citation)
55
+ - [✨ Acknowledgments](#-acknowledgments)
56
+
57
+ </details>
58
+
59
+ ---
60
+
61
+ ## πŸ“° News
62
+
63
+ - **2026-06-28** β€” Fixed an SDPA KV cache bug. **The SDPA backend now performs better on long sequences**. We still recommend the FlashInfer backend for the best performance.
64
+ - **2026-05-25** β€” πŸ“Š **Evaluation benchmark released**. We released the evaluation scripts for KITTI and Oxford Spires β€” see [benchmark/](benchmark/) for the pipeline, and run [`preprocess/oxford.py`](preprocess/oxford.py) to prepare Oxford Spires data before evaluation.
65
+ - **2026-04-29** β€” πŸ“Ή **Long-video demo released**. We released a very-long-video example (~25 000 frames, 13-minute indoor walkthrough) rendered with the offline pipeline β€” see [Worked Example](#worked-example--long-indoor-walkthrough-25-000-frames-13-minutes) for the command, flag rationale, and rendered output.
66
+ - **2026-04-27** β€” πŸš€ **LingBot-Map accelerated**. Pull the latest `main` and run `python demo.py --compile ...` or `python gct_profile.py --backend flashinfer --dtype bf16 --compile` to verify on your hardware.
67
+ - **2026-04-24** β€” Fixed a FlashInfer KV cache bug where `--keyframe_interval > 1` silently cached non-keyframes. **You should now see better pose and reconstruction quality when running with more than 320 frames**.
68
+
69
+ ---
70
+
71
+ ## πŸ“‹ TODO
72
+
73
+ - βœ… Release evaluation benchmark
74
+ - βœ… Oxford Spires dataset
75
+ - βœ… KITTI dataset
76
+ - βœ… VBR dataset
77
+ - βœ… Droid-W dataset
78
+ - βœ… TUM-D dataset
79
+ - βœ… 7-scenes dataset
80
+ - βœ… ETH3D dataset
81
+ - βœ… Tanks and Temples dataset
82
+ - βœ… NRGBD dataset
83
+ - βœ… Release demo scripts
84
+ - βœ… Indoor long-video demo ([Featured indoor walkthrough](#-featured-indoor-walkthrough-25-000-frames-13-minutes))
85
+ - βœ… Outdoor long-video demo
86
+ - βœ… LingBot-World demo ([Worked example](#worked-example--lingbot-world-scenes))
87
+ - βœ… Aerial long-video demo
88
+
89
+ ---
90
 
91
+ ## βš™οΈ Installation
92
 
93
  **1. Create conda environment**
94
 
 
100
  **2. Install PyTorch (CUDA 12.8)**
101
 
102
  ```bash
103
+ pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128
104
  ```
105
 
106
+ > PyTorch 2.8.0 is the recommended version because NVIDIA Kaolin (required by the batch rendering pipeline) has prebuilt wheels for `torch-2.8.0_cu128`. If you only need `demo.py` you may use a newer PyTorch, but the batch renderer then requires building Kaolin from source.
107
  > For other CUDA versions, see [PyTorch Get Started](https://pytorch.org/get-started/locally/).
108
 
109
  **3. Install lingbot-map**
 
114
 
115
  **4. Install FlashInfer (recommended)**
116
 
117
+ FlashInfer provides paged KV cache attention for efficient streaming inference. It is a pure-Python package that JIT-compiles CUDA kernels on first use, so a single wheel works across CUDA/PyTorch versions:
118
 
119
  ```bash
120
+ pip install --index-url https://pypi.org/simple flashinfer-python
 
121
  ```
122
 
123
+ > `--index-url https://pypi.org/simple` is only needed if your default pip index is an internal mirror that doesn't have `flashinfer-python`.
124
+ > (Optional) For faster first-use, you can additionally install a CUDA-specific JIT cache: `pip install flashinfer-jit-cache -f https://flashinfer.ai/whl/cu128/flashinfer-jit-cache/`.
125
+ > See [FlashInfer installation](https://docs.flashinfer.ai/installation.html) for details. If FlashInfer is not installed, the model falls back to SDPA (PyTorch native attention) via `--use_sdpa`.
126
 
127
  **5. Visualization dependencies (optional)**
128
 
 
130
  pip install -e ".[vis]"
131
  ```
132
 
133
+ ## πŸ“¦ Model Download
134
 
135
  | Model Name | Huggingface Repository | ModelScope Repository | Description |
136
  | :--- | :--- | :--- | :--- |
137
+ | lingbot-map-long | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | Better suited for long sequences and large scale scenes. |
138
+ | lingbot-map | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | Balanced checkpoint (used in paper, benchmark and offline demo) β€” trade off all-around performance across short and long sequences. |
139
+ | lingbot-map-stage1 | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | Stage-1 training checkpoint of lingbot-map β€” can be loaded into the VGGT model for bidirectional inference (c2w). |
140
 
141
+ > 🚧 **Coming soon:** we're training an stronger model that supports longer sequences β€” stay tuned.
142
 
143
+ ## πŸš€ Quick Start
144
+
145
+ After installation, run your first scene with one command:
146
 
147
  ```bash
148
+ python demo.py --model_path /path/to/lingbot-map-long.pt \
149
+ --image_folder example/courthouse --mask_sky
150
  ```
151
 
152
+ This launches an interactive [viser](https://github.com/nerfstudio-project/viser) viewer at `http://localhost:8080`. See [Interactive Demo](#-interactive-demo-demopy) below for the full set of scenes and flags, or jump to [Offline Rendering Pipeline](#-offline-rendering-pipeline-demo_renderbatch_demopy) for long-sequence batch rendering.
153
+
154
+ ## 🎬 Interactive Demo (`demo.py`)
155
+
156
+ Run `demo.py` for interactive 3D visualization via a browser-based [viser](https://github.com/nerfstudio-project/viser) viewer (default `http://localhost:8080`).
157
+
158
+ ### Try the Example Scenes
159
 
160
+ We provide four example scenes in `example/` that you can run out of the box:
161
  ```bash
162
+ # courthouse scene
163
+ python demo.py --model_path /path/to/lingbot-map-long.pt \
164
+ --image_folder example/courthouse --mask_sky
165
+ ```
166
+
167
+
168
+ https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8
169
+
170
+
171
+
172
+
173
+
174
+
175
+ ```bash
176
+ # University scene
177
+ python demo.py --model_path /path/to/lingbot-map-long.pt \
178
+ --image_folder example/university --mask_sky
179
+ ```
180
+
181
+
182
+ https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+ ```bash
191
+ # Loop scene (loop closure trajectory)
192
+ python demo.py --model_path /path/to/lingbot-map-long.pt \
193
+ --image_folder example/loop
194
+ ```
195
+
196
+
197
+ https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75
198
+
199
+
200
+
201
+
202
+
203
+ ```bash
204
+ # Oxford scene with sky masking (outdoor, large scale scene)
205
+ python demo.py --model_path /path/to/lingbot-map-long.pt \
206
+ --image_folder example/oxford --mask_sky
207
  ```
208
 
209
+
210
+ https://github.com/user-attachments/assets/6b8daa95-9ed4-40b2-9902-7435779b886d
211
+
212
+
213
+
214
+
215
+
216
+
217
+ #### 🎯 Featured: indoor walkthrough (~25 000 frames, 13 minutes)
218
+
219
+
220
+ *Sequence is too long for the interactive viser viewer β€” this clip was rendered with the [Offline Rendering Pipeline](#-offline-rendering-pipeline-demo_renderbatch_demopy). See that section for the full command.*
221
+
222
+ We will provide more examples in the follow-up.
223
+
224
  ### Streaming with Keyframe Interval
225
 
226
+ Use `--keyframe_interval` to reduce KV cache memory by only keeping every N-th frame as a keyframe. Non-keyframe frames still produce predictions but are not stored in the cache. This is useful for long sequences which exceed 320 frames (We train with video RoPE on 320 views, so performance degrades when the KV cache stores more than 320 views. Using a keyframe strategy allows inference over longer sequences.).
227
+
228
+
229
+ **Dataset:** Download the demo sequences from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face.
230
+
231
+ Example run on the `travel` sequence from the dataset above (sky masking on, 4 camera optimization iterations, keyframe every 2 frames):
232
 
233
  ```bash
234
+ python demo.py \
235
+ --image_folder /path/to/lingbot-map-demo/travel/ \
236
+ --model_path /path/to/lingbot-map-long.pt \
237
+ --mask_sky \
238
+ --camera_num_iterations 4 \
239
+ --keyframe_interval 2
240
  ```
241
 
242
+
243
+ https://github.com/user-attachments/assets/d350b590-d036-4363-af8c-7af3918338ef
244
+
245
+
246
+
247
+ > **Note on inference range.** Our method does not perform state resetting by default, so the maximum inference range is bounded by the longest distance seen during training on the dataset. Beyond that distance, state resetting becomes necessary. If you observe pose collapse, switch to windowed mode (`--mode windowed`) β€” in most cases tuning `--keyframe_interval` alone is enough and the rest of the windowed parameters can stay at their defaults.
248
+
249
+
250
  ### Windowed Inference (for long sequences, >3000 frames)
251
+
252
  ```bash
253
+ python demo.py --model_path /path/to/lingbot-map-long.pt \
254
  --video_path video.mp4 --fps 10 \
255
+ --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2
256
  ```
257
 
258
 
 
278
  --image_folder /path/to/images/ --mask_sky
279
  ```
280
 
281
+ Sky masks are cached in `<image_folder>_sky_masks/` so subsequent runs skip regeneration. You can also specify a custom cache directory with `--sky_mask_dir`, or save side-by-side mask visualizations with `--sky_mask_visualization_dir`:
282
+
283
+ ```bash
284
+ python demo.py --model_path /path/to/checkpoint.pt \
285
+ --image_folder /path/to/images/ --mask_sky \
286
+ --sky_mask_dir /path/to/cached_masks/ \
287
+ --sky_mask_visualization_dir /path/to/mask_viz/
288
+ ```
289
+
290
+ ### Visualization Options
291
+
292
+ | Argument | Default | Description |
293
+ |:---|:---|:---|
294
+ | `--port` | `8080` | Viser viewer port |
295
+ | `--conf_threshold` | `1.5` | Visibility threshold for filtering low-confidence points |
296
+ | `--point_size` | `0.00001` | Point cloud point size |
297
+ | `--downsample_factor` | `10` | Spatial downsampling for point cloud display |
298
+
299
+ ### Performance & Memory
300
 
301
+ #### Without FlashInfer (SDPA fallback)
302
 
303
  ```bash
304
  python demo.py --model_path /path/to/checkpoint.pt \
305
  --image_folder /path/to/images/ --use_sdpa
306
  ```
307
 
308
+ #### Running on Limited GPU Memory
309
+
310
+ If you run into out-of-memory issues, try one (or both) of the following:
311
+
312
+ - **`--offload_to_cpu`** β€” offload per-frame predictions to CPU during inference (on by default; use `--no-offload_to_cpu` only if you have memory to spare).
313
+ - **`--num_scale_frames 2`** β€” reduce the number of bidirectional scale frames from the default 8 down to 2, which shrinks the activation peak of the initial scale phase.
314
+
315
+ #### Faster Inference
316
+
317
+ Lower the number of iterative refinement steps in the camera head to trade a small amount of pose accuracy for wall-clock speed:
318
+
319
+ ```bash
320
+ python demo.py --model_path /path/to/checkpoint.pt \
321
+ --image_folder /path/to/images/ --camera_num_iterations 1
322
+ ```
323
+
324
+ `--camera_num_iterations` defaults to `4`; setting it to `1` skips three refinement passes in the camera head (and shrinks its KV cache by 4Γ—).
325
+
326
+ ## πŸŽ₯ Offline Rendering Pipeline (`demo_render/batch_demo.py`)
327
+
328
+ Use this pipeline when your sequence is too long for the interactive viser viewer β€” for example, the [indoor walkthrough featured above](#-featured-indoor-walkthrough-25-000-frames-13-minutes). `demo_render/batch_demo.py` is the all-in-one offline entry point: feed it a video or a folder of images and it will run model inference and produce a headless point-cloud flythrough MP4 in a single command. It shares the same PyTorch / FlashInfer / checkpoint stack as `demo.py`.
329
+
330
+ For those constrained by limited VRAM or GPU usage, you may also refer to the implementation at: https://github.com/ureeey/lingbot-map-rtx4060-8g/commit/eeee84a89cc97c1e39b736b46df4ee315275700b
331
+
332
+ ### Install (extends the main install)
333
+
334
+ **1. Rendering Python dependencies**
335
+
336
+ ```bash
337
+ pip install -e ".[vis,render]"
338
+ ```
339
+
340
+ `render` pulls in `open3d>=0.19` and `pyyaml` (the core `numpy<2` constraint comes from the base `lingbot-map` install). Sky masking in this pipeline uses `onnxruntime-gpu` for batched segmentation; install it if you don't already have the CPU `onnxruntime`:
341
+
342
+ ```bash
343
+ pip install onnxruntime-gpu
344
+ ```
345
+
346
+ **2. Kaolin** β€” matches the PyTorch 2.8.0 + CUDA 12.8 recommended above:
347
+
348
+ ```bash
349
+ pip install --index-url https://pypi.org/simple \
350
+ kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html
351
+ ```
352
+
353
+ > `--index-url https://pypi.org/simple` bypasses any internal mirror that might otherwise serve the PyPI placeholder wheel (which raises `ImportError` on import).
354
+ > NVIDIA Kaolin does not publish prebuilt wheels for PyTorch 2.9.x β€” if you're on 2.9 for other reasons, build Kaolin from source (`pip install --no-build-isolation git+https://github.com/NVIDIAGameWorks/kaolin.git`, needs local CUDA toolkit). For other torch/CUDA combinations see [NVIDIA Kaolin installation](https://kaolin.readthedocs.io/en/latest/notes/installation.html).
355
+
356
+ **3. ffmpeg**
357
+
358
+ ```bash
359
+ sudo apt install ffmpeg # or: brew install ffmpeg
360
+ ```
361
+
362
+ **4. CUDA extensions** (required before first run)
363
+
364
+ ```bash
365
+ cd demo_render/render_cuda_ext && python setup.py build_ext --inplace && cd ../..
366
+ ```
367
+
368
+ This builds `voxel_morton_ext` and `frustum_cull_ext` in place β€” both are imported by `rgbd_render` for GPU voxelization and frustum culling.
369
+
370
+ ### Worked Example β€” long indoor walkthrough (~25 000 frames, 13 minutes)
371
+
372
+ **Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face.
373
+
374
+ ```bash
375
+ python demo_render/batch_demo.py \
376
+ --video_path /data/demo_videos/indoor_travel.MP4 \
377
+ --output_folder /data/outputs/indoor_travel/ \
378
+ --model_path /path/to/lingbot-map.pt \
379
+ --config demo_render/config/indoor.yaml \
380
+ --mode windowed --window_size 128 \
381
+ --keyframe_interval 13 --overlap_keyframes 8 \
382
+ --sky_mask_dir /data/outputs/sky_masks \
383
+ --sky_mask_visualization_dir /data/outputs/sky_mask_viz \
384
+ --camera_vis default --keyframes_only_points \
385
+ --frame_tag --frame_tag_position top_right \
386
+ --save_predictions
387
+ ```
388
+
389
+ <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/f4f5e555-22a8-4cc9-b380-dfde5fe1c809" />
390
+
391
+ Flag-by-flag rationale:
392
+
393
+ | Flag | Why it's there |
394
+ |---|---|
395
+ | `--mode windowed --window_size 128` | Sliding-window inference is required once the sequence exceeds the ~320-frame RoPE training range; each window resets the KV cache. **`window_size` counts KV-cache slots, not actual frames** β€” the first `num_scale_frames` (=8) slots hold the scale frames and the remaining `128 βˆ’ 8 = 120` slots hold keyframes. With `keyframe_interval = 13`, one window therefore covers `8 + 120 Γ— 13 = 1568` actual frames. |
396
+ | `--keyframe_interval 13` | Cache only every 13th frame as a keyframe. Non-keyframes still emit per-frame predictions but don't grow the KV cache|
397
+ | `--overlap_keyframes 8` | Adjacent windows share 8 keyframes of context, resolved internally to `max(num_scale_frames, 8 Γ— keyframe_interval) = 8 Γ— 13 = 104` actual frames of overlap. Recommended whenever `keyframe_interval > 1`, to keep cross-window pose alignment stable. |
398
+ | `--config demo_render/config/indoor.yaml` | Seed render/scene/camera/overlay defaults from the indoor preset (short depth, tighter follow cam). Any CLI flag the user explicitly passes still overrides the YAML value. |
399
+ | `--sky_mask_dir` / `--sky_mask_visualization_dir` | Persist sky masks and their side-by-side visualizations to disk so subsequent reruns reuse them instead of re-running ONNX segmentation. (The render pipeline only consumes them when sky masking is enabled β€” by the YAML preset or by `--mask_sky`.) |
400
+ | `--camera_vis default` | Overlay the trajectory trail + recent-frame points on the rendered video. |
401
+ | `--keyframes_only_points` | Only unproject keyframe depth into the point cloud; non-keyframes still contribute their pose to the trajectory/frustum overlay. Keeps the cloud sparse for very long sequences. |
402
+ | `--frame_tag --frame_tag_position top_right` | Stamp a `<i> / <N> Frames` counter in the top-right corner of the MP4. |
403
+ | `--save_predictions` | Persist per-frame NPZs alongside the MP4. Useful for inspection or for re-rendering with different camera/overlay settings later. |
404
+
405
+ ### Worked Example β€” outdoor drive scene
406
+
407
+ **Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face.
408
+
409
+ ```bash
410
+ python demo_render/batch_demo.py \
411
+ --video_path /data/demo_videos/drive_frames.mp4 \
412
+ --output_folder /data/outputs/drive/ \
413
+ --model_path /path/to/lingbot-map.pt \
414
+ --config demo_render/config/outdoor_drive.yaml \
415
+ --mode windowed --window_size 128 \
416
+ --max_non_keyframe_gap 100 --overlap_keyframes 8 \
417
+ --image_stride 1 \
418
+ --sky_mask_dir /data/outputs/sky_masks \
419
+ --sky_mask_visualization_dir /data/outputs/sky_mask_viz \
420
+ --camera_vis default --keyframes_only_points \
421
+ --frame_tag --frame_tag_position top_right \
422
+ --save_predictions
423
+ ```
424
+
425
+ <img width="3822" height="1080" alt="image" src="https://github.com/user-attachments/assets/3c26afdb-6bb8-4d20-a7e0-f5a220382662" />
426
+
427
+
428
+ What differs from the indoor walkthrough above:
429
+
430
+ | Flag | Why it's there |
431
+ |---|---|
432
+ | `--config demo_render/config/outdoor_drive.yaml` | Seed defaults from the outdoor preset: sky masking enabled, deeper render range (`max_depth: 250`), and a follow cam tuned for vehicle trajectories with a final birdeye reveal. |
433
+ | `--image_stride 1` | Use every video frame. Increase it to subsample long or high-FPS drive footage. |
434
+ | `--max_non_keyframe_gap 100` | Upper bound on consecutive non-keyframes before a keyframe is forced. Only active with flow-based keyframe selection (`--flow_threshold > 0`); in the default fixed-interval mode it has no effect. |
435
+
436
+ The remaining flags (`--mode windowed --window_size 128`, `--overlap_keyframes 8`, sky-mask caching, overlays, `--save_predictions`) carry over unchanged from the indoor example β€” see the flag-by-flag table above.
437
+
438
+ ### Worked Example β€” LingBot-World scenes
439
+
440
+ Reconstruct videos generated by LingBot-World, our world model β€” the same pipeline works on generated footage out of the box.
441
+
442
+ **Dataset:** Download the example videos (`lingbo_world_frames.mp4`, `lingbo_world2_frames.mp4`) from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face.
443
+
444
+ ```bash
445
+ python demo_render/batch_demo.py \
446
+ --video_path /data/demo_videos/lingbo_world_frames.mp4 \
447
+ --output_folder /data/outputs/lingbo_world/ \
448
+ --model_path /path/to/lingbot-map.pt \
449
+ --config demo_render/config/outdoor_drive.yaml \
450
+ --mode windowed --window_size 128 \
451
+ --max_non_keyframe_gap 100 --overlap_keyframes 8 \
452
+ --image_stride 1 \
453
+ --sky_mask_dir /data/outputs/sky_masks \
454
+ --sky_mask_visualization_dir /data/outputs/sky_mask_viz \
455
+ --camera_vis default --keyframes_only_points \
456
+ --frame_tag --frame_tag_position top_right \
457
+ --save_predictions
458
+ ```
459
+
460
+ For the second clip, run the same command with `--video_path /data/demo_videos/lingbo_world2_frames.mp4 --output_folder /data/outputs/lingbo_world2/` (and separate `--sky_mask_dir` / `--sky_mask_visualization_dir` folders if you want to keep the cached masks apart).
461
+
462
+ All flags are identical to the [outdoor drive scene](#worked-example--outdoor-drive-scene) above β€” only the input video and output folder change. See the drive scene and indoor walkthrough tables for the flag-by-flag rationale.
463
+
464
+ <img width="3736" height="1080" alt="image" src="https://github.com/user-attachments/assets/1f60d505-1407-482c-9b5d-57c7145c0b7d" />
465
+
466
+ <img width="1200" height="339" alt="image" src="https://github.com/user-attachments/assets/e62bedaa-1e61-40b3-8fea-01c8a15355f0" />
467
+
468
+
469
+
470
+ ### Camera Path (YAML)
471
+
472
+ The virtual camera path is described by the `camera.segments` list in the YAML preset passed via `--config`. Edit the YAML to design your own shot β€” no need to touch CLI flags.
473
+
474
+ Built-in presets live in `demo_render/config/`: `default.yaml`, `indoor.yaml`, `outdoor_drive.yaml`. Copy one and edit the `camera:` block.
475
+
476
+ #### YAML structure
477
+
478
+ ```yaml
479
+ camera:
480
+ fov: 60.0 # camera field of view in degrees
481
+ transition: 30 # frames blended between adjacent segments
482
+ segments:
483
+ - mode: follow # chase cam following the input trajectory
484
+ frames: [0, 1500] # rendered-frame range this segment covers (-1 = end)
485
+ back_offset: 0.3 # how far behind the input camera (fraction of scene scale)
486
+ up_offset: 0.08 # vertical lift above the input camera
487
+ look_offset: 0.4 # how far ahead the lookat target points
488
+ smooth_window: 30 # trajectory smoothing window in frames
489
+ - mode: birdeye # rise up for a top-down reveal of the whole scene
490
+ frames: [1500, 1800]
491
+ reveal_height_mult: 2.5 # birdeye height = scene scale Γ— this factor
492
+ - mode: follow # drop back into chase cam
493
+ frames: [1800, -1]
494
+ back_offset: 0.3
495
+ up_offset: 0.08
496
+ look_offset: 0.4
497
+ ```
498
+
499
+ `transition` controls how many frames are blended between adjacent segments; `frames: [0, -1]` means "the whole sequence".
500
+
501
+ #### Available modes
502
+
503
+ | `mode` | Behavior | Tunable fields |
504
+ |---|---|---|
505
+ | `follow` | Chase cam tracks the input trajectory with smooth offsets. The most cinematic option for walkthroughs. | `back_offset`, `up_offset`, `look_offset`, `smooth_window`, `scale_frames` |
506
+ | `birdeye` | Top-down reveal of the whole scene. Useful for hero / overview shots. | `reveal_height_mult` |
507
+ | `static` | Fixed eye + lookat, auto-derived from the segment's start frame. | β€” |
508
+ | `pivot` | Fixed eye, lookat sweeps along the trajectory. | β€” |
509
+
510
+ #### Single-shot YAML examples
511
+
512
+ **Pure follow** (most common):
513
+
514
+ ```yaml
515
+ camera:
516
+ fov: 60.0
517
+ segments:
518
+ - mode: follow
519
+ frames: [0, -1]
520
+ back_offset: 0.3
521
+ up_offset: 0.08
522
+ look_offset: 0.4
523
+ smooth_window: 30
524
+ ```
525
+
526
+ **Full birdeye** (good for overview / hero shots):
527
+
528
+ ```yaml
529
+ camera:
530
+ fov: 60.0
531
+ segments:
532
+ - mode: birdeye
533
+ frames: [0, -1]
534
+ reveal_height_mult: 2.5
535
+ ```
536
+
537
+ **Follow with birdeye inserts**: just list multiple segments in order under `segments:` β€” adjacent segments are interpolated using `transition` frames.
538
+
539
+ > Caveat: when `--config` loads a YAML preset, passing **any** segment-shaping CLI flag (`--camera_mode`, `--back_offset`, `--up_offset`, `--look_offset`, `--smooth_window`, `--follow_scale_frames`, `--birdeye_start`, `--birdeye_duration`, `--reveal_height_mult`) discards the YAML's `segments` and rebuilds the camera path from those flags instead. To stay fully YAML-driven, don't pass any of them on the command line.
540
+
541
+ ### Output files
542
+
543
+ For a given output name (e.g. `<scene>` or `<video_name>`):
544
+
545
+ | File | Description |
546
+ |------|-------------|
547
+ | `<name>_pointcloud.mp4` | Rendered point-cloud flythrough |
548
+ | `<name>_pointcloud_rgb.mp4` | Original RGB frames encoded as video |
549
+ | `<name>_pointcloud_config.yaml` | Full config snapshot of this run |
550
+ | `batch_results.json` | Per-scene success / duration summary |
551
+
552
+ ## πŸ“œ License
553
 
554
  This project is released under the Apache License 2.0. See [LICENSE](LICENSE.txt) file for details.
555
 
556
+ ## πŸ“– Citation
557
 
558
  ```bibtex
559
  @article{chen2026geometric,
 
564
  }
565
  ```
566
 
567
+ ## ✨ Acknowledgments
568
 
569
  We thank Shangzhan Zhang, Jianyuan Wang, Yudong Jin, Christian Rupprecht, and Xun Cao for their helpful discussions and support.
570