Get each camera's numbers right
A rig multiplies whatever you got wrong about one camera. How to export camera intrinsics.
Rendering from several cameras is easy. Rendering from several cameras at the same instant, and being able to prove afterwards that you did, is the part that decides whether the dataset is usable for stereo, for multi-view, or for anything that compares one view against another.
The obvious rig is a loop: for each camera, position it, render, save. That produces the right number of images with the right names, and it is wrong in a way nothing in the output announces.
Between the first render and the last, the world moved. Animations advanced, the physics stepped, foliage swayed, a character’s foot came down. The images are not views of one moment, they are views of consecutive moments from different places, and the difference between the two is indistinguishable from parallax.
A stereo network trained on that learns a disparity that partly encodes time. It performs well on more of the same and fails on a real rig, which genuinely is synchronised. Nothing in the pipeline reports a fault, so it survives to training.
One frame means one scene state and many cameras, in that order. Advance the world once. Freeze it. Then render every camera against the frozen state, write every view, and only then let time move again.
That is a different program from the loop above, and the difference is where the world tick sits. If any part of the engine is still stepping while the cameras take turns, the rig is a sequence of single camera captures wearing a multi camera name.

The second failure is subtler and shows up months later, when somebody tries to use the dataset for geometry. Two cameras in a rig share a scene and share a moment, and share nothing else. Each has its own position, its own orientation, and, if the lenses differ, its own intrinsics.
A capture that writes one camera record per frame, because the single camera case only ever needed one, silently attributes every view to the first camera’s pose. Reprojection then fails for every view except one, and the failure looks like a calibration problem rather than a bookkeeping problem.
frames/
plugin_000004/
cam_left/
rgb.png
seg.png
depth.npy
frame.json # this camera's pose and intrinsics
cam_right/
rgb.png
seg.png
depth.npy
frame.json # a different pose, same instant
scene.json # what was true of the world for both of themThe shape matters more than the exact names. Anything shared by the views, what the objects were and where, belongs once at the frame level. Anything that differs per view belongs beside that view. A layout that cannot express the difference will eventually be asked to, and will answer with the wrong camera.
Give every camera a stable id in the level and use it in the path. Not an index: indices renumber when somebody deletes a camera, and a dataset where cam_1 means the left camera in the first half of the run and the right camera in the second half is worse than one with no ids at all, because it looks fine.
As long as the same physical camera keeps the same name from the first frame to the last, the pairing survives every later change.
| Rig | What it is for | The thing that ruins it |
|---|---|---|
| Stereo pair | Disparity and depth supervision | Any world motion between the two renders, which reads as extra disparity |
| Surround, four to six cameras | Coverage without moving the platform | Sharing one pose across views, so only the first reprojects correctly |
| Chase camera plus a mounted sensor | Showing a sensor's output against a third-person view | Assuming the sensor sees what the camera sees, when parallax means it does not |
The third row is worth a note because it is the one people underestimate. A sensor mounted on a vehicle and a camera watching that vehicle from fifteen metres away do not share an occlusion set. The sensor sees round things the camera cannot, and those returns land, in the camera’s image, on whatever is in the way. The rig is working correctly. A check that compares the two views has to account for it, or it reports a fault nobody has.
Put something moving in the scene, fast, and capture. Then look at that object in every view of a single frame. If the rig is synchronised, it is in one place, seen from several angles. If it is not, it is in several places, and the faster it moves the more obvious the smear across the views.
Do this with an object crossing the frame quickly rather than with a walking character. A slow subject hides the fault, and the whole point of the check is to make the fault impossible to miss.
The second check is arithmetic. Take an object the capture recorded a 3D position for, project it into every view using that view’s own intrinsics and pose, and confirm each lands inside that object’s box in that view. One view landing correctly and the rest drifting is the shared-pose bug above.
A rig multiplies whatever you got wrong about one camera. How to export camera intrinsics.
The per-frame layout, the camera ids and what is written once against per view: multi-camera capture in the documentation.