The Problem

RoMaV2 is a feature-matching system that produces dense correspondences between image pairs, but the repository ships without a dependency lockfile and its CI pipeline never executes the test suite. A green check that never runs assertions creates a false sense of reliability, and unpinned dependency ranges mean the artifact tested can differ from the artifact shipped.

What This Does

RoMaV2 implements a Vision Transformer (ViT) architecture with local correlation kernels for dense matching. The core model lives in src/romav2/romav2.py, which defines RoMaV2 with forward, match, sample, and to_pixel_coordinates methods. Matching proceeds through src/romav2/vit/attention.py (rope-augmented attention, 12 functions/3 classes) and src/romav2/vit/block.py (rope indexing, 8 functions/2 classes). A fused-local-corr kernel is available via --extra fused-local-corr. Benchmarks for Mega-1500 and ScanNet-1500 live in src/romav2/benchmarks/, each calling into the model and writing results files. The call graph shows benchmark invoked from 12 places, apply_setting from 8, and RoMaV2 from 7 — these are the primary entry points for execution and configuration.

How It Is Wired

Execution starts at src/romav2/romav2.py:__init__ which constructs the ViT backbone, then forward routes through src/romav2/vit/attention.py:forward and src/romav2/vit/block.py:_forward_list. The most connected hub is src/romav2/vit/__init__ (Ca 0, Ce 6, instability 1) — it is imported by six downstream modules including the attention and block layers, so changes there ripple widely. The internal call graph has 152 resolved edges; benchmark -> pose_auc fires 5 times, benchmark -> sample 4 times, and __init__ -> _make_fusion_block 4 times. The entry point reads image paths, runs the model, and the benchmark scripts (src/romav2/benchmarks/mega1500.py, src/romav2/benchmarks/scannet1500.py) open files without context managers (7 occurrences), risking handle leaks. The widest blast radius sits in src/romav2/romav2.py which calls into 3 internal modules and reads/writes files; a failure there aborts all downstream matching.

How To Use It

Setup: In a Python 3.12 environment on Linux:

uv pip install -e .

or

uv sync

Install the fused-local-corr extra if needed:

uv pip install romav2[fused-local-corr]

Configuration: No separate config file is required; settings are exposed through the RoMaV2 object attributes (model.H_lr, model.W_lr, model.H_hr, model.W_hr, model.bidirectional, model.threshold, model.balanced_sampling). Call model.apply_setting(...) to tweak them at runtime.

Running it:

from romav2 import RoMaV2

model = RoMaV2()
preds = model.match(img_A_path, img_B_path)
matches, overlaps, precision_AB, precision_BA = model.sample(preds, 5000)
kptsA, kptsB = model.to_pixel_coordinates(matches, H_A, W_A, H_B, W_B)

The README also provides CLI-style invocations: uv run tests/test_mega1500.py and uv run tests/test_scannet1500.py after running source scripts/eval_prep.sh to download MegaDepth and ScanNet.

Real-World Use

A typical perception pipeline would load a pretrained RoMaV2 model, compute dense matches between consecutive frames, denormalize pixel coordinates with to_pixel_coordinates, and feed the keypoints to a downstream solver (e.g., cv2.findFundamentalMat with RANSAC). The apply_setting method lets an engineer adjust model.threshold for MegaDepth overlap prediction or enable model.bidirectional for more diverse correspondences before RANSAC estimation.

Code Health & Issues

  • [HIGH] Commit a lockfile beside the manifest - pyproject.toml has no lockfile; unpinned ranges risk shipping different transitive dependencies than were tested.
  • [HIGH] Make CI invoke the test suite it has - .github/workflows contains no test command; 5 test files exist but never run in CI.
  • [MEDIUM] Declare least-privilege permissions for GITHUB_TOKEN - .github/workflows/build.yml declares no permissions block, so the token inherits repository defaults and can push or mint releases from within CI.
  • [MEDIUM] Enable Dependabot or Renovate - No update bot is configured for the manifests; published advisories go unpatched without manual audit.
  • [MEDIUM] Broad exception handling in logging - src/romav2/logging.py catches Exception broadly, swallowing errors indiscriminately.
  • [MEDIUM] File opened without context manager x7 - open(...) calls in src/romav2/benchmarks/mega1500.py, src/romav2/benchmarks/scannet1500.py, and src/romav2/romav2.py are not wrapped in with.
  • [LOW] Set timeout-minutes on workflow jobs - Two workflow jobs have no timeout-minutes, risking six-hour platform defaults.

The Bottom Line

The code is functional and well-structured for a research-grade matching system — the ViT backbone, rope attention, and local correlation kernel are the strongest parts. However, the lockfile gap and CI gap are dealbreakers for any production or shared use: without a pinned dependency set and a CI check that actually runs assertions, there's no guarantee the tested artifact matches the shipped one. Use it for experimentation or internal research, but lock dependencies and add a test-running step to CI before deploying anywhere downstream.