File size: 7,602 Bytes
281cc2c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | ---
license: mit
library_name: pytorch
tags:
- 3d-reconstruction
- camera-pose-estimation
- pointmap
- multi-camera-rig
- autonomous-driving
- waymo
- dust3r
datasets:
- waymo_open_dataset
metrics:
- pointmap_err
- pose_deg
- rig_deg
model-index:
- name: rig3r-waymo
results:
- task:
type: camera-pose-estimation
name: Multi-camera rig pose estimation
dataset:
type: waymo_open_dataset
name: Waymo Open Dataset (mini subset, held-out val split)
metrics:
- type: pose_deg
value: 1.4403
name: Pose rotation error (deg)
- type: rig_deg
value: 1.4075
name: Rig rotation error (deg)
- type: pointmap_err
value: 0.2159
name: Pointmap L2 error (scale-normalized)
---
# Open-Rig3R — Waymo (epoch 50)
Unofficial open reimplementation of **Rig3R**: a rig-aware transformer that takes multiple
camera views and predicts per-view pointmaps plus pose and rig raymaps, so a whole camera
rig is reconstructed jointly instead of one camera at a time.
Code: [engichang1467/Open-Rig3R](https://github.com/engichang1467/Open-Rig3R)
This checkpoint is epoch 50 of 50 from a single run on a Waymo mini subset. It is a
**research checkpoint from a reimplementation**, not a reproduction of the paper's
published numbers — see [Limitations](#limitations) before using it for anything.
## Results
Held-out Waymo val split, at the end of training:
| Metric | Value |
|---|---|
| Pointmap L2 error (scale-normalized) | **0.2159** |
| Pose rotation error | **1.440°** |
| Rig rotation error | **1.408°** |
| Pose centre error | 0.0156 |
| Rig centre error | 0.0161 |
### Read the validation curve carefully
`val/total` **rises** from 0.395 (epoch 6) to 0.961 (epoch 50). That is not overfitting,
and epoch 6 is not the better checkpoint.
The pointmap term is `C * err - alpha * log(C)`, where `C` is the model's own predicted
confidence. Over training `C` climbs from 1.2 to 9.98, saturating against the
`conf_max: 10.0` ceiling. The same geometric error therefore costs roughly 10x more at
epoch 50 than at epoch 6. Meanwhile the unweighted error stays flat or improves
(`pointmap_err` 0.279 → 0.216) and both angular errors fall monotonically
(14.10° → 1.44°, 14.16° → 1.41°).
| epoch | val/total | pointmap | **pointmap_err** | conf_mean | pose_deg | rig_deg |
|---|---|---|---|---|---|---|
| 6 | **0.395** | 0.253 | 0.2239 | 3.26 | 3.01 | 3.02 |
| 25 | 0.621 | 0.532 | 0.2187 | 6.86 | 1.63 | 1.59 |
| 50 | 0.961 | 0.880 | **0.2159** | 9.98 | **1.440** | **1.408** |
`val/total` is confounded by confidence saturation and should not be used for model
selection on this run. Select on `pointmap_err`, `pose_deg`, and `rig_deg` — all of which
plateau by roughly epoch 45 and are best at epoch 50.
## Usage
This is a plain `state_dict`, not a `transformers` `PreTrainedModel` — there is no
`from_pretrained`. Load it into the `Rig3R` class from the repo, with the **same
architecture arguments the checkpoint was trained with** (the class defaults differ and
will not load):
```python
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
from models.rig3r import Rig3R
model = Rig3R(
encoder_ckpt=None, # weights come from the checkpoint below, not from DUSt3R
img_size=128,
patch_size=16,
embed_dim=1024,
num_decoder_layers=2, # NOT the class default of 6
num_heads=8,
mlp_dim=4096, # NOT the class default of 2048
metadata_dropout=0.5,
)
path = hf_hub_download("mca183/rig3r-waymo", "model.safetensors")
model.load_state_dict(load_file(path))
model.eval()
# images: (B, V, 3, 128, 128) — V = n_frames * num_cameras, e.g. 2 * 5 = 10 for the full Waymo rig
# metadata is optional; the decoder is trained with per-field dropout so it runs without it
outputs = model(images, metadata=None)
```
`forward` returns a dict:
| key | shape | notes |
|---|---|---|
| `pointmap` | `(B, V, H*W, 3)` | dense per-pixel 3D points |
| `pointmap_conf` | `(B, V, H*W, 1)` | per-pixel confidence — saturated, see [Limitations](#limitations) |
| `pose_raymap` | `(B, V, P, 6)` | centre + unit direction per patch |
| `rig_raymap` | `(B, V, P, 6)` | centre + unit direction per patch |
| `camera_center_pose` | `(B, V, 3)` | one centre per view |
| `camera_center_rig` | `(B, V, 3)` | one centre per view |
| `features` | `(B, V, P, C)` | decoder patch features, for downstream heads |
`P = (128 / 16)^2 = 64` patches per view.
## Architecture
| | |
|---|---|
| Total parameters | 340.2 M (373 tensors, fp32) |
| Encoder | 303.2 M — DUSt3R ViT-L/16, **frozen** during training |
| Rig-aware decoder | 37.0 M — 2 pre-norm transformer layers, 8 heads, MLP dim 4096 |
| Heads | `pointmap_head`, `pose_raymap_head`, `rig_raymap_head` |
| Embedding dim | 1024 |
| Input resolution | 128 x 128, patch size 16 |
Encoder initialized from `DUSt3R_ViTLarge_BaseDecoder_512_dpt` and kept frozen, so only
the 37.0 M decoder and heads were trained.
## Training
| | |
|---|---|
| Dataset | Waymo Open Dataset, mini subset, full 5-camera rig (FRONT, FRONT_LEFT, FRONT_RIGHT, SIDE_LEFT, SIDE_RIGHT) |
| Views per sample | 10 (`n_frames: 2` x 5 cameras) |
| Epochs | 50 |
| Batch size | 8 |
| Optimizer | AdamW, lr 1e-4, weight decay 0.01 |
| Scheduler | Cosine annealing, `eta_min` 1e-6 |
| Precision | bf16 autocast, no grad scaler |
| Loss weights | `w_point` 1.0, `w_pose` 1.0, `w_rig` 1.0 |
| Confidence regularizer | `alpha` 0.2, `beta` 1.0, `conf_max` 10.0 |
| Metadata dropout | 0.5 per field (frame index exempt) |
| Seed | 0 |
| Hardware | NVIDIA A100 80GB PCIe |
| Wall clock | ~3h 20m |
`conf_max: 10.0` is a deliberate deviation from the paper: Eq. 3 leaves `-alpha*log(C)`
unbounded below, so the ceiling floors the pointmap term at `-alpha*log(10) = -0.46`.
Setting it to `null` restores Eq. 3 exactly.
## Limitations
- **Reimplementation, not the paper.** Unofficial; numbers here are not comparable to published Rig3R results.
- **Mini subset.** Trained on a small Waymo subset, not the full dataset. Generalization is untested.
- **128 x 128 input.** Well below the paper's resolution; pointmap detail is correspondingly coarse.
- **Shallow decoder.** 2 layers rather than 6, and the encoder is frozen throughout.
- **Waymo domain only.** Driving scenes, one rig geometry. No indoor, handheld, or non-automotive evaluation.
- **Single run, no held-out test.** Metrics are val-split only, one seed, no ablations.
- **Confidence is saturated.** `conf_mean` sits at the `conf_max` ceiling of 10.0, so predicted confidence is not calibrated and should not be read as an uncertainty estimate.
## Dataset terms
Trained on the Waymo Open Dataset, which carries its own license and terms of use. Using
this model does not grant any rights to that data — obtain it from Waymo directly and
comply with their terms.
## License
MIT (see the [source repo](https://github.com/engichang1467/Open-Rig3R)). Copyright 2025
Michael Chang. The DUSt3R encoder initialization and the Waymo Open Dataset carry their
own separate licenses.
## Citation
Rig3R (original paper):
```bibtex
@article{rig3r,
title = {Rig3R: Rig-Aware Conditioning for Learned 3D Reconstruction},
year = {2025}
}
```
This reimplementation:
```bibtex
@software{open_rig3r,
author = {Chang, Michael},
title = {Open-Rig3R: An open reimplementation of Rig3R},
url = {https://github.com/engichang1467/Open-Rig3R},
year = {2025}
}
```
|