From 6a8db0f469d3e5778a9119cb0e3ac35afe008878 Mon Sep 17 00:00:00 2001 From: tura-ai-agent Date: Thu, 23 Jul 2026 00:22:42 +0200 Subject: [PATCH 1/2] docs: add Chinese and Japanese README translations Add complete Simplified Chinese and Japanese README translations with language links. --- README.ja.md | 590 ++++++++++++++++++++++++ README.md | 1170 ++++++++++++++++++++++++----------------------- README.zh-CN.md | 590 ++++++++++++++++++++++++ 3 files changed, 1766 insertions(+), 584 deletions(-) create mode 100644 README.ja.md create mode 100644 README.zh-CN.md diff --git a/README.ja.md b/README.ja.md new file mode 100644 index 0000000..bae9a4a --- /dev/null +++ b/README.ja.md @@ -0,0 +1,590 @@ +
+ + +

LingBot-Map:ストリーミング 3D 再構成のための幾何コンテキスト Transformer

+ +Robbyant Team + +[English](README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) + +
+ +
+ +[![Paper](https://img.shields.io/static/v1?label=Paper&message=arXiv&color=red&logo=arxiv)](https://arxiv.org/abs/2604.14141) +[![PDF](https://img.shields.io/static/v1?label=Paper&message=PDF&color=red&logo=adobeacrobatreader)](lingbot-map_paper.pdf) +[![Project](https://img.shields.io/badge/Project-Website-blue)](https://technology.robbyant.com/lingbot-map) +[![HuggingFace](https://img.shields.io/static/v1?label=%F0%9F%A4%97%20Model&message=HuggingFace&color=orange)](https://huggingface.co/robbyant/lingbot-map) +[![ModelScope](https://img.shields.io/static/v1?label=%F0%9F%A4%96%20Model&message=ModelScope&color=purple)](https://www.modelscope.cn/models/Robbyant/lingbot-map) +[![License](https://img.shields.io/badge/License-Apache--2.0-green)](LICENSE.txt) + +
+ +https://github.com/user-attachments/assets/fe39e095-af2c-4ec9-b68d-a8ba97e505ab + +----- + +### 🗺️ LingBot-Map を紹介します!ストリーミング 3D 再構成のためのフィードフォワード型 3D 基盤モデルです!🏗️🌍 + +LingBot-Map は次の点に重点を置いています。 + +- **幾何コンテキスト Transformer**:アンカーコンテキスト、姿勢参照ウィンドウ、軌跡メモリを用い、座標グラウンディング、密な幾何学的手がかり、長距離ドリフト補正を単一のストリーミングフレームワーク内でアーキテクチャとして統合します。 +- **高効率なストリーミング推論**:ページ化 KV キャッシュアテンションを備えたフィードフォワードアーキテクチャにより、518×378 の解像度で 10,000 フレームを超える長いシーケンスに対して約 20 FPS の安定した推論を実現します。 +- **最先端の再構成性能**:既存のストリーミング手法および反復最適化ベースの手法と比べ、さまざまなベンチマークで優れた性能を発揮します。 + +--- + +## 📑 目次 + +
+クリックして展開 + +- [📰 最新情報](#-最新情報) +- [📋 TODO](#-todo) +- [⚙️ インストール](#️-インストール) +- [📦 モデルのダウンロード](#-モデルのダウンロード) +- [🚀 クイックスタート](#-クイックスタート) +- [🎬 インタラクティブデモ(`demo.py`)](#-インタラクティブデモdemopy) + - [サンプルシーンを試す](#サンプルシーンを試す) + - [キーフレーム間隔を用いたストリーミング](#キーフレーム間隔を用いたストリーミング) + - [ウィンドウ推論(3000 フレームを超える長いシーケンス向け)](#ウィンドウ推論3000-フレームを超える長いシーケンス向け) + - [空マスク](#空マスク) + - [可視化オプション](#可視化オプション) + - [パフォーマンスとメモリ](#パフォーマンスとメモリ) +- [🎥 オフラインレンダリングパイプライン(`demo_render/batch_demo.py`)](#-オフラインレンダリングパイプラインdemo_renderbatch_demopy) +- [📜 ライセンス](#-ライセンス) +- [📖 引用](#-引用) +- [✨ 謝辞](#-謝辞) + +
+ +--- + +## 📰 最新情報 + +- **2026-06-28** — SDPA KV キャッシュの不具合を修正しました。**SDPA バックエンドは長いシーケンスでより高い性能を発揮するようになりました**。最高の性能を得るには、引き続き FlashInfer バックエンドを推奨します。 +- **2026-05-25** — 📊 **評価ベンチマークを公開しました**。KITTI と Oxford Spires の評価スクリプトを公開しました。パイプラインは [benchmark/](benchmark/) を参照し、評価前に [`preprocess/oxford.py`](preprocess/oxford.py) を実行して Oxford Spires のデータを準備してください。 +- **2026-04-29** — 📹 **長時間動画のデモを公開しました**。オフラインパイプラインでレンダリングした非常に長い動画の例(約 25,000 フレーム、13 分間の屋内ウォークスルー)を公開しました。コマンド、フラグの理由、レンダリング結果は[実行例](#実行例-長時間の屋内ウォークスルー約-25000-フレーム13-分)を参照してください。 +- **2026-04-27** — 🚀 **LingBot-Map を高速化しました**。最新の `main` を取得し、`python demo.py --compile ...` または `python gct_profile.py --backend flashinfer --dtype bf16 --compile` を実行して、お使いのハードウェアで確認してください。 +- **2026-04-24** — `--keyframe_interval > 1` のときに非キーフレームが暗黙にキャッシュされる FlashInfer KV キャッシュの不具合を修正しました。**320 フレームを超えて実行する場合の姿勢および再構成品質が改善されます**。 + +--- + +## 📋 TODO + +- ✅ 評価ベンチマークを公開 + - ✅ Oxford Spires データセット + - ✅ KITTI データセット + - ✅ VBR データセット + - ✅ Droid-W データセット + - ✅ TUM-D データセット + - ✅ 7-scenes データセット + - ✅ ETH3D データセット + - ✅ Tanks and Temples データセット + - ✅ NRGBD データセット +- ✅ デモスクリプトを公開 + - ✅ 屋内の長時間動画デモ([注目の屋内ウォークスルー](#-注目屋内ウォークスルー約-25000-フレーム13-分)) + - ✅ 屋外の長時間動画デモ + - ✅ LingBot-World デモ([実行例](#実行例-lingbot-world-のシーン)) + - ✅ 空撮の長時間動画デモ + +--- + +## ⚙️ インストール + +**1. conda 環境を作成** + +```bash +conda create -n lingbot-map python=3.10 -y +conda activate lingbot-map +``` + +**2. PyTorch(CUDA 12.8)をインストール** + +```bash +pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128 +``` + +> PyTorch 2.8.0 を推奨します。バッチレンダリングパイプラインに必要な NVIDIA Kaolin が `torch-2.8.0_cu128` 用のビルド済み wheel を提供しているためです。`demo.py` のみを使用する場合は新しい PyTorch でも構いませんが、その場合、バッチレンダラーでは Kaolin をソースからビルドする必要があります。 +> その他の CUDA バージョンについては、[PyTorch Get Started](https://pytorch.org/get-started/locally/) を参照してください。 + +**3. lingbot-map をインストール** + +```bash +pip install -e . +``` + +**4. FlashInfer をインストール(推奨)** + +FlashInfer は、効率的なストリーミング推論のためのページ化 KV キャッシュアテンションを提供します。初回使用時に CUDA カーネルを JIT コンパイルする純粋な Python パッケージなので、1 つの wheel で複数の CUDA/PyTorch バージョンに対応できます。 + +```bash +pip install --index-url https://pypi.org/simple flashinfer-python +``` + +> `--index-url https://pypi.org/simple` が必要なのは、デフォルトの pip インデックスが `flashinfer-python` を含まない内部ミラーである場合のみです。 +> (任意)初回使用を高速化するため、CUDA 専用の JIT キャッシュもインストールできます:`pip install flashinfer-jit-cache -f https://flashinfer.ai/whl/cu128/flashinfer-jit-cache/`。 +> 詳細は [FlashInfer のインストール](https://docs.flashinfer.ai/installation.html) を参照してください。FlashInfer がインストールされていない場合、`--use_sdpa` によって SDPA(PyTorch ネイティブアテンション)へフォールバックします。 + +**5. 可視化用の依存関係(任意)** + +```bash +pip install -e ".[vis]" +``` + +## 📦 モデルのダウンロード + +| モデル名 | Hugging Face リポジトリ | ModelScope リポジトリ | 説明 | +| :--- | :--- | :--- | :--- | +| lingbot-map-long | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | 長いシーケンスや大規模なシーンに適しています。 | +| lingbot-map | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | バランス型チェックポイント(論文、ベンチマーク、オフラインデモで使用)。短いシーケンスと長いシーケンスの双方で総合的な性能を両立します。 | +| lingbot-map-stage1 | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | lingbot-map のステージ 1 学習チェックポイント。VGGT モデルへ読み込み、双方向推論(c2w)に使用できます。 | + +> 🚧 **近日公開:**より長いシーケンスに対応する、さらに強力なモデルを学習中です。続報をお待ちください。 + +## 🚀 クイックスタート + +インストール後、次の 1 コマンドで最初のシーンを実行します。 + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + +これにより、`http://localhost:8080` でインタラクティブな [viser](https://github.com/nerfstudio-project/viser) ビューアーが起動します。シーンとフラグの全一覧は下記の[インタラクティブデモ](#-インタラクティブデモdemopy)を、長いシーケンスのバッチレンダリングは[オフラインレンダリングパイプライン](#-オフラインレンダリングパイプラインdemo_renderbatch_demopy)を参照してください。 + +## 🎬 インタラクティブデモ(`demo.py`) + +`demo.py` を実行すると、ブラウザベースの [viser](https://github.com/nerfstudio-project/viser) ビューアー(デフォルトは `http://localhost:8080`)でインタラクティブな 3D 可視化を行えます。 + +### サンプルシーンを試す + +`example/` には、そのまま実行できる 3 つのサンプルシーンが用意されています。 +```bash +# courthouse scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + + +https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8 + + + + + + +```bash +# University scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/university --mask_sky +``` + + +https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57 + + + + + + + +```bash +# Loop scene (loop closure trajectory) +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/loop +``` + + +https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75 + + + + + +#### 🎯 注目:屋内ウォークスルー(約 25,000 フレーム、13 分) + + +*このシーケンスはインタラクティブな viser ビューアーには長すぎるため、[オフラインレンダリングパイプライン](#-オフラインレンダリングパイプラインdemo_renderbatch_demopy)でレンダリングしました。完全なコマンドは該当セクションを参照してください。* + +今後さらに多くのサンプルを提供する予定です。 + +### 動的シーンのデモ(Droid-W) + +**データセット:**Hugging Face の [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) からデモシーケンスをダウンロードします。 + +上記データセットの `dynamic` シーケンスでの実行例です(空マスクを有効化、カメラ最適化を 4 回反復、2 フレームごとにキーフレーム)。 + +空マスク、4 回のカメラ最適化反復、入力ストライド 2 で `dynamic` シーケンスを実行します。 + +```bash +python demo.py \ + --image_folder /path/to/dynamic\ + --model_path ../../Lingbot-Map/lingbot-map.pt \ + --camera_num_iterations 4 \ + --mask_sky \ + --stride 2 +``` + + + +https://github.com/user-attachments/assets/567b6e9b-1cbf-402a-96be-9bab70715ec3 + +image + + + + + +### キーフレーム間隔を用いたストリーミング + +`--keyframe_interval` を使用すると、N フレームごとのフレームだけをキーフレームとして保持し、KV キャッシュのメモリ使用量を削減できます。非キーフレームも予測を生成しますが、キャッシュには保存されません。これは 320 フレームを超える長いシーケンスに有効です(320 ビューの video RoPE で学習しているため、KV キャッシュに 320 ビューを超えて保存すると性能が低下します。キーフレーム戦略を使えば、より長いシーケンスを推論できます)。demo.py ではキーフレーム間隔が自動計算されます。 + +> **推論範囲について。**本手法はデフォルトで状態をリセットしないため、最大推論範囲は学習データセットで観測された最長距離に制限されます。その距離を超える場合は状態のリセットが必要です。姿勢の崩壊が見られたら、ウィンドウモード(`--mode windowed`)へ切り替えてください。多くの場合、`--keyframe_interval` の調整だけで十分であり、その他のウィンドウパラメーターはデフォルト値のまま使用できます。 + + +### ウィンドウ推論(3000 フレームを超える長いシーケンス向け) + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --video_path video.mp4 --fps 10 \ + --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2 +``` + + +### 空マスク + +空マスクでは ONNX の空セグメンテーションモデルを使用し、再構成した点群から空の点を除外します。これにより、屋外シーンの可視化品質が向上します。 + +**セットアップ:** + +```bash +# Install onnxruntime (required) +pip install onnxruntime # CPU +# or +pip install onnxruntime-gpu # GPU (faster for large image sets) +``` + +空セグメンテーションモデル(`skyseg.onnx`)は、初回使用時に [Hugging Face](https://huggingface.co/JianyuanWang/skyseg/resolve/main/skyseg.onnx) から自動的にダウンロードされます。 + +**使用方法:** + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky +``` + +空マスクは `_sky_masks/` にキャッシュされるため、2 回目以降の実行では再生成を省略できます。`--sky_mask_dir` で独自のキャッシュディレクトリを指定したり、`--sky_mask_visualization_dir` でマスクの並列比較画像を保存したりすることもできます。 + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky \ + --sky_mask_dir /path/to/cached_masks/ \ + --sky_mask_visualization_dir /path/to/mask_viz/ +``` + +### 可視化オプション + +| 引数 | デフォルト | 説明 | +|:---|:---|:---| +| `--port` | `8080` | Viser ビューアーのポート | +| `--conf_threshold` | `1.5` | 低信頼度の点を除外する可視性しきい値 | +| `--point_size` | `0.00001` | 点群の点サイズ | +| `--downsample_factor` | `10` | 点群表示の空間ダウンサンプリング係数 | + +### パフォーマンスとメモリ + +#### FlashInfer を使用しない場合(SDPA へフォールバック) + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --use_sdpa +``` + +#### GPU メモリが限られている環境での実行 + +メモリ不足が発生した場合は、次のいずれか、または両方を試してください。 + +- **`--offload_to_cpu`** — 推論中のフレームごとの予測を CPU にオフロードします(デフォルトで有効。メモリに余裕がある場合のみ `--no-offload_to_cpu` を使用してください)。 +- **`--num_scale_frames 2`** — 双方向スケールフレーム数をデフォルトの 8 から 2 へ減らし、初期スケールフェーズのアクティベーションピークを抑えます。 + +#### 推論の高速化 + +カメラヘッドの反復改良ステップ数を減らすことで、姿勢精度をわずかに犠牲にして実行時間を短縮できます。 + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --camera_num_iterations 1 +``` + +`--camera_num_iterations` のデフォルトは `4` です。`1` に設定すると、カメラヘッドで 3 回の改良パスを省略し、KV キャッシュを 4 分の 1 に縮小します。 + +## 🎥 オフラインレンダリングパイプライン(`demo_render/batch_demo.py`) + +シーケンスがインタラクティブな viser ビューアーには長すぎる場合(例:[上記の屋内ウォークスルー](#-注目屋内ウォークスルー約-25000-フレーム13-分))は、このパイプラインを使用します。`demo_render/batch_demo.py` は一体型のオフラインエントリーポイントです。動画または画像フォルダーを指定すると、1 つのコマンドでモデル推論を実行し、ヘッドレスの点群フライスルー MP4 を生成します。PyTorch / FlashInfer / チェックポイントのスタックは `demo.py` と共通です。 + +VRAM 容量や GPU 使用量に制約がある場合は、次の実装も参照できます:https://github.com/ureeey/lingbot-map-rtx4060-8g/commit/eeee84a89cc97c1e39b736b46df4ee315275700b + +### インストール(基本インストールの拡張) + +**1. レンダリング用 Python 依存関係** + +```bash +pip install -e ".[vis,render]" +``` + +`render` は `open3d>=0.19` と `pyyaml` を導入します(中心となる `numpy<2` 制約は `lingbot-map` の基本インストールに由来します)。このパイプラインの空マスクは、バッチセグメンテーションに `onnxruntime-gpu` を使用します。CPU 版の `onnxruntime` をまだ導入していない場合は、次をインストールしてください。 + +```bash +pip install onnxruntime-gpu +``` + +**2. Kaolin** — 上記で推奨した PyTorch 2.8.0 + CUDA 12.8 に対応: + +```bash +pip install --index-url https://pypi.org/simple \ + kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html +``` + +> `--index-url https://pypi.org/simple` は、PyPI のプレースホルダー wheel(インポート時に `ImportError` が発生します)を返す可能性がある内部ミラーを回避します。 +> NVIDIA Kaolin は PyTorch 2.9.x 用のビルド済み wheel を公開していません。他の理由で 2.9 を使用する場合は、Kaolin をソースからビルドしてください(`pip install --no-build-isolation git+https://github.com/NVIDIAGameWorks/kaolin.git`、ローカル CUDA ツールキットが必要)。その他の torch/CUDA の組み合わせは [NVIDIA Kaolin のインストール](https://kaolin.readthedocs.io/en/latest/notes/installation.html)を参照してください。 + +**3. ffmpeg** + +```bash +sudo apt install ffmpeg # or: brew install ffmpeg +``` + +**4. CUDA 拡張**(初回実行前に必須) + +```bash +cd demo_render/render_cuda_ext && python setup.py build_ext --inplace && cd ../.. +``` + +これにより、`voxel_morton_ext` と `frustum_cull_ext` がその場でビルドされます。どちらも GPU ボクセル化と視錐台カリングのために `rgbd_render` からインポートされます。 + +### 実行例 — 長時間の屋内ウォークスルー(約 25,000 フレーム、13 分) + +**データセット:**Hugging Face の [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) からサンプル動画をダウンロードします。 + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/indoor_travel.MP4 \ + --output_folder /data/outputs/indoor_travel/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/indoor.yaml \ + --mode windowed --window_size 128 \ + --keyframe_interval 10 --overlap_keyframes 8 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + +各フラグを指定する理由: + +| フラグ | 指定する理由 | +|---|---| +| `--mode windowed --window_size 128` | シーケンスが約 320 フレームの RoPE 学習範囲を超えると、スライディングウィンドウ推論が必要になります。各ウィンドウで KV キャッシュがリセットされます。**`window_size` が数えるのは実フレームではなく KV キャッシュのスロットです**。最初の `num_scale_frames`(=8)スロットにはスケールフレームを、残りの `128 − 8 = 120` スロットにはキーフレームを格納します。したがって `keyframe_interval = 13` の場合、1 ウィンドウで `8 + 120 × 13 = 1568` 実フレームをカバーします。 | +| `--keyframe_interval 10` | 10 フレームごとのフレームだけをキーフレームとしてキャッシュします。非キーフレームもフレームごとの予測を出力しますが、KV キャッシュは増加しません。| +| `--overlap_keyframes 8` | 隣接ウィンドウで 8 キーフレーム分のコンテキストを共有します。内部的には `max(num_scale_frames, 8 × keyframe_interval) = 8 × 13 = 104` 実フレームの重複として解決されます。ウィンドウ間の姿勢アラインメントを安定させるため、`keyframe_interval > 1` の場合は常に推奨します。 | +| `--config demo_render/config/indoor.yaml` | 屋内プリセットから、レンダリング、シーン、カメラ、オーバーレイのデフォルト値(短い深度、近接した追従カメラ)を初期化します。ユーザーが明示的に渡した CLI フラグは引き続き YAML 値を上書きします。 | +| `--sky_mask_dir` / `--sky_mask_visualization_dir` | 空マスクとその並列比較画像をディスクへ保存し、以後の再実行で ONNX セグメンテーションをやり直さずに再利用できるようにします。(レンダリングパイプラインが使用するのは、YAML プリセットまたは `--mask_sky` で空マスクを有効にした場合だけです。) | +| `--camera_vis default` | レンダリング動画に軌跡と直近フレームの点を重ねます。 | +| `--keyframes_only_points` | キーフレームの深度だけを点群へ逆投影します。非キーフレームも姿勢を軌跡/視錐台オーバーレイへ提供します。非常に長いシーケンスでも点群を疎に保てます。 | +| `--frame_tag --frame_tag_position top_right` | MP4 の右上へ ` / Frames` カウンターを表示します。 | +| `--save_predictions` | フレームごとの NPZ を MP4 とともに保存します。後から確認したり、異なるカメラ/オーバーレイ設定で再レンダリングしたりする場合に便利です。 | + + +keyframe_interval = 10 を image_stride = 10 に置き換えるとレンダリングを高速化できます。次に、demo_render/config/indoor.yaml のカメラ追従セクションのコメントを解除し、鳥瞰範囲を [2000, 2500] に設定すると、デモに示した屋内フライスルー効果を再現できます。 + +image + + + + + +https://github.com/user-attachments/assets/21b444ea-e6b6-48f0-8b34-3acad41166ac + + + + + + + +### 実行例 — 屋外走行シーン + +**データセット:**Hugging Face の [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) からサンプル動画をダウンロードします。 + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/drive_frames.mp4 \ + --output_folder /data/outputs/drive/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + + +上記の屋内ウォークスルーとの相違点: + +| フラグ | 指定する理由 | +|---|---| +| `--config demo_render/config/outdoor_drive.yaml` | 屋外プリセットからデフォルト値を初期化します。空マスクを有効にし、より深いレンダリング範囲(`max_depth: 250`)を使い、車両軌跡向けに調整した追従カメラで最後に鳥瞰表示を行います。 | +| `--image_stride 1` | 動画の全フレームを使用します。長時間または高 FPS の走行動画をサブサンプリングするには値を大きくします。 | +| `--max_non_keyframe_gap 100` | キーフレームを強制するまでの連続非キーフレーム数の上限です。フローベースのキーフレーム選択(`--flow_threshold > 0`)でのみ有効で、デフォルトの固定間隔モードでは効果がありません。 | + +残りのフラグ(`--mode windowed --window_size 128`、`--overlap_keyframes 8`、空マスクのキャッシュ、オーバーレイ、`--save_predictions`)は屋内の例と同じです。上記のフラグ別の表を参照してください。 + +### 実行例 — LingBot-World のシーン + +世界モデル LingBot-World で生成された動画を再構成します。同じパイプラインを生成動画にもそのまま使用できます。 + +**データセット:**Hugging Face の [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) からサンプル動画(`lingbo_world_frames.mp4`、`lingbo_world2_frames.mp4`)をダウンロードします。 + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/lingbo_world_frames.mp4 \ + --output_folder /data/outputs/lingbo_world/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +2 本目のクリップでは、`--video_path /data/demo_videos/lingbo_world2_frames.mp4 --output_folder /data/outputs/lingbo_world2/` を指定して同じコマンドを実行します(キャッシュしたマスクを分けて保存する場合は、`--sky_mask_dir` / `--sky_mask_visualization_dir` に別々のフォルダーを指定してください)。 + +すべてのフラグは上記の[屋外走行シーン](#実行例-屋外走行シーン)と同じで、入力動画と出力フォルダーだけが異なります。各フラグの理由は、走行シーンと屋内ウォークスルーの表を参照してください。 + +image + +image + + + +### カメラパス(YAML) + +仮想カメラパスは、YAML プリセットの `camera.segments` リストで記述します。このプリセットは `--config` で渡します。YAML を編集するだけで独自のショットを設計でき、CLI フラグを変更する必要はありません。 + +組み込みプリセットは `demo_render/config/` にあります:`default.yaml`、`indoor.yaml`、`outdoor_drive.yaml`。いずれかをコピーし、`camera:` ブロックを編集してください。 + +#### YAML の構造 + +```yaml +camera: + fov: 60.0 # camera field of view in degrees + transition: 30 # frames blended between adjacent segments + segments: + - mode: follow # chase cam following the input trajectory + frames: [0, 1500] # rendered-frame range this segment covers (-1 = end) + back_offset: 0.3 # how far behind the input camera (fraction of scene scale) + up_offset: 0.08 # vertical lift above the input camera + look_offset: 0.4 # how far ahead the lookat target points + smooth_window: 30 # trajectory smoothing window in frames + - mode: birdeye # rise up for a top-down reveal of the whole scene + frames: [1500, 1800] + reveal_height_mult: 2.5 # birdeye height = scene scale × this factor + - mode: follow # drop back into chase cam + frames: [1800, -1] + back_offset: 0.3 + up_offset: 0.08 + look_offset: 0.4 +``` + +`transition` は、隣接するセグメント間でブレンドするフレーム数を制御します。`frames: [0, -1]` は「シーケンス全体」を意味します。 + +#### 使用可能なモード + +| `mode` | 動作 | 調整可能なフィールド | +|---|---|---| +| `follow` | 追従カメラが滑らかなオフセットで入力軌跡をたどります。ウォークスルーで最も映画的な選択肢です。 | `back_offset`、`up_offset`、`look_offset`、`smooth_window`、`scale_frames` | +| `birdeye` | シーン全体を上から表示します。メインビジュアルや概要ショットに便利です。 | `reveal_height_mult` | +| `static` | eye + lookat を固定し、セグメントの開始フレームから自動導出します。 | — | +| `pivot` | eye を固定し、lookat を軌跡に沿って移動させます。 | — | + +#### 単一ショットの YAML 例 + +**追従のみ**(最も一般的): + +```yaml +camera: + fov: 60.0 + segments: + - mode: follow + frames: [0, -1] + back_offset: 0.3 + up_offset: 0.08 + look_offset: 0.4 + smooth_window: 30 +``` + +**全編鳥瞰**(概要/メインビジュアル向け): + +```yaml +camera: + fov: 60.0 + segments: + - mode: birdeye + frames: [0, -1] + reveal_height_mult: 2.5 +``` + +**追従に鳥瞰を挿入**:`segments:` の下へ複数のセグメントを順番に並べるだけです。隣接セグメントは `transition` フレームを使って補間されます。 + +> 注意:`--config` で YAML プリセットを読み込むときに、セグメント形状を指定する CLI フラグ(`--camera_mode`、`--back_offset`、`--up_offset`、`--look_offset`、`--smooth_window`、`--follow_scale_frames`、`--birdeye_start`、`--birdeye_duration`、`--reveal_height_mult`)を**1 つでも**渡すと、YAML の `segments` は破棄され、それらのフラグからカメラパスが再構築されます。YAML だけで駆動するには、これらをコマンドラインで渡さないでください。 + +### 出力ファイル + +出力名(例:`` または ``)ごとに、次のファイルが生成されます。 + +| ファイル | 説明 | +|------|-------------| +| `_pointcloud.mp4` | レンダリングした点群フライスルー | +| `_pointcloud_rgb.mp4` | 元の RGB フレームを動画としてエンコードしたもの | +| `_pointcloud_config.yaml` | この実行の完全な設定スナップショット | +| `batch_results.json` | シーンごとの成功状態/所要時間の要約 | + +## 📜 ライセンス + +本プロジェクトは Apache License 2.0 のもとで公開されています。詳細は [LICENSE](LICENSE.txt) ファイルを参照してください。 + +## 📖 引用 + +```bibtex +@article{chen2026geometric, + title={Geometric Context Transformer for Streaming 3D Reconstruction}, + 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}, + journal={arXiv preprint arXiv:2604.14141}, + year={2026} +} +``` + +## ✨ 謝辞 + +有益な議論と支援をいただいた Shangzhan Zhang、Jianyuan Wang、Yudong Jin、Christian Rupprecht、Xun Cao の各氏に感謝します。 + +本研究は、以下の優れたオープンソースプロジェクトを基盤としています。 + +- [VGGT](https://github.com/facebookresearch/vggt) +- [DINOv2](https://github.com/facebookresearch/dinov2) +- [Flashinfer](https://github.com/flashinfer-ai/flashinfer) + +--- diff --git a/README.md b/README.md index 2d412e3..d5cbe97 100644 --- a/README.md +++ b/README.md @@ -1,588 +1,590 @@ -
- - -

LingBot-Map: Geometric Context Transformer for Streaming 3D Reconstruction

- +
+ + +

LingBot-Map: Geometric Context Transformer for Streaming 3D Reconstruction

+ Robbyant Team -
- -
- -[![Paper](https://img.shields.io/static/v1?label=Paper&message=arXiv&color=red&logo=arxiv)](https://arxiv.org/abs/2604.14141) -[![PDF](https://img.shields.io/static/v1?label=Paper&message=PDF&color=red&logo=adobeacrobatreader)](lingbot-map_paper.pdf) -[![Project](https://img.shields.io/badge/Project-Website-blue)](https://technology.robbyant.com/lingbot-map) -[![HuggingFace](https://img.shields.io/static/v1?label=%F0%9F%A4%97%20Model&message=HuggingFace&color=orange)](https://huggingface.co/robbyant/lingbot-map) -[![ModelScope](https://img.shields.io/static/v1?label=%F0%9F%A4%96%20Model&message=ModelScope&color=purple)](https://www.modelscope.cn/models/Robbyant/lingbot-map) -[![License](https://img.shields.io/badge/License-Apache--2.0-green)](LICENSE.txt) +[English](README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md)
- -https://github.com/user-attachments/assets/fe39e095-af2c-4ec9-b68d-a8ba97e505ab - ------ - -### 🗺️ 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. - ---- - -## 📑 Table of Contents - -
-Click to expand - -- [📰 News](#-news) -- [📋 TODO](#-todo) -- [⚙️ Installation](#️-installation) -- [📦 Model Download](#-model-download) -- [🚀 Quick Start](#-quick-start) -- [🎬 Interactive Demo (`demo.py`)](#-interactive-demo-demopy) - - [Try the Example Scenes](#try-the-example-scenes) - - [Streaming with Keyframe Interval](#streaming-with-keyframe-interval) - - [Windowed Inference (for long sequences, >3000 frames)](#windowed-inference-for-long-sequences-3000-frames) - - [Sky Masking](#sky-masking) - - [Visualization Options](#visualization-options) - - [Performance & Memory](#performance--memory) -- [🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`)](#-offline-rendering-pipeline-demo_renderbatch_demopy) -- [📜 License](#-license) -- [📖 Citation](#-citation) -- [✨ Acknowledgments](#-acknowledgments) - -
- ---- - -## 📰 News - -- **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/](benchmark/) for the pipeline, and run [`preprocess/oxford.py`](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](#worked-example--long-indoor-walkthrough-25-000-frames-13-minutes) 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**. - ---- - -## 📋 TODO - -- ✅ Release evaluation benchmark - - ✅ Oxford Spires dataset - - ✅ KITTI dataset - - ✅ VBR dataset - - ✅ Droid-W dataset - - ✅ TUM-D dataset - - ✅ 7-scenes dataset - - ✅ ETH3D dataset - - ✅ Tanks and Temples dataset - - ✅ NRGBD dataset -- ✅ Release demo scripts - - ✅ Indoor long-video demo ([Featured indoor walkthrough](#-featured-indoor-walkthrough-25-000-frames-13-minutes)) - - ✅ Outdoor long-video demo - - ✅ LingBot-World demo ([Worked example](#worked-example--lingbot-world-scenes)) - - ✅ Aerial long-video demo - ---- - -## ⚙️ Installation - -**1. Create conda environment** - -```bash -conda create -n lingbot-map python=3.10 -y -conda activate lingbot-map -``` - -**2. Install PyTorch (CUDA 12.8)** - -```bash -pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128 -``` - -> 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](https://pytorch.org/get-started/locally/). - -**3. Install lingbot-map** - -```bash -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: - -```bash -pip install --index-url https://pypi.org/simple flashinfer-python -``` - -> `--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](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`. - -**5. Visualization dependencies (optional)** - -```bash -pip install -e ".[vis]" -``` - -## 📦 Model Download - -| Model Name | Huggingface Repository | ModelScope Repository | Description | -| :--- | :--- | :--- | :--- | -| 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. | -| 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. | -| 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). | - -> 🚧 **Coming soon:** we're training an stronger model that supports longer sequences — stay tuned. - -## 🚀 Quick Start - -After installation, run your first scene with one command: - -```bash -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/courthouse --mask_sky -``` - -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. - -## 🎬 Interactive Demo (`demo.py`) - -Run `demo.py` for interactive 3D visualization via a browser-based [viser](https://github.com/nerfstudio-project/viser) viewer (default `http://localhost:8080`). - -### Try the Example Scenes - -We provide three example scenes in `example/` that you can run out of the box: -```bash -# courthouse scene -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/courthouse --mask_sky -``` - - -https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8 - - - - - - -```bash -# University scene -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/university --mask_sky -``` - - -https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57 - - - - - - - -```bash -# Loop scene (loop closure trajectory) -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/loop -``` - - -https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75 - - - - - -#### 🎯 Featured: indoor walkthrough (~25 000 frames, 13 minutes) - - -*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.* - -We will provide more examples in the follow-up. - -### Dynamic Demo (From Droid-W) - -**Dataset:** Download the demo sequences from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -Example run on the `dynamic` sequence from the dataset above (sky masking on, 4 camera optimization iterations, keyframe every 2 frames): - -Run the `dynamic` sequence with sky masking, 4 camera optimization iterations, and an input stride of 2: - -```bash -python demo.py \ - --image_folder /path/to/dynamic\ - --model_path ../../Lingbot-Map/lingbot-map.pt \ - --camera_num_iterations 4 \ - --mask_sky \ - --stride 2 -``` - - - -https://github.com/user-attachments/assets/567b6e9b-1cbf-402a-96be-9bab70715ec3 - -image - - - - - -### 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.). In demo.py, the keyframe interval is calculated automatically. - -> **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) - -```bash -python demo.py --model_path /path/to/lingbot-map.pt \ - --video_path video.mp4 --fps 10 \ - --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2 -``` - - -### Sky Masking - -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 -# Install onnxruntime (required) -pip install onnxruntime # CPU -# or -pip install onnxruntime-gpu # GPU (faster for large image sets) -``` - -The sky segmentation model (`skyseg.onnx`) will be automatically downloaded from [HuggingFace](https://huggingface.co/JianyuanWang/skyseg/resolve/main/skyseg.onnx) on first use. - -**Usage:** - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --mask_sky -``` - -Sky masks are cached in `_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`: - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --mask_sky \ - --sky_mask_dir /path/to/cached_masks/ \ - --sky_mask_visualization_dir /path/to/mask_viz/ -``` - -### Visualization Options - -| Argument | Default | Description | -|:---|:---|:---| -| `--port` | `8080` | Viser viewer port | -| `--conf_threshold` | `1.5` | Visibility threshold for filtering low-confidence points | -| `--point_size` | `0.00001` | Point cloud point size | -| `--downsample_factor` | `10` | Spatial downsampling for point cloud display | - -### Performance & Memory - -#### Without FlashInfer (SDPA fallback) - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --use_sdpa -``` - -#### Running on Limited GPU Memory - -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: - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --camera_num_iterations 1 -``` - -`--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×). - -## 🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`) - -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`. - -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 - -### Install (extends the main install) - -**1. Rendering Python dependencies** - -```bash -pip install -e ".[vis,render]" -``` - -`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`: - -```bash -pip install onnxruntime-gpu -``` - -**2. Kaolin** — matches the PyTorch 2.8.0 + CUDA 12.8 recommended above: - -```bash -pip install --index-url https://pypi.org/simple \ - kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html -``` - -> `--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](https://kaolin.readthedocs.io/en/latest/notes/installation.html). - -**3. ffmpeg** - -```bash -sudo apt install ffmpeg # or: brew install ffmpeg -``` - -**4. CUDA extensions** (required before first run) - -```bash -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) - -**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -```bash - python demo_render/batch_demo.py \ - --video_path /data/demo_videos/indoor_travel.MP4 \ - --output_folder /data/outputs/indoor_travel/ \ - --model_path /path/to/lingbot-map.pt \ - --config demo_render/config/indoor.yaml \ - --mode windowed --window_size 128 \ - --keyframe_interval 10 --overlap_keyframes 8 \ - --sky_mask_dir /data/outputs/sky_masks \ - --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ - --camera_vis default --keyframes_only_points \ - --frame_tag --frame_tag_position top_right \ - --save_predictions -``` - -image - -Flag-by-flag rationale: - -| Flag | Why it's there | -|---|---| -| `--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. | -| `--keyframe_interval 10` | Cache only every 10th 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 ` / 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. | - - -Replacing keyframe_interval = 10 with image_stride = 10 speeds up rendering. Then, uncomment the camera follow section in demo_render/config/indoor.yaml and set the birdeye's ranges to [2000, 2500] to reproduce the indoor fly-through effect shown in the demo: - -image - - - - - -https://github.com/user-attachments/assets/21b444ea-e6b6-48f0-8b34-3acad41166ac - - - - - - - -### Worked Example — outdoor drive scene - -**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -```bash - python demo_render/batch_demo.py \ - --video_path /data/demo_videos/drive_frames.mp4 \ - --output_folder /data/outputs/drive/ \ - --model_path /path/to/lingbot-map.pt \ - --config demo_render/config/outdoor_drive.yaml \ - --mode windowed --window_size 128 \ - --max_non_keyframe_gap 100 --overlap_keyframes 8 \ - --image_stride 1 \ - --sky_mask_dir /data/outputs/sky_masks \ - --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ - --camera_vis default --keyframes_only_points \ - --frame_tag --frame_tag_position top_right \ - --save_predictions -``` - -image - - -What differs from the indoor walkthrough above: - -| Flag | Why it's there | -|---|---| -| `--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. | -| `--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](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -```bash - python demo_render/batch_demo.py \ - --video_path /data/demo_videos/lingbo_world_frames.mp4 \ - --output_folder /data/outputs/lingbo_world/ \ - --model_path /path/to/lingbot-map.pt \ - --config demo_render/config/outdoor_drive.yaml \ - --mode windowed --window_size 128 \ - --max_non_keyframe_gap 100 --overlap_keyframes 8 \ - --image_stride 1 \ - --sky_mask_dir /data/outputs/sky_masks \ - --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ - --camera_vis default --keyframes_only_points \ - --frame_tag --frame_tag_position top_right \ - --save_predictions -``` - -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](#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. - -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 -camera: - fov: 60.0 # camera field of view in degrees - transition: 30 # frames blended between adjacent segments - segments: - - mode: follow # chase cam following the input trajectory - frames: [0, 1500] # rendered-frame range this segment covers (-1 = end) - back_offset: 0.3 # how far behind the input camera (fraction of scene scale) - up_offset: 0.08 # vertical lift above the input camera - look_offset: 0.4 # how far ahead the lookat target points - smooth_window: 30 # trajectory smoothing window in frames - - mode: birdeye # rise up for a top-down reveal of the whole scene - frames: [1500, 1800] - reveal_height_mult: 2.5 # birdeye height = scene scale × this factor - - mode: follow # drop back into chase cam - frames: [1800, -1] - back_offset: 0.3 - up_offset: 0.08 - look_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. | `back_offset`, `up_offset`, `look_offset`, `smooth_window`, `scale_frames` | -| `birdeye` | Top-down reveal of the whole scene. Useful for hero / overview shots. | `reveal_height_mult` | -| `static` | Fixed eye + lookat, auto-derived from the segment's start frame. | — | -| `pivot` | Fixed eye, lookat sweeps along the trajectory. | — | - -#### Single-shot YAML examples - -**Pure follow** (most common): - -```yaml -camera: - fov: 60.0 - segments: - - mode: follow - frames: [0, -1] - back_offset: 0.3 - up_offset: 0.08 - look_offset: 0.4 - smooth_window: 30 -``` - -**Full birdeye** (good for overview / hero shots): - -```yaml -camera: - fov: 60.0 - segments: - - mode: birdeye - frames: [0, -1] - reveal_height_mult: 2.5 -``` - -**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. `` or ``): - -| File | Description | -|------|-------------| -| `_pointcloud.mp4` | Rendered point-cloud flythrough | -| `_pointcloud_rgb.mp4` | Original RGB frames encoded as video | -| `_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](LICENSE.txt) file for details. - -## 📖 Citation - -```bibtex -@article{chen2026geometric, - title={Geometric Context Transformer for Streaming 3D Reconstruction}, - 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}, - journal={arXiv preprint arXiv:2604.14141}, - year={2026} -} -``` - -## ✨ 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: - -- [VGGT](https://github.com/facebookresearch/vggt) -- [DINOv2](https://github.com/facebookresearch/dinov2) -- [Flashinfer](https://github.com/flashinfer-ai/flashinfer) - ---- + +
+ +[![Paper](https://img.shields.io/static/v1?label=Paper&message=arXiv&color=red&logo=arxiv)](https://arxiv.org/abs/2604.14141) +[![PDF](https://img.shields.io/static/v1?label=Paper&message=PDF&color=red&logo=adobeacrobatreader)](lingbot-map_paper.pdf) +[![Project](https://img.shields.io/badge/Project-Website-blue)](https://technology.robbyant.com/lingbot-map) +[![HuggingFace](https://img.shields.io/static/v1?label=%F0%9F%A4%97%20Model&message=HuggingFace&color=orange)](https://huggingface.co/robbyant/lingbot-map) +[![ModelScope](https://img.shields.io/static/v1?label=%F0%9F%A4%96%20Model&message=ModelScope&color=purple)](https://www.modelscope.cn/models/Robbyant/lingbot-map) +[![License](https://img.shields.io/badge/License-Apache--2.0-green)](LICENSE.txt) + +
+ +https://github.com/user-attachments/assets/fe39e095-af2c-4ec9-b68d-a8ba97e505ab + +----- + +### 🗺️ 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. + +--- + +## 📑 Table of Contents + +
+Click to expand + +- [📰 News](#-news) +- [📋 TODO](#-todo) +- [⚙️ Installation](#️-installation) +- [📦 Model Download](#-model-download) +- [🚀 Quick Start](#-quick-start) +- [🎬 Interactive Demo (`demo.py`)](#-interactive-demo-demopy) + - [Try the Example Scenes](#try-the-example-scenes) + - [Streaming with Keyframe Interval](#streaming-with-keyframe-interval) + - [Windowed Inference (for long sequences, >3000 frames)](#windowed-inference-for-long-sequences-3000-frames) + - [Sky Masking](#sky-masking) + - [Visualization Options](#visualization-options) + - [Performance & Memory](#performance--memory) +- [🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`)](#-offline-rendering-pipeline-demo_renderbatch_demopy) +- [📜 License](#-license) +- [📖 Citation](#-citation) +- [✨ Acknowledgments](#-acknowledgments) + +
+ +--- + +## 📰 News + +- **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/](benchmark/) for the pipeline, and run [`preprocess/oxford.py`](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](#worked-example--long-indoor-walkthrough-25-000-frames-13-minutes) 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**. + +--- + +## 📋 TODO + +- ✅ Release evaluation benchmark + - ✅ Oxford Spires dataset + - ✅ KITTI dataset + - ✅ VBR dataset + - ✅ Droid-W dataset + - ✅ TUM-D dataset + - ✅ 7-scenes dataset + - ✅ ETH3D dataset + - ✅ Tanks and Temples dataset + - ✅ NRGBD dataset +- ✅ Release demo scripts + - ✅ Indoor long-video demo ([Featured indoor walkthrough](#-featured-indoor-walkthrough-25-000-frames-13-minutes)) + - ✅ Outdoor long-video demo + - ✅ LingBot-World demo ([Worked example](#worked-example--lingbot-world-scenes)) + - ✅ Aerial long-video demo + +--- + +## ⚙️ Installation + +**1. Create conda environment** + +```bash +conda create -n lingbot-map python=3.10 -y +conda activate lingbot-map +``` + +**2. Install PyTorch (CUDA 12.8)** + +```bash +pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128 +``` + +> 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](https://pytorch.org/get-started/locally/). + +**3. Install lingbot-map** + +```bash +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: + +```bash +pip install --index-url https://pypi.org/simple flashinfer-python +``` + +> `--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](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`. + +**5. Visualization dependencies (optional)** + +```bash +pip install -e ".[vis]" +``` + +## 📦 Model Download + +| Model Name | Huggingface Repository | ModelScope Repository | Description | +| :--- | :--- | :--- | :--- | +| 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. | +| 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. | +| 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). | + +> 🚧 **Coming soon:** we're training an stronger model that supports longer sequences — stay tuned. + +## 🚀 Quick Start + +After installation, run your first scene with one command: + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + +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. + +## 🎬 Interactive Demo (`demo.py`) + +Run `demo.py` for interactive 3D visualization via a browser-based [viser](https://github.com/nerfstudio-project/viser) viewer (default `http://localhost:8080`). + +### Try the Example Scenes + +We provide three example scenes in `example/` that you can run out of the box: +```bash +# courthouse scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + + +https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8 + + + + + + +```bash +# University scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/university --mask_sky +``` + + +https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57 + + + + + + + +```bash +# Loop scene (loop closure trajectory) +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/loop +``` + + +https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75 + + + + + +#### 🎯 Featured: indoor walkthrough (~25 000 frames, 13 minutes) + + +*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.* + +We will provide more examples in the follow-up. + +### Dynamic Demo (From Droid-W) + +**Dataset:** Download the demo sequences from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +Example run on the `dynamic` sequence from the dataset above (sky masking on, 4 camera optimization iterations, keyframe every 2 frames): + +Run the `dynamic` sequence with sky masking, 4 camera optimization iterations, and an input stride of 2: + +```bash +python demo.py \ + --image_folder /path/to/dynamic\ + --model_path ../../Lingbot-Map/lingbot-map.pt \ + --camera_num_iterations 4 \ + --mask_sky \ + --stride 2 +``` + + + +https://github.com/user-attachments/assets/567b6e9b-1cbf-402a-96be-9bab70715ec3 + +image + + + + + +### 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.). In demo.py, the keyframe interval is calculated automatically. + +> **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) + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --video_path video.mp4 --fps 10 \ + --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2 +``` + + +### Sky Masking + +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 +# Install onnxruntime (required) +pip install onnxruntime # CPU +# or +pip install onnxruntime-gpu # GPU (faster for large image sets) +``` + +The sky segmentation model (`skyseg.onnx`) will be automatically downloaded from [HuggingFace](https://huggingface.co/JianyuanWang/skyseg/resolve/main/skyseg.onnx) on first use. + +**Usage:** + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky +``` + +Sky masks are cached in `_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`: + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky \ + --sky_mask_dir /path/to/cached_masks/ \ + --sky_mask_visualization_dir /path/to/mask_viz/ +``` + +### Visualization Options + +| Argument | Default | Description | +|:---|:---|:---| +| `--port` | `8080` | Viser viewer port | +| `--conf_threshold` | `1.5` | Visibility threshold for filtering low-confidence points | +| `--point_size` | `0.00001` | Point cloud point size | +| `--downsample_factor` | `10` | Spatial downsampling for point cloud display | + +### Performance & Memory + +#### Without FlashInfer (SDPA fallback) + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --use_sdpa +``` + +#### Running on Limited GPU Memory + +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: + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --camera_num_iterations 1 +``` + +`--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×). + +## 🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`) + +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`. + +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 + +### Install (extends the main install) + +**1. Rendering Python dependencies** + +```bash +pip install -e ".[vis,render]" +``` + +`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`: + +```bash +pip install onnxruntime-gpu +``` + +**2. Kaolin** — matches the PyTorch 2.8.0 + CUDA 12.8 recommended above: + +```bash +pip install --index-url https://pypi.org/simple \ + kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html +``` + +> `--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](https://kaolin.readthedocs.io/en/latest/notes/installation.html). + +**3. ffmpeg** + +```bash +sudo apt install ffmpeg # or: brew install ffmpeg +``` + +**4. CUDA extensions** (required before first run) + +```bash +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) + +**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/indoor_travel.MP4 \ + --output_folder /data/outputs/indoor_travel/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/indoor.yaml \ + --mode windowed --window_size 128 \ + --keyframe_interval 10 --overlap_keyframes 8 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + +Flag-by-flag rationale: + +| Flag | Why it's there | +|---|---| +| `--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. | +| `--keyframe_interval 10` | Cache only every 10th 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 ` / 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. | + + +Replacing keyframe_interval = 10 with image_stride = 10 speeds up rendering. Then, uncomment the camera follow section in demo_render/config/indoor.yaml and set the birdeye's ranges to [2000, 2500] to reproduce the indoor fly-through effect shown in the demo: + +image + + + + + +https://github.com/user-attachments/assets/21b444ea-e6b6-48f0-8b34-3acad41166ac + + + + + + + +### Worked Example — outdoor drive scene + +**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/drive_frames.mp4 \ + --output_folder /data/outputs/drive/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + + +What differs from the indoor walkthrough above: + +| Flag | Why it's there | +|---|---| +| `--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. | +| `--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](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/lingbo_world_frames.mp4 \ + --output_folder /data/outputs/lingbo_world/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +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](#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. + +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 +camera: + fov: 60.0 # camera field of view in degrees + transition: 30 # frames blended between adjacent segments + segments: + - mode: follow # chase cam following the input trajectory + frames: [0, 1500] # rendered-frame range this segment covers (-1 = end) + back_offset: 0.3 # how far behind the input camera (fraction of scene scale) + up_offset: 0.08 # vertical lift above the input camera + look_offset: 0.4 # how far ahead the lookat target points + smooth_window: 30 # trajectory smoothing window in frames + - mode: birdeye # rise up for a top-down reveal of the whole scene + frames: [1500, 1800] + reveal_height_mult: 2.5 # birdeye height = scene scale × this factor + - mode: follow # drop back into chase cam + frames: [1800, -1] + back_offset: 0.3 + up_offset: 0.08 + look_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. | `back_offset`, `up_offset`, `look_offset`, `smooth_window`, `scale_frames` | +| `birdeye` | Top-down reveal of the whole scene. Useful for hero / overview shots. | `reveal_height_mult` | +| `static` | Fixed eye + lookat, auto-derived from the segment's start frame. | — | +| `pivot` | Fixed eye, lookat sweeps along the trajectory. | — | + +#### Single-shot YAML examples + +**Pure follow** (most common): + +```yaml +camera: + fov: 60.0 + segments: + - mode: follow + frames: [0, -1] + back_offset: 0.3 + up_offset: 0.08 + look_offset: 0.4 + smooth_window: 30 +``` + +**Full birdeye** (good for overview / hero shots): + +```yaml +camera: + fov: 60.0 + segments: + - mode: birdeye + frames: [0, -1] + reveal_height_mult: 2.5 +``` + +**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. `` or ``): + +| File | Description | +|------|-------------| +| `_pointcloud.mp4` | Rendered point-cloud flythrough | +| `_pointcloud_rgb.mp4` | Original RGB frames encoded as video | +| `_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](LICENSE.txt) file for details. + +## 📖 Citation + +```bibtex +@article{chen2026geometric, + title={Geometric Context Transformer for Streaming 3D Reconstruction}, + 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}, + journal={arXiv preprint arXiv:2604.14141}, + year={2026} +} +``` + +## ✨ 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: + +- [VGGT](https://github.com/facebookresearch/vggt) +- [DINOv2](https://github.com/facebookresearch/dinov2) +- [Flashinfer](https://github.com/flashinfer-ai/flashinfer) + +--- diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..2f3150e --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,590 @@ +
+ + +

LingBot-Map:用于流式三维重建的几何上下文 Transformer

+ +Robbyant Team + +[English](README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) + +
+ +
+ +[![Paper](https://img.shields.io/static/v1?label=Paper&message=arXiv&color=red&logo=arxiv)](https://arxiv.org/abs/2604.14141) +[![PDF](https://img.shields.io/static/v1?label=Paper&message=PDF&color=red&logo=adobeacrobatreader)](lingbot-map_paper.pdf) +[![Project](https://img.shields.io/badge/Project-Website-blue)](https://technology.robbyant.com/lingbot-map) +[![HuggingFace](https://img.shields.io/static/v1?label=%F0%9F%A4%97%20Model&message=HuggingFace&color=orange)](https://huggingface.co/robbyant/lingbot-map) +[![ModelScope](https://img.shields.io/static/v1?label=%F0%9F%A4%96%20Model&message=ModelScope&color=purple)](https://www.modelscope.cn/models/Robbyant/lingbot-map) +[![License](https://img.shields.io/badge/License-Apache--2.0-green)](LICENSE.txt) + +
+ +https://github.com/user-attachments/assets/fe39e095-af2c-4ec9-b68d-a8ba97e505ab + +----- + +### 🗺️ 认识 LingBot-Map!我们构建了一个用于流式三维重建的前馈式三维基础模型!🏗️🌍 + +LingBot-Map 重点解决以下问题: + +- **几何上下文 Transformer**:通过锚点上下文、位姿参考窗口和轨迹记忆,在单一流式框架中从架构层面统一坐标锚定、稠密几何线索与长距离漂移校正。 +- **高效流式推理**:采用带分页 KV 缓存注意力的前馈架构,在 518×378 分辨率、超过 10,000 帧的长序列上实现约 20 FPS 的稳定推理。 +- **先进的重建效果**:与现有流式方法和基于迭代优化的方法相比,在多种基准测试上均表现更优。 + +--- + +## 📑 目录 + +
+点击展开 + +- [📰 新闻](#-新闻) +- [📋 待办事项](#-待办事项) +- [⚙️ 安装](#️-安装) +- [📦 模型下载](#-模型下载) +- [🚀 快速开始](#-快速开始) +- [🎬 交互式演示(`demo.py`)](#-交互式演示demopy) + - [尝试示例场景](#尝试示例场景) + - [按关键帧间隔进行流式推理](#按关键帧间隔进行流式推理) + - [窗口化推理(用于超过 3000 帧的长序列)](#窗口化推理用于超过-3000-帧的长序列) + - [天空遮罩](#天空遮罩) + - [可视化选项](#可视化选项) + - [性能与显存](#性能与显存) +- [🎥 离线渲染流水线(`demo_render/batch_demo.py`)](#-离线渲染流水线demo_renderbatch_demopy) +- [📜 许可证](#-许可证) +- [📖 引用](#-引用) +- [✨ 致谢](#-致谢) + +
+ +--- + +## 📰 新闻 + +- **2026-06-28** — 修复了一个 SDPA KV 缓存错误。**SDPA 后端现在处理长序列时表现更好**。为获得最佳性能,我们仍建议使用 FlashInfer 后端。 +- **2026-05-25** — 📊 **发布评测基准**。我们发布了 KITTI 和 Oxford Spires 的评测脚本——流水线请参阅 [benchmark/](benchmark/),并在评测前运行 [`preprocess/oxford.py`](preprocess/oxford.py) 准备 Oxford Spires 数据。 +- **2026-04-29** — 📹 **发布长视频演示**。我们发布了一个由离线流水线渲染的超长视频示例(约 25,000 帧、13 分钟室内漫游)——命令、参数说明和渲染结果请参阅[完整示例](#完整示例长室内漫游约-25000-帧13-分钟)。 +- **2026-04-27** — 🚀 **LingBot-Map 加速完成**。拉取最新的 `main`,然后运行 `python demo.py --compile ...` 或 `python gct_profile.py --backend flashinfer --dtype bf16 --compile`,即可在你的硬件上验证。 +- **2026-04-24** — 修复了 `--keyframe_interval > 1` 时 FlashInfer KV 缓存会静默缓存非关键帧的问题。**现在处理超过 320 帧时,位姿与重建质量应当会更好**。 + +--- + +## 📋 待办事项 + +- ✅ 发布评测基准 + - ✅ Oxford Spires 数据集 + - ✅ KITTI 数据集 + - ✅ VBR 数据集 + - ✅ Droid-W 数据集 + - ✅ TUM-D 数据集 + - ✅ 7-scenes 数据集 + - ✅ ETH3D 数据集 + - ✅ Tanks and Temples 数据集 + - ✅ NRGBD 数据集 +- ✅ 发布演示脚本 + - ✅ 室内长视频演示([精选室内漫游](#-精选室内漫游约-25000-帧13-分钟)) + - ✅ 室外长视频演示 + - ✅ LingBot-World 演示([完整示例](#完整示例lingbot-world-场景)) + - ✅ 航拍长视频演示 + +--- + +## ⚙️ 安装 + +**1. 创建 conda 环境** + +```bash +conda create -n lingbot-map python=3.10 -y +conda activate lingbot-map +``` + +**2. 安装 PyTorch(CUDA 12.8)** + +```bash +pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128 +``` + +> 推荐使用 PyTorch 2.8.0,因为 NVIDIA Kaolin(批量渲染流水线所必需)为 `torch-2.8.0_cu128` 提供了预编译 wheel。如果只需要 `demo.py`,可以使用更新的 PyTorch,但批量渲染器届时需要从源码构建 Kaolin。 +> 其他 CUDA 版本请参阅 [PyTorch 入门指南](https://pytorch.org/get-started/locally/)。 + +**3. 安装 lingbot-map** + +```bash +pip install -e . +``` + +**4. 安装 FlashInfer(推荐)** + +FlashInfer 提供分页 KV 缓存注意力,以实现高效的流式推理。它是一个纯 Python 包,在首次使用时对 CUDA 内核进行 JIT 编译,因此一个 wheel 即可适配多个 CUDA/PyTorch 版本: + +```bash +pip install --index-url https://pypi.org/simple flashinfer-python +``` + +> `--index-url https://pypi.org/simple` 仅在默认 pip 索引是缺少 `flashinfer-python` 的内部镜像时才需要使用。 +> (可选)为加快首次使用速度,还可以安装 CUDA 专用的 JIT 缓存:`pip install flashinfer-jit-cache -f https://flashinfer.ai/whl/cu128/flashinfer-jit-cache/`。 +> 详情请参阅 [FlashInfer 安装文档](https://docs.flashinfer.ai/installation.html)。如果未安装 FlashInfer,可通过 `--use_sdpa` 让模型回退到 SDPA(PyTorch 原生注意力)。 + +**5. 可视化依赖(可选)** + +```bash +pip install -e ".[vis]" +``` + +## 📦 模型下载 + +| 模型名称 | Hugging Face 仓库 | ModelScope 仓库 | 说明 | +| :--- | :--- | :--- | :--- | +| lingbot-map-long | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | 更适合长序列和大尺度场景。 | +| lingbot-map | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | 均衡检查点(用于论文、基准测试和离线演示),兼顾短序列与长序列的综合性能。 | +| lingbot-map-stage1 | [robbyant/lingbot-map](https://huggingface.co/robbyant/lingbot-map) | [Robbyant/lingbot-map](https://www.modelscope.cn/models/Robbyant/lingbot-map) | lingbot-map 第一阶段训练检查点,可加载到 VGGT 模型中进行双向推理(c2w)。 | + +> 🚧 **即将推出:**我们正在训练一个支持更长序列的更强模型,敬请期待。 + +## 🚀 快速开始 + +安装完成后,用一条命令运行你的第一个场景: + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + +这会在 `http://localhost:8080` 启动交互式 [viser](https://github.com/nerfstudio-project/viser) 查看器。完整的场景和参数请参阅下方的[交互式演示](#-交互式演示demopy),长序列批量渲染请直接跳转到[离线渲染流水线](#-离线渲染流水线demo_renderbatch_demopy)。 + +## 🎬 交互式演示(`demo.py`) + +运行 `demo.py`,通过基于浏览器的 [viser](https://github.com/nerfstudio-project/viser) 查看器(默认地址为 `http://localhost:8080`)进行交互式三维可视化。 + +### 尝试示例场景 + +我们在 `example/` 中提供了三个开箱即用的示例场景: +```bash +# courthouse scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + + +https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8 + + + + + + +```bash +# University scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/university --mask_sky +``` + + +https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57 + + + + + + + +```bash +# Loop scene (loop closure trajectory) +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/loop +``` + + +https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75 + + + + + +#### 🎯 精选:室内漫游(约 25,000 帧,13 分钟) + + +*该序列对交互式 viser 查看器而言过长——此视频使用[离线渲染流水线](#-离线渲染流水线demo_renderbatch_demopy)渲染。完整命令请参阅对应章节。* + +后续我们将提供更多示例。 + +### 动态场景演示(来自 Droid-W) + +**数据集:**从 Hugging Face 上的 [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) 下载演示序列。 + +在上述数据集的 `dynamic` 序列上运行示例(启用天空遮罩、相机优化迭代 4 次、每 2 帧设一个关键帧): + +使用天空遮罩、4 次相机优化迭代和 2 的输入步长运行 `dynamic` 序列: + +```bash +python demo.py \ + --image_folder /path/to/dynamic\ + --model_path ../../Lingbot-Map/lingbot-map.pt \ + --camera_num_iterations 4 \ + --mask_sky \ + --stride 2 +``` + + + +https://github.com/user-attachments/assets/567b6e9b-1cbf-402a-96be-9bab70715ec3 + +image + + + + + +### 按关键帧间隔进行流式推理 + +使用 `--keyframe_interval` 仅将每第 N 帧保留为关键帧,从而减少 KV 缓存占用。非关键帧仍会生成预测,但不会存入缓存。这对超过 320 帧的长序列很有用(我们使用 320 个视图的视频 RoPE 进行训练,因此 KV 缓存存储超过 320 个视图时性能会下降;采用关键帧策略可以对更长序列进行推理)。在 demo.py 中,关键帧间隔会自动计算。 + +> **关于推理范围。**本方法默认不重置状态,因此最大推理范围受训练数据集中所见最长距离限制。超过该距离后,就必须重置状态。如果观察到位姿崩溃,请切换到窗口模式(`--mode windowed`)——大多数情况下,只需调整 `--keyframe_interval`,其余窗口参数保持默认值即可。 + + +### 窗口化推理(用于超过 3000 帧的长序列) + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --video_path video.mp4 --fps 10 \ + --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2 +``` + + +### 天空遮罩 + +天空遮罩使用 ONNX 天空分割模型,从重建点云中过滤天空点,从而提升室外场景的可视化质量。 + +**设置:** + +```bash +# Install onnxruntime (required) +pip install onnxruntime # CPU +# or +pip install onnxruntime-gpu # GPU (faster for large image sets) +``` + +首次使用时会从 [Hugging Face](https://huggingface.co/JianyuanWang/skyseg/resolve/main/skyseg.onnx) 自动下载天空分割模型(`skyseg.onnx`)。 + +**用法:** + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky +``` + +天空遮罩会缓存在 `_sky_masks/` 中,后续运行可跳过重新生成。也可以用 `--sky_mask_dir` 指定自定义缓存目录,或用 `--sky_mask_visualization_dir` 保存并排对比的遮罩可视化结果: + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky \ + --sky_mask_dir /path/to/cached_masks/ \ + --sky_mask_visualization_dir /path/to/mask_viz/ +``` + +### 可视化选项 + +| 参数 | 默认值 | 说明 | +|:---|:---|:---| +| `--port` | `8080` | Viser 查看器端口 | +| `--conf_threshold` | `1.5` | 过滤低置信度点的可见性阈值 | +| `--point_size` | `0.00001` | 点云中的点大小 | +| `--downsample_factor` | `10` | 点云显示的空间下采样系数 | + +### 性能与显存 + +#### 不使用 FlashInfer(回退到 SDPA) + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --use_sdpa +``` + +#### 在 GPU 显存有限时运行 + +如果遇到显存不足问题,请尝试以下一种或两种方法: + +- **`--offload_to_cpu`**——推理期间将逐帧预测卸载到 CPU(默认启用;仅在显存充足时使用 `--no-offload_to_cpu`)。 +- **`--num_scale_frames 2`**——将双向尺度帧数量从默认的 8 减少到 2,从而降低初始尺度阶段的激活峰值。 + +#### 加快推理 + +减少相机头中的迭代细化次数,以少量位姿精度换取实际运行速度: + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --camera_num_iterations 1 +``` + +`--camera_num_iterations` 默认为 `4`;将其设为 `1` 会跳过相机头中的三次细化过程(并将其 KV 缓存缩小为四分之一)。 + +## 🎥 离线渲染流水线(`demo_render/batch_demo.py`) + +当序列对交互式 viser 查看器而言过长时,请使用此流水线,例如[上方精选的室内漫游](#-精选室内漫游约-25000-帧13-分钟)。`demo_render/batch_demo.py` 是一体化离线入口:向它提供视频或图像文件夹,一条命令即可运行模型推理并生成无需图形界面的点云穿行 MP4。它与 `demo.py` 共用同一套 PyTorch / FlashInfer / 检查点栈。 + +如果受到显存容量或 GPU 使用限制,也可以参考此实现:https://github.com/ureeey/lingbot-map-rtx4060-8g/commit/eeee84a89cc97c1e39b736b46df4ee315275700b + +### 安装(在主安装流程基础上扩展) + +**1. 渲染所需的 Python 依赖** + +```bash +pip install -e ".[vis,render]" +``` + +`render` 会引入 `open3d>=0.19` 和 `pyyaml`(核心的 `numpy<2` 约束来自 `lingbot-map` 基础安装)。此流水线的天空遮罩使用 `onnxruntime-gpu` 进行批量分割;如果尚未安装 CPU 版 `onnxruntime`,请安装它: + +```bash +pip install onnxruntime-gpu +``` + +**2. Kaolin**——与上方推荐的 PyTorch 2.8.0 + CUDA 12.8 匹配: + +```bash +pip install --index-url https://pypi.org/simple \ + kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html +``` + +> `--index-url https://pypi.org/simple` 可绕过内部镜像;否则内部镜像可能提供 PyPI 占位 wheel,导入时会引发 `ImportError`。 +> NVIDIA Kaolin 不为 PyTorch 2.9.x 发布预编译 wheel——如果因其他原因使用 2.9,请从源码构建 Kaolin(`pip install --no-build-isolation git+https://github.com/NVIDIAGameWorks/kaolin.git`,需要本地 CUDA 工具包)。其他 torch/CUDA 组合请参阅 [NVIDIA Kaolin 安装文档](https://kaolin.readthedocs.io/en/latest/notes/installation.html)。 + +**3. ffmpeg** + +```bash +sudo apt install ffmpeg # or: brew install ffmpeg +``` + +**4. CUDA 扩展**(首次运行前必需) + +```bash +cd demo_render/render_cuda_ext && python setup.py build_ext --inplace && cd ../.. +``` + +这会在原位置构建 `voxel_morton_ext` 和 `frustum_cull_ext`——`rgbd_render` 会导入二者,用于 GPU 体素化和视锥剔除。 + +### 完整示例——长室内漫游(约 25,000 帧,13 分钟) + +**数据集:**从 Hugging Face 上的 [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) 下载示例视频。 + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/indoor_travel.MP4 \ + --output_folder /data/outputs/indoor_travel/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/indoor.yaml \ + --mode windowed --window_size 128 \ + --keyframe_interval 10 --overlap_keyframes 8 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + +各参数的设置理由: + +| 参数 | 设置理由 | +|---|---| +| `--mode windowed --window_size 128` | 序列超过约 320 帧的 RoPE 训练范围后,需要使用滑动窗口推理;每个窗口都会重置 KV 缓存。**`window_size` 计算的是 KV 缓存槽位,而不是实际帧数**——前 `num_scale_frames`(=8)个槽位保存尺度帧,其余 `128 − 8 = 120` 个槽位保存关键帧。因此,当 `keyframe_interval = 13` 时,一个窗口覆盖 `8 + 120 × 13 = 1568` 个实际帧。 | +| `--keyframe_interval 10` | 每 10 帧仅缓存一帧作为关键帧。非关键帧仍会生成逐帧预测,但不会增大 KV 缓存。| +| `--overlap_keyframes 8` | 相邻窗口共享 8 个关键帧的上下文,内部解析为 `max(num_scale_frames, 8 × keyframe_interval) = 8 × 13 = 104` 个实际重叠帧。只要 `keyframe_interval > 1` 就建议使用,以保持跨窗口位姿对齐稳定。 | +| `--config demo_render/config/indoor.yaml` | 从室内预设初始化渲染、场景、相机和叠加层的默认值(较短深度、更贴近的跟随相机)。用户显式传入的任何 CLI 参数仍会覆盖 YAML 值。 | +| `--sky_mask_dir` / `--sky_mask_visualization_dir` | 将天空遮罩及其并排可视化结果持久化到磁盘,使后续重新运行时可以复用,而无需再次执行 ONNX 分割。(仅当 YAML 预设或 `--mask_sky` 启用天空遮罩时,渲染流水线才会使用它们。) | +| `--camera_vis default` | 在渲染视频上叠加轨迹路径和最近帧的点。 | +| `--keyframes_only_points` | 只将关键帧深度反投影到点云;非关键帧的位姿仍用于轨迹/视锥叠加层。这样可让超长序列的点云保持稀疏。 | +| `--frame_tag --frame_tag_position top_right` | 在 MP4 右上角标注 ` / Frames` 计数器。 | +| `--save_predictions` | 在 MP4 旁持久化逐帧 NPZ 文件,便于检查,或稍后使用不同相机/叠加层设置重新渲染。 | + + +将 keyframe_interval = 10 替换为 image_stride = 10 可以加快渲染。然后取消注释 demo_render/config/indoor.yaml 中的相机跟随部分,并将鸟瞰范围设为 [2000, 2500],即可复现演示中的室内穿行效果: + +image + + + + + +https://github.com/user-attachments/assets/21b444ea-e6b6-48f0-8b34-3acad41166ac + + + + + + + +### 完整示例——室外行车场景 + +**数据集:**从 Hugging Face 上的 [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) 下载示例视频。 + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/drive_frames.mp4 \ + --output_folder /data/outputs/drive/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + + +与上方室内漫游示例的不同之处: + +| 参数 | 设置理由 | +|---|---| +| `--config demo_render/config/outdoor_drive.yaml` | 从室外预设初始化默认值:启用天空遮罩、使用更深的渲染范围(`max_depth: 250`),并采用针对车辆轨迹调优的跟随相机,最后以鸟瞰画面收尾。 | +| `--image_stride 1` | 使用视频的每一帧。增大该值可对长视频或高帧率行车视频进行下采样。 | +| `--max_non_keyframe_gap 100` | 强制设置关键帧前允许连续非关键帧的上限。仅在基于光流选择关键帧(`--flow_threshold > 0`)时生效;在默认的固定间隔模式下不起作用。 | + +其余参数(`--mode windowed --window_size 128`、`--overlap_keyframes 8`、天空遮罩缓存、叠加层、`--save_predictions`)与室内示例保持一致——请参阅上方逐项参数表。 + +### 完整示例——LingBot-World 场景 + +重建由我们的世界模型 LingBot-World 生成的视频——同一流水线可直接处理生成式视频。 + +**数据集:**从 Hugging Face 上的 [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) 下载示例视频(`lingbo_world_frames.mp4`、`lingbo_world2_frames.mp4`)。 + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/lingbo_world_frames.mp4 \ + --output_folder /data/outputs/lingbo_world/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +对于第二段视频,使用 `--video_path /data/demo_videos/lingbo_world2_frames.mp4 --output_folder /data/outputs/lingbo_world2/` 运行同一命令(如果希望分别保存缓存的遮罩,请为 `--sky_mask_dir` / `--sky_mask_visualization_dir` 使用不同文件夹)。 + +所有参数都与上方的[室外行车场景](#完整示例室外行车场景)相同——仅输入视频和输出文件夹不同。各参数的设置理由请参阅行车场景和室内漫游表格。 + +image + +image + + + +### 相机路径(YAML) + +虚拟相机路径由 YAML 预设中的 `camera.segments` 列表描述,该预设通过 `--config` 传入。编辑 YAML 即可设计自己的镜头,无需修改 CLI 参数。 + +内置预设位于 `demo_render/config/`:`default.yaml`、`indoor.yaml`、`outdoor_drive.yaml`。复制其中一个并编辑 `camera:` 块。 + +#### YAML 结构 + +```yaml +camera: + fov: 60.0 # camera field of view in degrees + transition: 30 # frames blended between adjacent segments + segments: + - mode: follow # chase cam following the input trajectory + frames: [0, 1500] # rendered-frame range this segment covers (-1 = end) + back_offset: 0.3 # how far behind the input camera (fraction of scene scale) + up_offset: 0.08 # vertical lift above the input camera + look_offset: 0.4 # how far ahead the lookat target points + smooth_window: 30 # trajectory smoothing window in frames + - mode: birdeye # rise up for a top-down reveal of the whole scene + frames: [1500, 1800] + reveal_height_mult: 2.5 # birdeye height = scene scale × this factor + - mode: follow # drop back into chase cam + frames: [1800, -1] + back_offset: 0.3 + up_offset: 0.08 + look_offset: 0.4 +``` + +`transition` 控制相邻片段之间混合多少帧;`frames: [0, -1]` 表示“整个序列”。 + +#### 可用模式 + +| `mode` | 行为 | 可调字段 | +|---|---|---| +| `follow` | 追踪相机以平滑偏移跟随输入轨迹,是漫游中最具电影感的选择。 | `back_offset`、`up_offset`、`look_offset`、`smooth_window`、`scale_frames` | +| `birdeye` | 从俯视视角展示整个场景,适合主视觉或全景镜头。 | `reveal_height_mult` | +| `static` | 固定 eye + lookat,由片段起始帧自动推导。 | — | +| `pivot` | 固定 eye,lookat 沿轨迹扫动。 | — | + +#### 单镜头 YAML 示例 + +**纯跟随**(最常用): + +```yaml +camera: + fov: 60.0 + segments: + - mode: follow + frames: [0, -1] + back_offset: 0.3 + up_offset: 0.08 + look_offset: 0.4 + smooth_window: 30 +``` + +**全程鸟瞰**(适合全景/主视觉镜头): + +```yaml +camera: + fov: 60.0 + segments: + - mode: birdeye + frames: [0, -1] + reveal_height_mult: 2.5 +``` + +**跟随镜头中插入鸟瞰**:只需在 `segments:` 下按顺序列出多个片段,相邻片段会使用 `transition` 帧进行插值。 + +> 注意:当 `--config` 加载 YAML 预设时,传入**任何**用于设定片段形态的 CLI 参数(`--camera_mode`、`--back_offset`、`--up_offset`、`--look_offset`、`--smooth_window`、`--follow_scale_frames`、`--birdeye_start`、`--birdeye_duration`、`--reveal_height_mult`)都会丢弃 YAML 中的 `segments`,改为根据这些参数重建相机路径。若要完全由 YAML 驱动,请勿在命令行中传入这些参数。 + +### 输出文件 + +对于给定的输出名称(例如 `` 或 ``): + +| 文件 | 说明 | +|------|-------------| +| `_pointcloud.mp4` | 渲染后的点云穿行视频 | +| `_pointcloud_rgb.mp4` | 编码为视频的原始 RGB 帧 | +| `_pointcloud_config.yaml` | 本次运行的完整配置快照 | +| `batch_results.json` | 各场景成功状态/耗时汇总 | + +## 📜 许可证 + +本项目根据 Apache License 2.0 发布。详情请参阅 [LICENSE](LICENSE.txt) 文件。 + +## 📖 引用 + +```bibtex +@article{chen2026geometric, + title={Geometric Context Transformer for Streaming 3D Reconstruction}, + 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}, + journal={arXiv preprint arXiv:2604.14141}, + year={2026} +} +``` + +## ✨ 致谢 + +感谢 Shangzhan Zhang、Jianyuan Wang、Yudong Jin、Christian Rupprecht 和 Xun Cao 提供的宝贵讨论与支持。 + +本工作基于以下优秀的开源项目构建: + +- [VGGT](https://github.com/facebookresearch/vggt) +- [DINOv2](https://github.com/facebookresearch/dinov2) +- [Flashinfer](https://github.com/flashinfer-ai/flashinfer) + +--- From 0bcb4524910cb95c1a5472a904abc15f7ee25672 Mon Sep 17 00:00:00 2001 From: tura-ai-agent Date: Thu, 23 Jul 2026 00:25:32 +0200 Subject: [PATCH 2/2] docs: preserve README line endings --- README.md | 1170 ++++++++++++++++++++++++++--------------------------- 1 file changed, 585 insertions(+), 585 deletions(-) diff --git a/README.md b/README.md index d5cbe97..f564063 100644 --- a/README.md +++ b/README.md @@ -1,590 +1,590 @@ -
- - -

LingBot-Map: Geometric Context Transformer for Streaming 3D Reconstruction

- +
+ + +

LingBot-Map: Geometric Context Transformer for Streaming 3D Reconstruction

+ Robbyant Team [English](README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md)
- -
- -[![Paper](https://img.shields.io/static/v1?label=Paper&message=arXiv&color=red&logo=arxiv)](https://arxiv.org/abs/2604.14141) -[![PDF](https://img.shields.io/static/v1?label=Paper&message=PDF&color=red&logo=adobeacrobatreader)](lingbot-map_paper.pdf) -[![Project](https://img.shields.io/badge/Project-Website-blue)](https://technology.robbyant.com/lingbot-map) -[![HuggingFace](https://img.shields.io/static/v1?label=%F0%9F%A4%97%20Model&message=HuggingFace&color=orange)](https://huggingface.co/robbyant/lingbot-map) -[![ModelScope](https://img.shields.io/static/v1?label=%F0%9F%A4%96%20Model&message=ModelScope&color=purple)](https://www.modelscope.cn/models/Robbyant/lingbot-map) -[![License](https://img.shields.io/badge/License-Apache--2.0-green)](LICENSE.txt) - -
- -https://github.com/user-attachments/assets/fe39e095-af2c-4ec9-b68d-a8ba97e505ab - ------ - -### 🗺️ 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. - ---- - -## 📑 Table of Contents - -
-Click to expand - -- [📰 News](#-news) -- [📋 TODO](#-todo) -- [⚙️ Installation](#️-installation) -- [📦 Model Download](#-model-download) -- [🚀 Quick Start](#-quick-start) -- [🎬 Interactive Demo (`demo.py`)](#-interactive-demo-demopy) - - [Try the Example Scenes](#try-the-example-scenes) - - [Streaming with Keyframe Interval](#streaming-with-keyframe-interval) - - [Windowed Inference (for long sequences, >3000 frames)](#windowed-inference-for-long-sequences-3000-frames) - - [Sky Masking](#sky-masking) - - [Visualization Options](#visualization-options) - - [Performance & Memory](#performance--memory) -- [🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`)](#-offline-rendering-pipeline-demo_renderbatch_demopy) -- [📜 License](#-license) -- [📖 Citation](#-citation) -- [✨ Acknowledgments](#-acknowledgments) - -
- ---- - -## 📰 News - -- **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/](benchmark/) for the pipeline, and run [`preprocess/oxford.py`](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](#worked-example--long-indoor-walkthrough-25-000-frames-13-minutes) 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**. - ---- - -## 📋 TODO - -- ✅ Release evaluation benchmark - - ✅ Oxford Spires dataset - - ✅ KITTI dataset - - ✅ VBR dataset - - ✅ Droid-W dataset - - ✅ TUM-D dataset - - ✅ 7-scenes dataset - - ✅ ETH3D dataset - - ✅ Tanks and Temples dataset - - ✅ NRGBD dataset -- ✅ Release demo scripts - - ✅ Indoor long-video demo ([Featured indoor walkthrough](#-featured-indoor-walkthrough-25-000-frames-13-minutes)) - - ✅ Outdoor long-video demo - - ✅ LingBot-World demo ([Worked example](#worked-example--lingbot-world-scenes)) - - ✅ Aerial long-video demo - ---- - -## ⚙️ Installation - -**1. Create conda environment** - -```bash -conda create -n lingbot-map python=3.10 -y -conda activate lingbot-map -``` - -**2. Install PyTorch (CUDA 12.8)** - -```bash -pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128 -``` - -> 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](https://pytorch.org/get-started/locally/). - -**3. Install lingbot-map** - -```bash -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: - -```bash -pip install --index-url https://pypi.org/simple flashinfer-python -``` - -> `--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](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`. - -**5. Visualization dependencies (optional)** - -```bash -pip install -e ".[vis]" -``` - -## 📦 Model Download - -| Model Name | Huggingface Repository | ModelScope Repository | Description | -| :--- | :--- | :--- | :--- | -| 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. | -| 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. | -| 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). | - -> 🚧 **Coming soon:** we're training an stronger model that supports longer sequences — stay tuned. - -## 🚀 Quick Start - -After installation, run your first scene with one command: - -```bash -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/courthouse --mask_sky -``` - -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. - -## 🎬 Interactive Demo (`demo.py`) - -Run `demo.py` for interactive 3D visualization via a browser-based [viser](https://github.com/nerfstudio-project/viser) viewer (default `http://localhost:8080`). - -### Try the Example Scenes - -We provide three example scenes in `example/` that you can run out of the box: -```bash -# courthouse scene -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/courthouse --mask_sky -``` - - -https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8 - - - - - - -```bash -# University scene -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/university --mask_sky -``` - - -https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57 - - - - - - - -```bash -# Loop scene (loop closure trajectory) -python demo.py --model_path /path/to/lingbot-map.pt \ - --image_folder example/loop -``` - - -https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75 - - - - - -#### 🎯 Featured: indoor walkthrough (~25 000 frames, 13 minutes) - - -*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.* - -We will provide more examples in the follow-up. - -### Dynamic Demo (From Droid-W) - -**Dataset:** Download the demo sequences from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -Example run on the `dynamic` sequence from the dataset above (sky masking on, 4 camera optimization iterations, keyframe every 2 frames): - -Run the `dynamic` sequence with sky masking, 4 camera optimization iterations, and an input stride of 2: - -```bash -python demo.py \ - --image_folder /path/to/dynamic\ - --model_path ../../Lingbot-Map/lingbot-map.pt \ - --camera_num_iterations 4 \ - --mask_sky \ - --stride 2 -``` - - - -https://github.com/user-attachments/assets/567b6e9b-1cbf-402a-96be-9bab70715ec3 - -image - - - - - -### 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.). In demo.py, the keyframe interval is calculated automatically. - -> **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) - -```bash -python demo.py --model_path /path/to/lingbot-map.pt \ - --video_path video.mp4 --fps 10 \ - --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2 -``` - - -### Sky Masking - -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 -# Install onnxruntime (required) -pip install onnxruntime # CPU -# or -pip install onnxruntime-gpu # GPU (faster for large image sets) -``` - -The sky segmentation model (`skyseg.onnx`) will be automatically downloaded from [HuggingFace](https://huggingface.co/JianyuanWang/skyseg/resolve/main/skyseg.onnx) on first use. - -**Usage:** - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --mask_sky -``` - -Sky masks are cached in `_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`: - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --mask_sky \ - --sky_mask_dir /path/to/cached_masks/ \ - --sky_mask_visualization_dir /path/to/mask_viz/ -``` - -### Visualization Options - -| Argument | Default | Description | -|:---|:---|:---| -| `--port` | `8080` | Viser viewer port | -| `--conf_threshold` | `1.5` | Visibility threshold for filtering low-confidence points | -| `--point_size` | `0.00001` | Point cloud point size | -| `--downsample_factor` | `10` | Spatial downsampling for point cloud display | - -### Performance & Memory - -#### Without FlashInfer (SDPA fallback) - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --use_sdpa -``` - -#### Running on Limited GPU Memory - -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: - -```bash -python demo.py --model_path /path/to/checkpoint.pt \ - --image_folder /path/to/images/ --camera_num_iterations 1 -``` - -`--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×). - -## 🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`) - -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`. - -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 - -### Install (extends the main install) - -**1. Rendering Python dependencies** - -```bash -pip install -e ".[vis,render]" -``` - -`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`: - -```bash -pip install onnxruntime-gpu -``` - -**2. Kaolin** — matches the PyTorch 2.8.0 + CUDA 12.8 recommended above: - -```bash -pip install --index-url https://pypi.org/simple \ - kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html -``` - -> `--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](https://kaolin.readthedocs.io/en/latest/notes/installation.html). - -**3. ffmpeg** - -```bash -sudo apt install ffmpeg # or: brew install ffmpeg -``` - -**4. CUDA extensions** (required before first run) - -```bash -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) - -**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -```bash - python demo_render/batch_demo.py \ - --video_path /data/demo_videos/indoor_travel.MP4 \ - --output_folder /data/outputs/indoor_travel/ \ - --model_path /path/to/lingbot-map.pt \ - --config demo_render/config/indoor.yaml \ - --mode windowed --window_size 128 \ - --keyframe_interval 10 --overlap_keyframes 8 \ - --sky_mask_dir /data/outputs/sky_masks \ - --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ - --camera_vis default --keyframes_only_points \ - --frame_tag --frame_tag_position top_right \ - --save_predictions -``` - -image - -Flag-by-flag rationale: - -| Flag | Why it's there | -|---|---| -| `--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. | -| `--keyframe_interval 10` | Cache only every 10th 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 ` / 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. | - - -Replacing keyframe_interval = 10 with image_stride = 10 speeds up rendering. Then, uncomment the camera follow section in demo_render/config/indoor.yaml and set the birdeye's ranges to [2000, 2500] to reproduce the indoor fly-through effect shown in the demo: - -image - - - - - -https://github.com/user-attachments/assets/21b444ea-e6b6-48f0-8b34-3acad41166ac - - - - - - - -### Worked Example — outdoor drive scene - -**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -```bash - python demo_render/batch_demo.py \ - --video_path /data/demo_videos/drive_frames.mp4 \ - --output_folder /data/outputs/drive/ \ - --model_path /path/to/lingbot-map.pt \ - --config demo_render/config/outdoor_drive.yaml \ - --mode windowed --window_size 128 \ - --max_non_keyframe_gap 100 --overlap_keyframes 8 \ - --image_stride 1 \ - --sky_mask_dir /data/outputs/sky_masks \ - --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ - --camera_vis default --keyframes_only_points \ - --frame_tag --frame_tag_position top_right \ - --save_predictions -``` - -image - - -What differs from the indoor walkthrough above: - -| Flag | Why it's there | -|---|---| -| `--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. | -| `--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](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. - -```bash - python demo_render/batch_demo.py \ - --video_path /data/demo_videos/lingbo_world_frames.mp4 \ - --output_folder /data/outputs/lingbo_world/ \ - --model_path /path/to/lingbot-map.pt \ - --config demo_render/config/outdoor_drive.yaml \ - --mode windowed --window_size 128 \ - --max_non_keyframe_gap 100 --overlap_keyframes 8 \ - --image_stride 1 \ - --sky_mask_dir /data/outputs/sky_masks \ - --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ - --camera_vis default --keyframes_only_points \ - --frame_tag --frame_tag_position top_right \ - --save_predictions -``` - -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](#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. - -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 -camera: - fov: 60.0 # camera field of view in degrees - transition: 30 # frames blended between adjacent segments - segments: - - mode: follow # chase cam following the input trajectory - frames: [0, 1500] # rendered-frame range this segment covers (-1 = end) - back_offset: 0.3 # how far behind the input camera (fraction of scene scale) - up_offset: 0.08 # vertical lift above the input camera - look_offset: 0.4 # how far ahead the lookat target points - smooth_window: 30 # trajectory smoothing window in frames - - mode: birdeye # rise up for a top-down reveal of the whole scene - frames: [1500, 1800] - reveal_height_mult: 2.5 # birdeye height = scene scale × this factor - - mode: follow # drop back into chase cam - frames: [1800, -1] - back_offset: 0.3 - up_offset: 0.08 - look_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. | `back_offset`, `up_offset`, `look_offset`, `smooth_window`, `scale_frames` | -| `birdeye` | Top-down reveal of the whole scene. Useful for hero / overview shots. | `reveal_height_mult` | -| `static` | Fixed eye + lookat, auto-derived from the segment's start frame. | — | -| `pivot` | Fixed eye, lookat sweeps along the trajectory. | — | - -#### Single-shot YAML examples - -**Pure follow** (most common): - -```yaml -camera: - fov: 60.0 - segments: - - mode: follow - frames: [0, -1] - back_offset: 0.3 - up_offset: 0.08 - look_offset: 0.4 - smooth_window: 30 -``` - -**Full birdeye** (good for overview / hero shots): - -```yaml -camera: - fov: 60.0 - segments: - - mode: birdeye - frames: [0, -1] - reveal_height_mult: 2.5 -``` - -**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. `` or ``): - -| File | Description | -|------|-------------| -| `_pointcloud.mp4` | Rendered point-cloud flythrough | -| `_pointcloud_rgb.mp4` | Original RGB frames encoded as video | -| `_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](LICENSE.txt) file for details. - -## 📖 Citation - -```bibtex -@article{chen2026geometric, - title={Geometric Context Transformer for Streaming 3D Reconstruction}, - 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}, - journal={arXiv preprint arXiv:2604.14141}, - year={2026} -} -``` - -## ✨ 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: - -- [VGGT](https://github.com/facebookresearch/vggt) -- [DINOv2](https://github.com/facebookresearch/dinov2) -- [Flashinfer](https://github.com/flashinfer-ai/flashinfer) - ---- + +
+ +[![Paper](https://img.shields.io/static/v1?label=Paper&message=arXiv&color=red&logo=arxiv)](https://arxiv.org/abs/2604.14141) +[![PDF](https://img.shields.io/static/v1?label=Paper&message=PDF&color=red&logo=adobeacrobatreader)](lingbot-map_paper.pdf) +[![Project](https://img.shields.io/badge/Project-Website-blue)](https://technology.robbyant.com/lingbot-map) +[![HuggingFace](https://img.shields.io/static/v1?label=%F0%9F%A4%97%20Model&message=HuggingFace&color=orange)](https://huggingface.co/robbyant/lingbot-map) +[![ModelScope](https://img.shields.io/static/v1?label=%F0%9F%A4%96%20Model&message=ModelScope&color=purple)](https://www.modelscope.cn/models/Robbyant/lingbot-map) +[![License](https://img.shields.io/badge/License-Apache--2.0-green)](LICENSE.txt) + +
+ +https://github.com/user-attachments/assets/fe39e095-af2c-4ec9-b68d-a8ba97e505ab + +----- + +### 🗺️ 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. + +--- + +## 📑 Table of Contents + +
+Click to expand + +- [📰 News](#-news) +- [📋 TODO](#-todo) +- [⚙️ Installation](#️-installation) +- [📦 Model Download](#-model-download) +- [🚀 Quick Start](#-quick-start) +- [🎬 Interactive Demo (`demo.py`)](#-interactive-demo-demopy) + - [Try the Example Scenes](#try-the-example-scenes) + - [Streaming with Keyframe Interval](#streaming-with-keyframe-interval) + - [Windowed Inference (for long sequences, >3000 frames)](#windowed-inference-for-long-sequences-3000-frames) + - [Sky Masking](#sky-masking) + - [Visualization Options](#visualization-options) + - [Performance & Memory](#performance--memory) +- [🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`)](#-offline-rendering-pipeline-demo_renderbatch_demopy) +- [📜 License](#-license) +- [📖 Citation](#-citation) +- [✨ Acknowledgments](#-acknowledgments) + +
+ +--- + +## 📰 News + +- **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/](benchmark/) for the pipeline, and run [`preprocess/oxford.py`](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](#worked-example--long-indoor-walkthrough-25-000-frames-13-minutes) 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**. + +--- + +## 📋 TODO + +- ✅ Release evaluation benchmark + - ✅ Oxford Spires dataset + - ✅ KITTI dataset + - ✅ VBR dataset + - ✅ Droid-W dataset + - ✅ TUM-D dataset + - ✅ 7-scenes dataset + - ✅ ETH3D dataset + - ✅ Tanks and Temples dataset + - ✅ NRGBD dataset +- ✅ Release demo scripts + - ✅ Indoor long-video demo ([Featured indoor walkthrough](#-featured-indoor-walkthrough-25-000-frames-13-minutes)) + - ✅ Outdoor long-video demo + - ✅ LingBot-World demo ([Worked example](#worked-example--lingbot-world-scenes)) + - ✅ Aerial long-video demo + +--- + +## ⚙️ Installation + +**1. Create conda environment** + +```bash +conda create -n lingbot-map python=3.10 -y +conda activate lingbot-map +``` + +**2. Install PyTorch (CUDA 12.8)** + +```bash +pip install torch==2.8.0 torchvision==0.23.0 --index-url https://download.pytorch.org/whl/cu128 +``` + +> 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](https://pytorch.org/get-started/locally/). + +**3. Install lingbot-map** + +```bash +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: + +```bash +pip install --index-url https://pypi.org/simple flashinfer-python +``` + +> `--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](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`. + +**5. Visualization dependencies (optional)** + +```bash +pip install -e ".[vis]" +``` + +## 📦 Model Download + +| Model Name | Huggingface Repository | ModelScope Repository | Description | +| :--- | :--- | :--- | :--- | +| 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. | +| 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. | +| 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). | + +> 🚧 **Coming soon:** we're training an stronger model that supports longer sequences — stay tuned. + +## 🚀 Quick Start + +After installation, run your first scene with one command: + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + +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. + +## 🎬 Interactive Demo (`demo.py`) + +Run `demo.py` for interactive 3D visualization via a browser-based [viser](https://github.com/nerfstudio-project/viser) viewer (default `http://localhost:8080`). + +### Try the Example Scenes + +We provide three example scenes in `example/` that you can run out of the box: +```bash +# courthouse scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/courthouse --mask_sky +``` + + +https://github.com/user-attachments/assets/aa10f7ab-8024-43c7-92f8-d56159ec85c8 + + + + + + +```bash +# University scene +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/university --mask_sky +``` + + +https://github.com/user-attachments/assets/212a1744-6ff5-4ccf-9bd4-728608248b57 + + + + + + + +```bash +# Loop scene (loop closure trajectory) +python demo.py --model_path /path/to/lingbot-map.pt \ + --image_folder example/loop +``` + + +https://github.com/user-attachments/assets/5ae0a292-b081-40c6-838c-b7c1a0538d75 + + + + + +#### 🎯 Featured: indoor walkthrough (~25 000 frames, 13 minutes) + + +*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.* + +We will provide more examples in the follow-up. + +### Dynamic Demo (From Droid-W) + +**Dataset:** Download the demo sequences from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +Example run on the `dynamic` sequence from the dataset above (sky masking on, 4 camera optimization iterations, keyframe every 2 frames): + +Run the `dynamic` sequence with sky masking, 4 camera optimization iterations, and an input stride of 2: + +```bash +python demo.py \ + --image_folder /path/to/dynamic\ + --model_path ../../Lingbot-Map/lingbot-map.pt \ + --camera_num_iterations 4 \ + --mask_sky \ + --stride 2 +``` + + + +https://github.com/user-attachments/assets/567b6e9b-1cbf-402a-96be-9bab70715ec3 + +image + + + + + +### 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.). In demo.py, the keyframe interval is calculated automatically. + +> **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) + +```bash +python demo.py --model_path /path/to/lingbot-map.pt \ + --video_path video.mp4 --fps 10 \ + --mode windowed --window_size 128 --overlap_keyframes 16 --keyframe_interval 2 +``` + + +### Sky Masking + +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 +# Install onnxruntime (required) +pip install onnxruntime # CPU +# or +pip install onnxruntime-gpu # GPU (faster for large image sets) +``` + +The sky segmentation model (`skyseg.onnx`) will be automatically downloaded from [HuggingFace](https://huggingface.co/JianyuanWang/skyseg/resolve/main/skyseg.onnx) on first use. + +**Usage:** + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky +``` + +Sky masks are cached in `_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`: + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --mask_sky \ + --sky_mask_dir /path/to/cached_masks/ \ + --sky_mask_visualization_dir /path/to/mask_viz/ +``` + +### Visualization Options + +| Argument | Default | Description | +|:---|:---|:---| +| `--port` | `8080` | Viser viewer port | +| `--conf_threshold` | `1.5` | Visibility threshold for filtering low-confidence points | +| `--point_size` | `0.00001` | Point cloud point size | +| `--downsample_factor` | `10` | Spatial downsampling for point cloud display | + +### Performance & Memory + +#### Without FlashInfer (SDPA fallback) + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --use_sdpa +``` + +#### Running on Limited GPU Memory + +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: + +```bash +python demo.py --model_path /path/to/checkpoint.pt \ + --image_folder /path/to/images/ --camera_num_iterations 1 +``` + +`--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×). + +## 🎥 Offline Rendering Pipeline (`demo_render/batch_demo.py`) + +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`. + +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 + +### Install (extends the main install) + +**1. Rendering Python dependencies** + +```bash +pip install -e ".[vis,render]" +``` + +`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`: + +```bash +pip install onnxruntime-gpu +``` + +**2. Kaolin** — matches the PyTorch 2.8.0 + CUDA 12.8 recommended above: + +```bash +pip install --index-url https://pypi.org/simple \ + kaolin -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html +``` + +> `--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](https://kaolin.readthedocs.io/en/latest/notes/installation.html). + +**3. ffmpeg** + +```bash +sudo apt install ffmpeg # or: brew install ffmpeg +``` + +**4. CUDA extensions** (required before first run) + +```bash +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) + +**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/indoor_travel.MP4 \ + --output_folder /data/outputs/indoor_travel/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/indoor.yaml \ + --mode windowed --window_size 128 \ + --keyframe_interval 10 --overlap_keyframes 8 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + +Flag-by-flag rationale: + +| Flag | Why it's there | +|---|---| +| `--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. | +| `--keyframe_interval 10` | Cache only every 10th 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 ` / 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. | + + +Replacing keyframe_interval = 10 with image_stride = 10 speeds up rendering. Then, uncomment the camera follow section in demo_render/config/indoor.yaml and set the birdeye's ranges to [2000, 2500] to reproduce the indoor fly-through effect shown in the demo: + +image + + + + + +https://github.com/user-attachments/assets/21b444ea-e6b6-48f0-8b34-3acad41166ac + + + + + + + +### Worked Example — outdoor drive scene + +**Dataset:** Download the example video from [robbyant/lingbot-map-demo](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/drive_frames.mp4 \ + --output_folder /data/outputs/drive/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +image + + +What differs from the indoor walkthrough above: + +| Flag | Why it's there | +|---|---| +| `--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. | +| `--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](https://huggingface.co/datasets/robbyant/lingbot-map-demo/tree/main) on Hugging Face. + +```bash + python demo_render/batch_demo.py \ + --video_path /data/demo_videos/lingbo_world_frames.mp4 \ + --output_folder /data/outputs/lingbo_world/ \ + --model_path /path/to/lingbot-map.pt \ + --config demo_render/config/outdoor_drive.yaml \ + --mode windowed --window_size 128 \ + --max_non_keyframe_gap 100 --overlap_keyframes 8 \ + --image_stride 1 \ + --sky_mask_dir /data/outputs/sky_masks \ + --sky_mask_visualization_dir /data/outputs/sky_mask_viz \ + --camera_vis default --keyframes_only_points \ + --frame_tag --frame_tag_position top_right \ + --save_predictions +``` + +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](#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. + +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 +camera: + fov: 60.0 # camera field of view in degrees + transition: 30 # frames blended between adjacent segments + segments: + - mode: follow # chase cam following the input trajectory + frames: [0, 1500] # rendered-frame range this segment covers (-1 = end) + back_offset: 0.3 # how far behind the input camera (fraction of scene scale) + up_offset: 0.08 # vertical lift above the input camera + look_offset: 0.4 # how far ahead the lookat target points + smooth_window: 30 # trajectory smoothing window in frames + - mode: birdeye # rise up for a top-down reveal of the whole scene + frames: [1500, 1800] + reveal_height_mult: 2.5 # birdeye height = scene scale × this factor + - mode: follow # drop back into chase cam + frames: [1800, -1] + back_offset: 0.3 + up_offset: 0.08 + look_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. | `back_offset`, `up_offset`, `look_offset`, `smooth_window`, `scale_frames` | +| `birdeye` | Top-down reveal of the whole scene. Useful for hero / overview shots. | `reveal_height_mult` | +| `static` | Fixed eye + lookat, auto-derived from the segment's start frame. | — | +| `pivot` | Fixed eye, lookat sweeps along the trajectory. | — | + +#### Single-shot YAML examples + +**Pure follow** (most common): + +```yaml +camera: + fov: 60.0 + segments: + - mode: follow + frames: [0, -1] + back_offset: 0.3 + up_offset: 0.08 + look_offset: 0.4 + smooth_window: 30 +``` + +**Full birdeye** (good for overview / hero shots): + +```yaml +camera: + fov: 60.0 + segments: + - mode: birdeye + frames: [0, -1] + reveal_height_mult: 2.5 +``` + +**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. `` or ``): + +| File | Description | +|------|-------------| +| `_pointcloud.mp4` | Rendered point-cloud flythrough | +| `_pointcloud_rgb.mp4` | Original RGB frames encoded as video | +| `_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](LICENSE.txt) file for details. + +## 📖 Citation + +```bibtex +@article{chen2026geometric, + title={Geometric Context Transformer for Streaming 3D Reconstruction}, + 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}, + journal={arXiv preprint arXiv:2604.14141}, + year={2026} +} +``` + +## ✨ 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: + +- [VGGT](https://github.com/facebookresearch/vggt) +- [DINOv2](https://github.com/facebookresearch/dinov2) +- [Flashinfer](https://github.com/flashinfer-ai/flashinfer) + +---