Skip to content

Rolling shutter

A rolling-shutter camera exposes rows sequentially, so one image spans a range of times. CamCalib solves the readout time as a parameter rather than correcting for it afterwards.

config.yaml
cameras:
- camera_id: 0
shutter_type: rolling # global | rolling

Per camera — a rig can mix global- and rolling-shutter cameras freely, and the released 4-IMU/4-camera rig does exactly that.

In a camera–IMU config, readout can be estimated in the cam-IMU stage instead:

cam_imu.yaml
camera_input:
estimate_readout: true

Projection, time offset, rolling shutter, extrinsic composition, and SLERP pose interpolation live in one unified factor. A row’s observation is projected against the trajectory at that row’s time, not the frame’s — so readout is constrained by the same residuals as everything else instead of being a post-hoc correction with no derivative.

Readout is among the weakest parameters in the graph. It is only excited by motion fast enough that rows see meaningfully different geometry — a slow, smooth recording will not constrain it, and the estimate will drift toward whatever the initialisation suggested.

Two checks worth more than a single run’s residual:

  • Repeatability. Calibrate several sequences of the same rig with batch mode and compare. A readout that moves between sequences was not observed in them.
  • Round-trip. camcalib roundtrip catches a readout that absorbed error belonging to the time offset.

Readout appears in the HTML report’s temporal-calibration section, merged with the time offset — they are read together for the reason above. Regression comparison reports break out per-collection readout and time offset so drift across a dataset family is visible.

The residual and its Jacobians: Camera model.