Camera rigs (stereo / multi-camera)¶
Since pyTheia 1.2.0. Calibrated multi-camera / stereo rigs as an additive layer on the usual View / Track / ViewGraph model.
Concepts¶
| Type | Meaning |
|---|---|
CameraRig |
Abstract body frame + per-sensor extrinsics (RigSensor) in that frame |
RigCapture |
One synchronized sample: one timestamp = one body pose |
View |
Still one image; features and matches stay on Views |
| Membership | Each View may belong to (rig_id, rig_camera_id, capture_id) |
Pose composition (Camera convention):
PropagateCameraPosesForCapture writes composed extrinsics into each member View.Camera.
Calibrated by default: averaging and the v1 reconstructors do not optimize inter-camera extrinsics. Set sensor poses (e.g. stereo baseline) when defining the CameraRig.
Metric relative rig pose (5+1)¶
For capture–capture edges with a known baseline, prefer metric relative pose instead of unit-scale monocular essentials:
FivePointOnePointGeneralizedRelativePose— central 5-pt on same-sensor bearings + 1 possibly cross-sensor ray to fix scale (PoseLibgen_relpose_5p1ptalgorithm, implemented in-tree).FourPointUprightGeneralizedRelativePose— upright (gravity-axis) generalized 4-pt; wraps Theia’s SweeneyFourPointRelativePosePartialRotation.EstimateRelativeRigInfo/EstimateRelativeRigInfoUpright— RANSAC overGeneralizedRayCorrespondence(ray origins + bearings in each rig frame). OutputRelativeRigInfohas metrictranslation/position; callToTwoViewInfo()for capture-graph edges.
Lift pixels with known RigSensor extrinsics into the abstract body frame, then estimate.
Degeneracy: 5+1 cannot fix scale when relative translation is parallel to the offset used by the scale ray (typical failure: side-by-side stereo baseline along \(X\) with pure \(X\) motion). Forward motion (\(Z\)) with a lateral baseline is well-conditioned.
BuildCaptureViewGraph (default): when tracks span two captures, lifts observations into the rig frame and runs EstimateRelativeRigInfo so capture edges are metric. Falls back to stripping View–View essentials only for pairs that cannot be estimated metrically. GlobalRigReconstructor then optionally rescales averaged positions to match those metric edge lengths (rescale_positions_to_metric_edges).
opts = pt.sfm.BuildCaptureViewGraphOptions()
opts.use_metric_relative_rig_pose = True
opts.fallback_to_twoview_strip = True
pt.sfm.BuildCaptureViewGraph(recon, view_graph, capture_graph, opts)
gro = pt.sfm.GlobalRigReconstructorOptions()
gro.capture_graph_options = opts
gro.rescale_positions_to_metric_edges = True
Reconstructors¶
IncrementalRigReconstructor¶
Seeds the first capture at identity, triangulates (intra-rig matches are metric), then localizes later captures (generalized 2D–3D / single-view fallback).
GlobalRigReconstructor (MGSfM-inspired)¶
BuildCaptureViewGraph— metric 5+1 from tracks when possible; optional strip of View–View essentials for missing pairs- Rotation averaging on captures — any
GlobalRotationEstimatorType(ROBUST_L1L2,NONLINEAR,LINEAR,LAGRANGE_DUAL,HYBRID) - Position averaging —
LEAST_UNSQUARED_DEVIATIONon the capture graph, or otherGlobalPositionEstimatorTypes (NONLINEAR,LINEAR_TRIPLET,LIGT,GLOMAP) via the View graph after propagating orientations; optional median rescale to metric edge lengths - Propagate → triangulate → BA (then re-snap Views to the calibrated rig)
opts = pt.sfm.GlobalRigReconstructorOptions()
opts.sfm_options.global_rotation_estimator_type = pt.sfm.GlobalRotationEstimatorType.ROBUST_L1L2
opts.sfm_options.global_position_estimator_type = pt.sfm.GlobalPositionEstimatorType.LEAST_UNSQUARED_DEVIATION
summary = pt.sfm.GlobalRigReconstructor(opts).Estimate(view_graph, reconstruction)
Example¶
See pyexamples/stereo/stereo_rig_reconstruction.py: set focal / principal point / baseline, match with vismatch (edm by default), run global or incremental rig SfM.
ZED SVO: pyexamples/preprocess/zed_svo_extract_stereo.py opens a Stereolabs .svo / .svo2 (needs pyzed), writes rectified left/ / right/ frames plus rig_calibration.json (intrinsics, baseline, mid-point body-frame sensor poses) for the reconstruction example above.
Visualization: pt.io.WriteRigPlyFile(...) exports tracks plus capture trajectory and intra-capture sensor baselines (densely sampled polylines) for MeshLab / Open3D.
See also¶
- SfM —
Reconstruction, Views, Tracks - View graph — pairwise matches
- Global pose estimation — averaging backends
- Matching / vismatch + SfM