Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
aeba561
Add verified-viewgraph pipeline: verified-graph partition + post-merg…
kathirgounder Jun 23, 2026
170dc61
Fix stale BA field + tune Brussels phototourism configs (baseline + v…
kathirgounder Jun 23, 2026
77796d2
Adopt gp-glomap-parity peak frontend: PoseLibVerifier + 8192 SIFT + 3…
kathirgounder Jun 23, 2026
e08be7c
Enable scipy view-graph focal calibration (use_view_graph_calibration…
kathirgounder Jun 23, 2026
1bc7319
Cluster BA: reuse global SIFT tracks + triangulate structure (verifie…
kathirgounder Jun 23, 2026
86a5a3c
Fix worker OOM in verified pipeline: scatter global frontend without …
kathirgounder Jun 23, 2026
20adfc4
Fix view-graph calibration: return intrinsics dict (not keys) when no…
kathirgounder Jun 23, 2026
1c5ef76
Drop global-track scatter; per-cluster frontend + flag-gated triangul…
kathirgounder Jun 24, 2026
7b64162
Focal flow: global Fetzer + two-tier focal anchoring through cluster …
kathirgounder Jun 25, 2026
ede5a9b
Revert per-cluster triangulated structure to VGGT depth (keep focal-f…
kathirgounder Jun 25, 2026
1dc132b
Run cluster frontend inline; drop worker_client() nested submission
kathirgounder Jun 25, 2026
bbd79ff
Recover track-less cameras: VGGT depth no longer vetoes verified SIFT…
kathirgounder Jun 26, 2026
8d6948b
Reuse global verified correspondences per cluster (skip the per-clust…
kathirgounder Jun 26, 2026
1af1e43
Recover good-pose / no-depth cameras via the post-merge retriangulation
kathirgounder Jun 27, 2026
dc9824a
Integrate VGGT-Omega geometry into the verified pipeline (Brussels A/B)
kathirgounder Jun 27, 2026
7a7c51c
Dispatch image preprocessing on the geometry transformer (fix omega run)
kathirgounder Jun 28, 2026
652e62b
Add REVIEW.md: reviewer guide for the verified-pipeline PR
kathirgounder Jun 28, 2026
ac86c70
VGGT-depth build: drop out-of-bounds depth anchors instead of clamping
kathirgounder Jun 30, 2026
fad3cad
COLMAP-DB frontend: run the verified VGGT pipeline straight from a da…
kathirgounder Jun 30, 2026
01053e5
Lean gather: reduce two-view results to v_corr_idxs on the worker
kathirgounder Jul 1, 2026
b6db814
Stream global two-view: keep only v_corr_idxs on the worker
kathirgounder Jul 1, 2026
1e1cc46
Run global verified frontend inline in the main process (drop the das…
kathirgounder Jul 1, 2026
93d883b
Stabilize dask comms for large single-node scenes (image-load storm +…
kathirgounder Jul 1, 2026
0f82d9c
Add progress logging to the in-process frontend serial loops
kathirgounder Jul 1, 2026
ba559cb
Add ColmapDBMegaLoc retriever: sparsify the COLMAP view graph by Mega…
kathirgounder Jul 1, 2026
cd83f54
Fetzer: pass jac_sparsity to least_squares (fixes hang at scale)
kathirgounder Jul 1, 2026
ce1932a
ColmapDBMegaLoc: per-image top-K over COLMAP neighbors (fix strict-in…
kathirgounder Jul 1, 2026
07b8d05
Make ColmapCorrespondenceGenerator picklable (drop pycolmap.Database …
kathirgounder Jul 1, 2026
7d2d71b
Parallelize the global verified frontend across Dask workers (keep in…
kathirgounder Jul 2, 2026
818f7b4
Cap COLMAP SIFT to num_threads=1 (fix worker oversubscription hang)
kathirgounder Jul 2, 2026
eaca349
Skip per-cluster pre-BA reproj filter (let robust BA keep VGGT-Omega …
kathirgounder Jul 3, 2026
fa0b6f6
Turn off trackless-camera recovery in post-merge retri (paired with p…
kathirgounder Jul 3, 2026
9f7c701
./viz Babylon viewer: fix gimbal lock + big perf/UX upgrade
kathirgounder Jul 3, 2026
7faabee
Revert "Turn off trackless-camera recovery in post-merge retri (paire…
kathirgounder Jul 3, 2026
eda5725
Revert "Skip per-cluster pre-BA reproj filter (let robust BA keep VGG…
kathirgounder Jul 3, 2026
b703e43
Turn off trackless-camera recovery (audit: 55/79 injected poses were …
kathirgounder Jul 3, 2026
8e25870
Switch per-cluster 3D init to SIFT-triangulated structure (VGGT poses…
kathirgounder Jul 3, 2026
9858024
./viz: make frustum/dot sizing outlier-robust + drop garbage cameras
kathirgounder Jul 3, 2026
d87e756
Anchor merges by GLOBAL TRACK IDENTITY (gid-index sidecar) + one-rule…
kathirgounder Jul 4, 2026
e56e630
Refine merge guard from gid-run evidence: overlap escape + 0-track dr…
kathirgounder Jul 4, 2026
7f4e0b5
Weight Sim3 point correspondences to scene scale (they were cosmetic …
kathirgounder Jul 4, 2026
7af6437
RANSAC-prefilter Sim3 point pairs + soften sigma + escape structure f…
kathirgounder Jul 4, 2026
59d147d
RESET to R3-baseline merge semantics; gate gid anchoring + post-merge…
kathirgounder Jul 4, 2026
d11429a
Offline-replay telemetry: dump global tracks, cluster tree, verified …
kathirgounder Jul 4, 2026
2c93fe5
Port three offline-validated fixes: majority-vote gid identity, hones…
kathirgounder Jul 4, 2026
8e43ed6
Config: retrieval 0.10/200 (C_9 bridge edges), boundary+gid off for p…
kathirgounder Jul 4, 2026
0b4a702
Retrieval 120/0.15: validated live — K=60 floater block seats at K=120
kathirgounder Jul 5, 2026
bd70af6
Add export-time triangulation-angle track filter (merged_anglefiltered/)
kathirgounder Jul 5, 2026
3935f53
Add run_per_cluster_ba flag; OFF in verified config (BA census: degra…
kathirgounder Jul 5, 2026
12b13be
EXIF focal passthrough (calibration_source=exif) + tighten merge foca…
kathirgounder Jul 6, 2026
1d09611
Revert to golden-run machinery + EXIF focals: cluster BA back ON, mer…
kathirgounder Jul 6, 2026
1a51bf8
./viz: port pipeline-viz scene-up alignment (fix the gimbal-lock feel)
kathirgounder Jul 6, 2026
03f8327
Fix ratio-test crash on <2 knnMatch candidates (near-featureless images)
kathirgounder Jul 6, 2026
b4e85be
Precomputed-frontend injection: consume 1DSfM's released view graph +…
kathirgounder Jul 7, 2026
6f03453
Precomputed frontend: EXIF-rotation dims guard + max_resolution assert
kathirgounder Jul 7, 2026
b574e9c
Robust gated Fetzer calibration (borglab#1115 recipe): PoseLib F + pl…
kathirgounder Jul 7, 2026
32c98f5
Add precomputed_pairs_path: run our full frontend on a fixed pair lis…
kathirgounder Jul 7, 2026
a0d7d27
Pin OpenCV to 1 thread per matcher task (N-workers x all-cores oversu…
kathirgounder Jul 7, 2026
104ea5b
Add TorchTwoWayMatcher: GPU/torch mutual-NN + bidirectional ratio mat…
kathirgounder Jul 7, 2026
b5e568d
Make MERGE_GUARD Sim3 scale band configurable; widen to [0.02, 50] in…
kathirgounder Jul 8, 2026
9e3a5fd
Remove Sim3 scale band by default (band = [0, inf))
kathirgounder Jul 8, 2026
e99859f
Configurable cache root (GTSFM_CACHE_ROOT) + atomic cache writes
kathirgounder Jul 8, 2026
862f270
Fetzer gtsam solver: accept FetzerFactor/SelfCalibrationFactor names …
kathirgounder Jul 8, 2026
c482561
Expose cam_pose3_prior_noise_sigma in BundleAdjustmentOptions
kathirgounder Jul 8, 2026
9f25fb2
Add retri_free_ba + retri_iterations to post-merge retriangulation
kathirgounder Jul 8, 2026
3f68251
Expose retri_free_ba/retri_iterations keys in the verified omega config
kathirgounder Jul 8, 2026
17c5b96
retri_free_ba: drop priors only; inherit robustness from ba_options
kathirgounder Jul 8, 2026
8947430
./viz: paper-figure frustums + H to hide UI for clean shots
kathirgounder Jul 9, 2026
1bfa349
Save model-predicted intrinsics as per-cluster telemetry
kathirgounder Jul 9, 2026
4ed605d
Add modern Babylon.js reconstruction viewer (./pipeline-viz) as an op…
kathirgounder Jul 10, 2026
562e0ca
Sync colmapdb config to the peak verified config
kathirgounder Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "thirdparty/FastVGGT"]
path = thirdparty/FastVGGT
url = https://github.com/mystorm16/FastVGGT.git
[submodule "thirdparty/vggt-omega"]
path = thirdparty/vggt-omega
url = https://github.com/facebookresearch/vggt-omega.git
151 changes: 151 additions & 0 deletions REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# PR #1117 — VGGT Verified Pipeline

**16 commits · +1913 / −80 · 23 files · targets `mapping-vggt`**

This branch turns the per-cluster VGGT reconstruction into a **verified-view-graph pipeline** and stacks
four accuracy/robustness features on top of it, ending with a **VGGT-Omega** geometry arm. It's large
because it was developed as one experimental thread; this doc decomposes it into **6 self-contained
feature units** that map 1:1 to the clean branches we'll land into `master`.

## Headline result — IMC Phototourism *Grand Place Brussels* (234 images)

| Arm (config) | AUC@3° (full) | AUC@3° (constructed) | Cameras |
|---|---|---|---|
| Prior VGGT baseline (non-verified) | ~0.675 | — | ~219 |
| **VGGT — verified** (`vggt_sift_…_verified`) | **0.6968** | 0.7213 | **230** |
| **VGGT-Omega — verified** (`vggt_omega_…_verified`) | **0.7191** | **0.7380** | **231** |

Controlled A/B: the omega vs VGGT rows differ **only** in the per-cluster geometry model — same frontend,
partition, Fetzer, and retri.

## How to review this efficiently

1. **Start with the configs** — they are the table of contents. Diff
`gtsfm/configs/vggt_sift_frontend_megaloc_phototourism_verified.yaml` (every new flag is commented in place).
2. **Then `scene_optimizer.py`** (+205) — the orchestration: global two-view verification → verified
partition → global Fetzer → post-merge retriangulation.
3. **Then the per-feature files** in the order below.
4. **Mechanical vs load-bearing:** `cluster_vggt_omega_with_frontend.py` (+404) and
`vggt_omega_geometry_transformer.py` (+347) are **vendored from PR #1116** (Harneet) — review at the
interface level, not line-by-line. The load-bearing *new* logic is in `scene_optimizer.py`,
`cluster_merging.py`, `cluster_vggt_with_frontend.py`, and `view_graph_calibration.py`.

## New config flags (the feature switches — all default to the prior behavior)

| Flag | Where | Default | Effect |
|---|---|---|---|
| `use_verified_pipeline` | top-level | `false` | global two-view verification + verified-graph METIS partition + post-merge retriangulation |
| `use_view_graph_calibration` | optimizer | `false` | per-edge scipy Fetzer focal refinement |
| `use_global_view_graph_calibration` | optimizer | `false` | single **global** Fetzer over the verified graph (takes precedence) |
| `calibration_prior_focal_sigma` | ba_options | n/a | anchors focals in cluster (5px) & merge (10px) BAs |
| `reuse_global_correspondences` | optimizer | `false` | build per-cluster tracks from the global verified frontend (skip per-cluster frontend) — pure speedup |
| `recover_trackless_cameras_in_retriangulation` | merging | `false` | inject good-pose/no-track cams into the post-merge retri for a geometric second chance |

**Every flag off ⇒ byte-identical to the prior baseline.** This is the key reviewer reassurance: the diff
is additive and gated.

---

## Feature units → suggested clean branches (with landing order)

**Dependency order: ① must land first** (everything reads its `ClusterContext` fields + `use_verified_pipeline`).
**②③④⑤ are independent** of each other on top of ①. **⑥ is independent** but its config overlays ①–⑤.

### ① Verified view-graph pipeline *(foundation)*
- **Commits:** `aeba561a`, `86a5a3ca`, `1c5ef76f`, `1dc132ba`
- **What:** Runs one global two-view verification pass over the full MegaLoc retrieval graph, partitions
METIS on the **verified** subgraph (edges where `TwoViewResult.valid()`), and adds a **post-merge
retriangulation+BA** stage (`results/merged_retriangulated/`). Several follow-ups fix worker-OOM/dask-nesting
by running the global frontend **inline** instead of via nested `worker_client()` submission.
- **Key files:** `scene_optimizer.py` (orchestration), `cluster_mvo.py`, `two_view_estimator.py`
(`create_two_view_results_inline`), `frontend/correspondence_generator/*` (`generate_correspondences_inline`),
`cluster_optimizer_base.py` (new `ClusterContext` fields).
- **Review focus:** the inline-vs-dask execution model (the OOM fixes) and the verified-graph construction.

### ② Global Fetzer focal calibration + focal-flow anchoring
- **Commits:** `e08be7c8`, `20adfc40`, `7b641621`
- **What:** Estimates focals with scipy Fetzer over the verified F-matrices (`compute_global_view_graph_intrinsics`),
then **anchors** those focals through the cluster BA (σ=5px) and merge BA (σ=10px) so BAs optimize
*poses, not focals* (kills the focal/depth ambiguity that was diverging separator cameras across clusters).
- **Key files:** `graph_optimizer/view_graph_estimator/view_graph_calibration.py` (+44), `scene_optimizer.py`,
the `ba_options.calibration_prior_*` plumbing.
- **Note:** Fetzer initializes from the loader heuristic (1.2·maxdim) and **never reads VGGT/omega focals** —
important for the omega arm (its anisotropic fx/fy is irrelevant; a single-focal `Cal3Bundler` is produced).

### ③ Peak frontend (config + PoseLib verifier)
- **Commits:** `170dc610`, `77796d20`
- **What:** Adopts the "gp-glomap-parity" frontend — `PoseLibVerifier` (5-point + LO-RANSAC), 8192 ColmapSIFT
keypoints, 30/0.15 inlier gate — and retunes the Brussels baseline + verified configs.
- **Key files:** `configs/vggt_sift_frontend_megaloc_phototourism.yaml` (+ verified), `two_view_estimator.py`,
**`pyproject.toml`** (+`poselib>=2.0`).
- **Review focus:** the new `poselib` dependency + verifier swap. Mostly config; lowest-risk unit.

### ④ Global correspondence reuse *(speedup)*
- **Commits:** `1bc73192`, `ede5a9ba`, `8d6948ba`
- **What:** Each cluster builds its 2D tracks from the **already-computed** global verified correspondences
instead of re-running its own frontend (which only cache-read the same data, serially + redundantly across
overlapping clusters). Pure speedup — per-edge `v_corr` is identical. *(One commit, `ede5a9ba`, reverts an
experimental per-cluster triangulated-structure path that inflated clusters to 11k–15k tracks → OOM; it keeps
the focal-flow fix. See "reverted experiments" below.)*
- **Key files:** `cluster_vggt_with_frontend.py` (the `reuse_global_correspondences` branch),
`ClusterContext.global_v_corr_idxs_dict` / `global_keypoints`.
- **Cache token:** adds `/gcorr` to the optimizer `__repr__` (cluster-cache key).

### ⑤ Trackless-camera recovery
- **Commits:** `bbd79ff7` (build-side "all-measurements"/`/allkpts`), `1af1e43e` (retriangulation recovery)
- **What:** Two orthogonal levers to rescue cameras that get good poses but no VGGT-depth track. (a) The
depth-lift build no longer lets zero-confidence VGGT depth *veto* a verified SIFT measurement (`/allkpts`).
(b) Cameras dropped at the root merge's track filter are **captured and injected** into the post-merge
retriangulation for a geometric (depth-independent) second chance; those that still can't triangulate are
cleanly dropped. **Provably can't perturb the constructed cameras** (recovered cams have <15 tracks ⇒
excluded from the retri BA factor graph).
- **Key files:** `cluster_merging.py` (+38, capture + `trackless_cameras` on the merge result),
`scene_optimizer.py` (`_run_post_merge_retriangulation` inject + first-class diagnostic logging),
`cluster_vggt_with_frontend.py` (build).
- **Result note:** on Brussels this recovers cam 49; 104/207 remain (they're a pose-constraint problem — see
follow-ups).

### ⑥ VGGT-Omega geometry integration
- **Commits:** `dc9824af` (integration), `7a7c51ca` (preprocessing-dispatch fix)
- **What:** Vendors VGGT-Omega (from PR #1116) and runs it **through our verified optimizer**
(`ClusterVGGTWithFrontend` via its injected `geometry_transformer`), *not* the bundled
`ClusterVGGTOmegaWithFrontend` (a master-based copy lacking Fetzer/reuse/recovery). Two small enabling changes:
(a) guard `transformer.config` access (omega has none) at `__init__` + `__repr__`; (b) **generalize image
preprocessing to follow the transformer** — new `GeometryTransformer.load_image_batch` (base = VGGT loader,
omega overrides → its own 512/16-aligned loader). This is the cleanest standalone refactor in the PR and is a
**no-op for all VGGT configs**.
- **Key files:** `frontend/geometry_transformer.py` (+20, the abstraction),
`frontend/vggt_omega_geometry_transformer.py` (+347, vendored),
`cluster_optimizer/cluster_vggt_omega_with_frontend.py` (+404, vendored), `cluster_vggt.py` (loader dispatch),
`cluster_optimizer/__init__.py` (register), `configs/vggt_omega_*` (×2), `.gitmodules` +
`thirdparty/vggt-omega` submodule, `scripts/download_model_weights.sh`, `utils/torch.py`.
- **⚠️ Reviewer/ops notes:** weights are **gated, `cc-by-nc-4.0`, non-redistributable**, **CUDA-only**.
Submodule + `--hf_token` download required; omega module imports are lazy so non-omega paths/tests are
unaffected. **The geometry-transformer abstraction (a) is worth landing as its own tiny branch first** —
omega then becomes a pure add-on.

---

## Reverted experiments (intentionally *not* in the final pipeline)
- **Per-cluster triangulated structure** (`use_triangulated_structure: true`) — inflated clusters to 11k–15k
tracks → 85–200s BAs → worker OOM. Reverted to VGGT depth-lift (`ede5a9ba`); the flag remains but defaults
`false`.

## Known limitations / planned follow-ups (out of scope for this PR)
1. **Brussels 104/207 still unrecovered** — they never acquire tracks at *any* stage, so they're carried
through the Sim3 merge unconstrained (one is ~90°-flipped). This is a **pose-constraint problem →
PnP/resection**, not a geometry-model one (omega didn't and couldn't fix it).
2. **One Sim3-flipped camera** (max rot err 90.05° vs 0.13° median) dominates the mean rotation error —
separate AUC lever.
3. **Omega licensing/ops** — NC weights must stay out of any commercial/redistributed artifact.

## How to run / verify
```bash
git submodule update --init --recursive # omega arm only
bash scripts/download_model_weights.sh --hf_token <tok> # omega arm only (CUDA)
uv run ./run --config_name vggt_omega_sift_frontend_megaloc_phototourism_verified \
loader._target_=gtsfm.loader.Colmap loader.dataset_dir=$DATA \
+loader.colmap_files_subdir=sfm_updated +loader.use_gt_intrinsics=false
```
Drop `_omega` from the config name for the VGGT arm. Metrics land in `results/merged_retriangulated/…json`
(`pose_auc_@3.0_deg`, `number_cameras_merged`).
5 changes: 5 additions & 0 deletions gtsfm/bundle/bundle_adjustment.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ class BundleAdjustmentOptions:
use_calibration_prior: bool = False
use_pose_prior_all_cameras: bool = False
use_pose_prior_first_camera: bool = False
# Sigma for the PriorFactorPose3 added by the use_pose_prior_* flags, anchored at the
# initialization poses. Near-zero (e.g. 1e-3, in scene units) freezes poses so the BA
# refines only structure + calibration ("self-calibration against the init poses").
cam_pose3_prior_noise_sigma: float = 0.1
use_gnc: bool = False
gnc_loss: Union[RobustBAMode, str] = RobustBAMode.GMC
factor_weight_outlier_threshold: float = 0.0
Expand Down Expand Up @@ -188,6 +192,7 @@ def to_optimizer(self, **overrides) -> "BundleAdjustmentOptimizer":
use_calibration_prior=self.use_calibration_prior,
use_pose_prior_all_cameras=self.use_pose_prior_all_cameras,
use_pose_prior_first_camera=self.use_pose_prior_first_camera,
cam_pose3_prior_noise_sigma=self.cam_pose3_prior_noise_sigma,
use_gnc=self.use_gnc,
gnc_loss=self.gnc_loss,
factor_weight_outlier_threshold=self.factor_weight_outlier_threshold,
Expand Down
Loading
Loading