Paper · 2023-06-08 · cs.CV
Tracking Everything Everywhere All at Once
OmniMotion fits one video at test time: a NeRF-style canonical volume and per-frame invertible networks, one latent code per frame, link every pixel across frames. It is trained on filtered pairwise flows with photometric and acceleration losses. Author-reported TAP-Vid AJ with RAFT input: 55.1 Kinetics, 51.7 DAVIS, 77.5 RGB-Stacking, the best of the compared methods.
Optical flowDense correspondenceNeural radiance field (NeRF) and variantsNormalizing flowstrackingoptical-flowtest-time-optimizationvideocorrespondence
arXivPDFCodeMarkdown14 min read
AI-compiled by claude-haiku-5-5 on 2026-11-06, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Tracking a point through a video, even when it hides behind something for a while, is hard if you only look at pairs of neighbouring frames. This paper takes a different route: for each video it trains a small model from scratch, so that the model knows where every piece of the scene is in one shared reference space. Each frame gets its own mapping into that space, and the mapping can be run in both directions, so a point followed from frame A to frame B and back returns to where it started.
The model is taught with rough motion estimates that some existing tool already produced, plus the requirement that the colours it renders match the video. Because the whole video is fitted together, a point that disappears for a while can still be placed sensibly when it comes back. The model does not separate camera motion from object motion, which is why the authors call it quasi-3D rather than a true 3D reconstruction.
TL;DR
- Test-time optimisation per video: a canonical volume (NeRF-style density and colour) plus one invertible network per frame, conditioned on a per-frame latent code §4 §4.1 §4.2.
- Trained on filtered pairwise optical flow (RAFT by default, TAP-Net as an alternative), with a flow loss, a photometric loss and an acceleration regulariser §5.1 §5.2.
- Author-reported on TAP-Vid: Ours (RAFT) reaches AJ 55.1 on Kinetics, 51.7 on DAVIS and 77.5 on RGB-Stacking, above the compared baselines on AJ tab. 1.
- Costs: all pairwise flows are computed exhaustively, and training runs for 200K iterations §5.4 app. C §7.
- Official code exists (Apache-2.0), but the paper and code disagree on several details; see Paper vs code.
Problem
Dense correspondence methods that look at two frames at a time, such as pairwise optical flow, lose a point once it is occluded, and composing their outputs over many frames produces inconsistent trajectories §4. A full dynamic 3D reconstruction would avoid both problems, but it is an ill-posed problem with camera and scene motion entangled §4. The paper asks whether long-range, occlusion-aware motion can be recovered without an explicit dynamic 3D model §4.
Method
The representation is called OmniMotion. It has two parts §4:
- Canonical volume. A coordinate network maps each canonical 3D point to a density and a colour, as in NeRF §4.1. The density tells where surfaces are in canonical space, and the colour supports a photometric loss §4.1.
- Bijections per frame. Each frame has an invertible mapping between its local 3D coordinates and the canonical space. The mapping is a Real-NVP style stack of affine coupling layers, all sharing one network but conditioned on a per-frame latent code §4.2. Composing one frame's mapping with the inverse of another's gives a cycle-consistent correspondence between any two frames §4.2 eq. 1.
The representation does not separate camera motion from scene motion, so the
authors call it quasi-3D rather than a physical reconstruction §4. The camera
is treated as part of the bijections: rendering uses a fixed orthographic
camera §4.3. To compute motion for a query pixel p in frame
i and a target frame j §4.3:
- Lift
ptoKpoints along a ray at depths sampled between the near and far bounds §4.3 app. C. - Map each point to canonical space with frame
i's bijection and query the volume for density and colour §4.2 §4.1. - Map each point into frame
jwith the inverse of framej's bijection eq. 1. - Alpha-composite the frame-
jpoints with the weights from the volume eq. 2. Project the result to 2D to obtain the predicted correspondence §4.3.
Training fits the representation to the input flows and the video colours. Input correspondences are computed for all frame pairs, filtered with a cycle check and an appearance check, and optionally extended by chaining §5.1 app. A. Each step samples frame pairs and pixels, then minimises the total loss below §5.2 app. C.
# Correspondence for one query pixel across frames [§4.3] [eq. 1] [eq. 2]
# Input: pixel (2,) in frame i; target frame j; K depths
# Output: predicted pixel (2,) in frame j
def predict_pixel(pixel, frame_i, frame_j, depths):
# Lift the pixel to K points on an orthographic ray [§4.3]
points_i = concat(pixel, depths) # (K, 3)
# Map the points into canonical space with frame i's bijection [§4.2]
canonical = to_canonical(points_i, latent_code[frame_i]) # (K, 3)
# Read density and colour from the canonical volume [§4.1]
# Contraction of canonical coordinates is applied first [app. C]
density, colour = canonical_field(canonical) # (K,), (K, 3)
# Alpha values from density [eq. 2]
alpha = 1 - exp(-density) # (K,)
# Transmittance T_k = product of (1 - alpha_l) over l < k [eq. 2]
transmittance = exclusive_cumprod(1 - alpha) # (K,)
weights = transmittance * alpha # (K,)
# Map every sample into frame j with the inverse bijection [eq. 1]
points_j = from_canonical(canonical, latent_code[frame_j]) # (K, 3)
# Composite the mapped samples [eq. 2]
composited = sum(weights[:, None] * points_j, axis=0) # (3,)
# Orthographic projection keeps the first two coordinates [§4.3]
return composited[:2] # (2,)# Total training objective for one batch [§5.2] [eq. 3] [eq. 4] [eq. 5]
# Input: sampled pairs with input flow, weight and observed colour
# Output: scalar loss
def training_loss(batch, step):
# Predict correspondences and rendered colours for sampled pixels [§4.3]
predicted_flow = predict_flows(batch) # (B, P, 2)
rendered_colour = render_colours(batch) # (B, P, 3)
# Flow loss: weighted L1 to the filtered input flow [eq. 3]
flow_loss = sum(batch.weights * abs(predicted_flow - batch.flow))
# Photometric loss: squared error to the observed colour [eq. 4]
photo_loss = sum((rendered_colour - batch.colour) ** 2)
# Acceleration: L1 of the second difference over frames i-1, i, i+1 [eq. 5]
accel = abs(x_prev + x_next - 2 * x) # (B, P, 3)
reg_loss = sum(accel)
# Photometric weight ramps from 0 to 10 over the first 50k steps [app. C]
lambda_pho = min(10.0, 10.0 * step / 50000)
# Regularisation weight is fixed at 20 [app. C]
lambda_reg = 20.0
# Total loss [eq. 6]
return flow_loss + lambda_pho * photo_loss + lambda_reg * reg_lossOther training rules from the paper:
- Adam, 200K iterations, 8 frame pairs per batch with 256 correspondences each, so 1024 correspondences per step §5.4. Learning rates and decay are in the table below app. C.
- Pairs are sampled with a maximum frame interval that starts at 20 and grows by one every 2k steps app. C.
- Hard mining: every 20k steps the model's flow predictions are cached and compared with the input flows. Half of the sampled query pixels are drawn with weights proportional to that error, and half uniformly §5.3 app. C.
- The mapped points are kept inside the predefined depth range to avoid degenerate solutions app. C. A distortion loss from mip-NeRF 360 suppresses floaters app. C.
- An auxiliary gradient loss compares colour differences between random, possibly distant, pixel pairs, and the same gradient loss is applied to the flow app. C.
Inference: the query can be any pixel in any frame. The predicted location uses the single sample with the largest alpha, not the composited average, because the authors found it visually better with similar numbers app. C. Occlusion is detected by checking the transmittance of the mapped 3D location in the target frame §6.2.
Hyperparameters
Values are the paper's unless the Notes column says otherwise. "not stated" means the paper does not give a value.
| Name | Value | Where | Notes |
|---|---|---|---|
| Optimiser | Adam | §5.4 app. C | |
| Training iterations | 200K | §5.4 | Code default is 100000 config.py; README also says 100k README.md |
| Frame pairs per batch | 8 | §5.4 | Code default default.txt |
| Correspondences per pair | 256 (1024 per batch) | §5.4 | default.txt |
| Samples per ray K | 32, stratified | §5.4 app. C | config.py, jittered during training trainer.py |
| Pixel coordinates | normalised to [-1, 1] | §5.4 | |
| Depth range | near 0, far 2 | §5.4 | config.py |
| Canonical coordinates | mip-NeRF 360 contraction before the volume | §5.4 | Contraction formula not restated in the paper; trainer.py |
| Mapping network | 6 affine coupling layers | §5.4 app. C | trainer.py |
| Coupling MLP | 3 layers, 256 channels | app. C | Each coupling layer's MLP takes the frame latent code |
| Positional encoding | 4 frequencies | §5.4 app. C | Applied to the coupling layer inputs app. C |
| Latent code | 128-d; 2-layer MLP, 256 channels, input frame time t_i | §5.4 | Time normalisation not stated; code uses (i+1)/N trainer.py |
| Canonical volume network | 3 layers, 512 channels | §5.4 | Colour and density heads not described in the paper; trainer.py |
| Learning rate, canonical volume | 3e-4 | app. C | config.py matches |
| Learning rate, mapping network | 1e-4 | app. C | config.py matches |
| Learning rate, latent code MLP | 1e-3 | app. C | config.py matches |
| LR decay | x0.5 every 20k steps | app. C | config.py matches |
| Photometric weight lambda_pho | 0 rising linearly to 10 over the first 50k steps, then fixed | app. C | trainer.py matches |
| Regularisation weight lambda_reg | 20 | app. C | trainer.py matches |
| Flow loss weight | not stated (sum of terms as in eq. 3) | §5.2 | Code uses a mean, not a sum; see Paper vs code |
| Pair weight | w = 1 / cos(Delta / N' * pi / 2), N' = current window | app. C | Code uses a different formula; see Paper vs code |
| Max frame interval | starts at 20, +1 every 2k steps | app. C | train.py config.py |
| Hard-mining cache | every 20k steps | app. C | config.py |
| Hard-mining split | half error-weighted, half uniform | app. C | |
| Cycle-consistency threshold | 3 pixels | app. A | filter_raft.py |
| Appearance threshold | DINO cosine similarity 0.5, pairs more than 3 frames apart | app. A | filter_raft.py |
| Occlusion bypass | pairs less than 3 frames apart | app. A | |
| Occlusion threshold at test time | not stated | §6.2 | Code default 0.99 config.py |
| Quantitative resolution | 256 x 256 | §6.1 | Qualitative results at 480p §6.1 |
| Kinetics subset | 100 videos, randomly sampled | §6.1 | Sampling seed not stated |
Results
Author-reported. Setup: TAP-Vid benchmark §6.1. DAVIS is 30 videos from the 2017 validation set. Kinetics is a random subset of 100 videos from 1,189 videos of 250 frames. RGB-Stacking is 50 synthetic videos of 250 frames with 30 tracks each. Baselines: RAFT chained (RAFT-C) and direct (RAFT-D), PIPs, Flow-Walk chained and direct, TAP-Net and Deformable Sprites §6.2. Metrics: average position accuracy over 1, 2, 4, 8 and 16 px (δ_avg), Average Jaccard (AJ), occlusion accuracy (OA) and temporal coherence (TC, lower is better) §6.1. Test-time-optimised methods are Ours and Deformable Sprites; the rest run feed-forward tab. 1 caption.
| Method | Kinetics AJ | Kinetics δ_avg | Kinetics OA | Kinetics TC | DAVIS AJ | DAVIS δ_avg | DAVIS OA | DAVIS TC | RGB-Stacking AJ | RGB-Stacking δ_avg | RGB-Stacking OA | RGB-Stacking TC | Source |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| RAFT-C | 31.7 | 51.7 | 84.3 | 0.82 | 30.7 | 46.6 | 80.2 | 0.93 | 42.0 | 56.4 | 91.5 | 0.18 | tab. 1 |
| RAFT-D | 50.6 | 66.9 | 85.5 | 3.00 | 34.1 | 48.9 | 76.1 | 9.83 | 72.1 | 85.1 | 92.1 | 1.04 | tab. 1 |
| TAP-Net | 48.5 | 61.7 | 86.6 | 6.65 | 38.4 | 53.4 | 81.4 | 10.82 | 61.3 | 73.7 | 91.5 | 1.52 | tab. 1 |
| PIPs | 39.1 | 55.3 | 82.9 | 1.30 | 39.9 | 56.0 | 81.3 | 1.78 | 37.3 | 50.6 | 89.7 | 0.84 | tab. 1 |
| Flow-Walk-C | 40.9 | 55.5 | 84.5 | 0.77 | 35.2 | 51.4 | 80.6 | 0.90 | 41.3 | 55.7 | 92.2 | 0.13 | tab. 1 |
| Flow-Walk-D | 46.9 | 65.9 | 81.8 | 3.04 | 24.4 | 40.9 | 76.5 | 10.41 | 66.3 | 82.7 | 91.2 | 0.47 | tab. 1 |
| Deformable Sprites | 25.6 | 39.5 | 71.4 | 1.70 | 20.6 | 32.9 | 69.7 | 2.07 | 45.0 | 58.3 | 84.0 | 0.99 | tab. 1 |
| Ours (TAP-Net input) | 53.8 | 68.3 | 88.8 | 0.77 | 50.9 | 66.7 | 85.7 | 0.86 | 73.4 | 84.1 | 92.2 | 0.11 | tab. 1 |
| Ours (RAFT input) | 55.1 | 69.6 | 89.6 | 0.76 | 51.7 | 67.5 | 85.3 | 0.74 | 77.5 | 87.0 | 93.5 | 0.13 | tab. 1 |
Ablations on DAVIS, with the full model as reference tab. 2 app. B:
| Variant | AJ | δ_avg | OA | TC | Source |
|---|---|---|---|---|---|
| Plain 2D (MLP from pixel and times to target pixel) | 11.6 | 19.8 | 76.7 | 1.25 | app. B |
| No invertible mapping (separate forward and backward nets) | 12.5 | 21.4 | 76.5 | 0.97 | tab. 2 app. B |
| No flow loss | 23.9 | 37.3 | 70.8 | 1.75 | app. B |
| No photometric loss | 42.3 | 58.3 | 84.1 | 0.83 | tab. 2 |
| Uniform sampling instead of hard mining | 47.8 | 61.8 | 83.6 | 0.88 | tab. 2 |
| K = 8 samples per ray | 48.1 | 63.5 | 84.6 | 0.75 | app. B |
| K = 16 samples per ray | 49.7 | 65.0 | 85.6 | 0.84 | app. B |
| Full | 51.7 | 67.5 | 85.3 | 0.74 | tab. 2 |
Qualitative results and pseudo-depth maps are described in fig. 3 and fig. 4. The paper's claims are these: the method tracks through long occlusions where PIPs cannot, gives smoother trajectories than direct pairwise methods, and beats chained methods on longer videos §6.3. The pseudo-depth maps show a relative depth ordering, not physical depth §6.4.
Paper vs code
Official code: the project page links https://github.com/qianqianwang68/omnimotion, a repository under the first author's account, with Apache-2.0 licence. The appendix says the code will be released on acceptance app. C. The code is at commit 43868e5d214b795f00c586b790cb832a6f571a4d. Searches made: the paper's own links, the arXiv abstract, the project page, and the first author's GitHub account. Citations below refer to that commit.
- Pair weight formula. The paper's weight is 1 / cos(Delta / N' * pi / 2), which grows as the interval grows app. C. The code uses cos((Delta - 1) / N' * pi / 2), which falls from 1 towards 0 as the interval grows raft.py. These point in opposite directions. Pick one and say which in your implementation.
- Loss normalisation. Paper: flow loss as a sum of L1 terms eq. 3, photometric loss as a sum of squared errors eq. 4, regulariser as a sum of L1 terms eq. 5. Code: the flow loss is a weighted mean of L1 with the normalisation switched off, after dropping the single largest element trainer.py criterion.py; the photometric loss is a mean of squared errors trainer.py; the regulariser is a mean-based L1 trainer.py criterion.py. The two differ by constant factors per term, which changes the effective lambda values.
- Regulariser support. The paper applies the acceleration regulariser to the union of all local 3D samples eq. 5 §5.2. The code applies it to the first quarter of the sampled points in the batch only trainer.py.
- Inference sample. The paper says the single sample with the largest alpha is used app. C. The code takes the sample with the largest blending weight, alpha times transmittance trainer.py trainer.py.
- Occlusion. The paper checks the transmittance at the mapped 3D location §6.2. The code sums the blending weights of samples in front of the mapped point and thresholds that sum at 0.99 by default trainer.py config.py. The threshold is not stated in the paper.
- Input-flow filtering. The paper's cycle threshold (3 px) and DINO check (cosine above 0.5, pairs more than 3 frames apart) match the preprocessing code app. A filter_raft.py filter_raft.py, except the code applies the DINO check at 3 frames or more. The occlusion bypass for pairs closer than 3 frames uses an extra factor of 1.5 on the forward-backward error filter_raft.py, which the paper does not state. In the training loader, pairs one frame apart keep every pixel raft.py. The paper limits the bypass to occluded-region correspondences app. A.
- Training length. The paper trains for 200K iterations §5.4. The code's default is 100000 config.py, and the README says 100k README.md.
- Affine pre-transform. The paper does not describe a global affine transform. The code applies a per-frame 2D affine map, predicted from time and depth, before the coupling layers when
use_affineis set nvp_simplified.py nvp_simplified.py. The provided config turns it on default.txt; the argument default is off config.py. - Losses not in the paper's main equations. The code adds a depth-range loss with weight 10 trainer.py trainer.py, a distortion loss whose weight starts at step 40k and grows by 1/2000 per step up to 10 trainer.py trainer.py, and a flow-gradient loss whose weight grows by 1/500000 per step up to 0.1 trainer.py. The colour-gradient loss is weighted by the photometric schedule trainer.py. The paper gives no weights for these app. C. A canonical-sphere loss exists but its weight is 0 by default trainer.py.
- Hard mining details. The paper describes pixel-level error sampling §5.3 app. C. The code also draws the base frame with probability proportional to its per-frame flow error raft.py, blurs the error map with a 5 x 5 Gaussian raft.py, and gives pairs one frame apart a weight of 0.5 when choosing the second frame raft.py.
- Coupling layer details. The paper says the coupling layers alternate their split pattern app. C. The code draws a random permutation of the three coordinates for each group of three layers at construction nvp_simplified.py. The code also clamps the scale output to [-8, 8] nvp_simplified.py nvp_simplified.py; the clamp is not in the paper.
- Matches. Learning rates, LR decay, photometric ramp, lambda_reg, pairs per batch, sample count, depth range, mapping and volume architectures, contraction and the window schedule all match the paper config.py trainer.py trainer.py train.py.
Pitfalls
- Pair weight. The paper's formula and the code's formula disagree in direction (see Paper vs code 1). Test both on one sequence before choosing.
- Units of the losses. Sums in the paper and means in the code mean the lambda values do not transfer as written. Decide which normalisation you implement and state it.
- Iteration budget. 200K in the paper, 100k in the code and README. Full training takes about 8 to 9 hours on an A100 according to the README README.md.
- Cost scales quadratically. Exhaustive pairwise flow costs grow with the square of the frame count, and the authors name this as a limitation §7. Chaining and appearance checks add preprocessing app. A.
- Sensitivity to initialisation. The optimisation is non-convex. The authors report occasional wrong surface ordering or duplicated objects in canonical space that optimisation can fail to fix §7.
- Fast-moving and thin structures are hard for the input flows, and so for this method §7.
- Occlusion decision. The paper describes a transmittance check §6.2. The code turns an accumulated weight into a binary decision with a threshold of 0.99 (see Paper vs code 5), which the paper does not state.
- Hidden hard-coded device. The fixed positional encoding creates its frequency tensor on
cudanvp_simplified.py, so the code will not run on CPU as written. - Global SSL change. Importing the criterion module disables HTTPS certificate verification for the whole Python process criterion.py. Remove that line before reusing the module.
- NaN handling. On a NaN the training loop drops into the Python debugger trainer.py trainer.py. Replace these with an error for batch runs.
- Test-time optimisation per video. Every video is a new training run. Results cannot be reused across videos, so the method's output for one video says nothing about another.
- Subset sampling. Kinetics results use a random subset of 100 videos without a stated seed §6.1, so the exact subset cannot be recreated from the paper.
- Version. This card follows arXiv v2 (updated 2023-09-12), the version in the metadata this card was compiled from.
Lineage
- Builds on: NeRF-style volume rendering and coordinate networks §4.1 app. C (mildenhall2021nerf in the paper's bibliography; no card); Real-NVP affine coupling for invertible mappings §4.2 (dinh2016density; no card); RAFT and TAP-Net as input correspondences §5.1 (no card); mip-NeRF 360 contraction and distortion loss app. C (no card).
- Compared against: RAFT, TAP-Net, PIPs, Flow-Walk and Deformable Sprites §6.2. None has a card yet.
- Superseded by: not stated in this card's sources.
Source and license
Adapted from “Tracking Everything Everywhere All at Once” by Qianqian Wang, Yen-Yu Chang, Ruojin Cai, Zhengqi Li, Bharath Hariharan, Aleksander Holynski, Noah Snavely (arXiv:2306.05422), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/qianqianwang68/omnimotion at commit 43868e5d214b (Apache-2.0). Any quoted code is shown under that license.