two_view op• Data kinds: image2d × image2d → pose
• Call: import fullseye as fs; fs.ledger.recover_pose(pts1, pts2, K1, K2=None, planar_tol=3.0) (to call the implementation directly, import twoview; twoview.recover_pose(pts1, pts2, K1, K2=None, planar_tol=3.0); from the registry, ops3d.get("recover_pose"))
• GPU: none (CPU reference implementation only)
Recover the relative pose (R,t) and the 3-D structure from correspondences and K (disambiguated by cheirality). → (R, t_unit, points3d).
t is defined only up to scale, so it is a unit vector. points3d is in the cam1 frame (at the scale corresponding to |t|=1).
fail-closed: planar (coplanar 3D) / pure-rotation scenes are degenerate configurations of essential-matrix decomposition: the Sampson residual stays ~0 while a wrong translation direction is returned (it looks perfect). Since the pose cannot be recovered from such input, it is rejected explicitly with ValueError (use homography decomposition). planar_tol is a planarity threshold independent of parallax and scale (degenerate if _planar_degeneracy_ratio = the ratio of the H fit to the F fit is below it; default 3.0).
Procedure:
• Entry check (at least 8 points, matching point counts, (N,2) and finite).
• Planarity: fit both a homography H by normalised DLT and F by the normalised 8-point algorithm, and compute the ratio σ_H / σ_F of their residual scales divided by the degrees of freedom (_planar_degeneracy_ratio). If it is below planar_tol (default 3.0), ValueError. Planar scenes and pure rotation give about √2; non-planar scenes give a value that grows with parallax / noise. If H is singular, the ratio is taken as 0 and likewise rejected. With exactly 8 points the noise cannot be measured from the F residual, so the previous test (median H residual / spread of the points < 1e-2, proportional to parallax) is used.
• ★2026-11-05 (adversarial verification G11): the previous ratio (H residual / spread of the points, default 1e-2) was proportional to parallax and rejected even non-planar scenes when the baseline was small (camera.recover_pose solves the same data). Conversely, with large noise it let even planar scenes through (ratio above 1e-2 at σ = 2 px). The new ratio fixes both.
• essential_8point → decompose into 4 candidates (R, ±t) → triangulate for each candidate and take the candidate with the most points at depth > 0 in both cameras (on a tie, the earlier candidate).
Returns: R (3,3), t (3,) unit vector, points3d (N,3). Convention: cam1 = K1[I|0], cam2 = K2[R|t], X2 = R X1 + t. points3d is in the cam1 frame at the scale |t|=1. Correspondences with parallel lines of sight become NaN rows.
• If K2 is omitted, K1 is used for both images.
• Defenceless against outliers (no RANSAC). Remove mismatches upstream.
• Deterministic. GT verification with pose_error, residuals with sampson_distance.
• depth_sensors — Depth sensors — ranging principles, real-device figures, how dropouts appear
• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).
• Operator provenance and references — the sources of the research/methods this op family came from.
• two_view_pose — py -3.11 examples_3d/two_view_pose.py
pose as input)fuse_to_voxel · pose_error · bundle_adjust · mean_reprojection_error · optimize_pose_graph · relative_pose · mean_edge_error · rotation_translation_error
two_view)fundamental_8point · essential_8point · triangulate · sampson_distance
*Provenance: twoview.py — 3D operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.