🗺️ Meet LingBot-Map! We've built a feed-forward 3D foundation model for streaming 3D reconstruction! 🏗️🌍
LingBot-Map has focused on:
Geometric Context Transformer: Architecturally unifies coordinate grounding, dense geometric cues, and long-range drift correction within a single streaming framework through anchor context, pose-reference window, and trajectory memory.
High-Efficiency Streaming Inference: A feed-forward architecture with paged KV cache attention, enabling stable inference at ~20 FPS on 518×378 resolution over long sequences exceeding 10,000 frames.
State-of-the-Art Reconstruction: Superior performance on diverse benchmarks compared to both existing streaming and iterative optimization-based approaches.
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.
2026-05-25 — 📊 Evaluation benchmark released. We released the evaluation scripts for KITTI and Oxford Spires — see benchmark/ for the pipeline, and run preprocess/oxford.py to prepare Oxford Spires data before evaluation.
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 for the command, flag rationale, and rendered output.
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.
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.
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.
For other CUDA versions, see PyTorch Get Started.
3. Install lingbot-map
pip install -e .
4. Install FlashInfer (recommended)
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:
--index-url https://pypi.org/simple is only needed if your default pip index is an internal mirror that doesn't have flashinfer-python.
(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/.
See FlashInfer installation for details. If FlashInfer is not installed, the model falls back to SDPA (PyTorch native attention) via --use_sdpa.
This launches an interactive viser viewer at http://localhost:8080. See Interactive Demo below for the full set of scenes and flags, or jump to Offline Rendering Pipeline for long-sequence batch rendering.
🎬 Interactive Demo (demo.py)
Run demo.py for interactive 3D visualization via a browser-based viser viewer (default http://localhost:8080).
Try the Example Scenes
We provide four example scenes in example/ that you can run out of the box:
Sequence is too long for the interactive viser viewer — this clip was rendered with the Offline Rendering Pipeline. See that section for the full command.
We will provide more examples in the follow-up.
Streaming with Keyframe Interval
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.).
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.
Windowed Inference (for long sequences, >3000 frames)
Sky masking uses an ONNX sky segmentation model to filter out sky points from the reconstructed point cloud, which improves visualization quality for outdoor scenes.
Setup:
bash
1# Install onnxruntime (required)2pip install onnxruntime # CPU3# or4pip install onnxruntime-gpu # GPU (faster for large image sets)
The sky segmentation model (skyseg.onnx) will be automatically downloaded from HuggingFace on first use.
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:
If you run into out-of-memory issues, try one (or both) of the following:
--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).
--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.
Faster Inference
Lower the number of iterative refinement steps in the camera head to trade a small amount of pose accuracy for wall-clock speed:
Use this pipeline when your sequence is too long for the interactive viser viewer — for example, the indoor walkthrough featured above. 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.
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:
pip install onnxruntime-gpu
2. Kaolin — matches the PyTorch 2.8.0 + CUDA 12.8 recommended above:
--index-url https://pypi.org/simple bypasses any internal mirror that might otherwise serve the PyPI placeholder wheel (which raises ImportError on import).
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.
3. ffmpeg
sudo apt install ffmpeg # or: brew install ffmpeg
4. CUDA extensions (required before first run)
cd demo_render/render_cuda_ext && python setup.py build_ext --inplace && cd ../..
This builds voxel_morton_ext and frustum_cull_ext in place — both are imported by rgbd_render for GPU voxelization and frustum culling.
Worked Example — long indoor walkthrough (~25 000 frames, 13 minutes)
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.
--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
--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.
--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.
--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.)
--camera_vis default
Overlay the trajectory trail + recent-frame points on the rendered video.
--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.
--frame_tag --frame_tag_position top_right
Stamp a <i> / <N> Frames counter in the top-right corner of the MP4.
--save_predictions
Persist per-frame NPZs alongside the MP4. Useful for inspection or for re-rendering with different camera/overlay settings later.
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.
--image_stride 1
Use every video frame. Increase it to subsample long or high-FPS drive footage.
--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.
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.
Worked Example — LingBot-World scenes
Reconstruct videos generated by LingBot-World, our world model — the same pipeline works on generated footage out of the box.
Dataset: Download the example videos (lingbo_world_frames.mp4, lingbo_world2_frames.mp4) from robbyant/lingbot-map-demo on Hugging Face.
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).
All flags are identical to the 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.
image
image
Camera Path (YAML)
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.
Built-in presets live in demo_render/config/: default.yaml, indoor.yaml, outdoor_drive.yaml. Copy one and edit the camera: block.
YAML structure
yaml
1camera:2fov:60.0# camera field of view in degrees3transition:30# frames blended between adjacent segments4segments:5-mode: follow # chase cam following the input trajectory6frames:[0,1500]# rendered-frame range this segment covers (-1 = end)7back_offset:0.3# how far behind the input camera (fraction of scene scale)8up_offset:0.08# vertical lift above the input camera9look_offset:0.4# how far ahead the lookat target points10smooth_window:30# trajectory smoothing window in frames11-mode: birdeye # rise up for a top-down reveal of the whole scene12frames:[1500,1800]13reveal_height_mult:2.5# birdeye height = scene scale × this factor14-mode: follow # drop back into chase cam15frames:[1800,-1]16back_offset:0.317up_offset:0.0818look_offset:0.4
transition controls how many frames are blended between adjacent segments; frames: [0, -1] means "the whole sequence".
Available modes
mode
Behavior
Tunable fields
follow
Chase cam tracks the input trajectory with smooth offsets. The most cinematic option for walkthroughs.
Follow with birdeye inserts: just list multiple segments in order under segments: — adjacent segments are interpolated using transition frames.
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.
Output files
For a given output name (e.g. <scene> or <video_name>):
File
Description
<name>_pointcloud.mp4
Rendered point-cloud flythrough
<name>_pointcloud_rgb.mp4
Original RGB frames encoded as video
<name>_pointcloud_config.yaml
Full config snapshot of this run
batch_results.json
Per-scene success / duration summary
📜 License
This project is released under the Apache License 2.0. See LICENSE file for details.
📖 Citation
bibtex
1@article{chen2026geometric,
2 title={Geometric Context Transformer for Streaming 3D Reconstruction},
3 author={Chen, Lin-Zhuo and Gao, Jian and Chen, Yihang and Cheng, Ka Leong and Sun, Yipengjing and Hu, Liangxiao and Xue, Nan and Zhu, Xing and Shen, Yujun and Yao, Yao and Xu, Yinghao},
4 journal={arXiv preprint arXiv:2604.14141},
5 year={2026}
6}
✨ Acknowledgments
We thank Shangzhan Zhang, Jianyuan Wang, Yudong Jin, Christian Rupprecht, and Xun Cao for their helpful discussions and support.
This work builds upon several excellent open-source projects: