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.tomlhas no lockfile; unpinned ranges risk shipping different transitive dependencies than were tested. - [HIGH] Make CI invoke the test suite it has -
.github/workflowscontains no test command; 5 test files exist but never run in CI. - [MEDIUM] Declare least-privilege permissions for GITHUB_TOKEN -
.github/workflows/build.ymldeclares nopermissionsblock, 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.pycatchesExceptionbroadly, swallowing errors indiscriminately. - [MEDIUM] File opened without context manager x7 -
open(...)calls insrc/romav2/benchmarks/mega1500.py,src/romav2/benchmarks/scannet1500.py, andsrc/romav2/romav2.pyare not wrapped inwith. - [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.