The two-view geometry between uncalibrated cameras is captured by a seven-dimensional object called the fundamental matrix. This header provides FundamentalMatrix, a rank-two epipolar matrix, and SimpleFundamentalMatrix, a camera-oriented parameterization that keeps focal lengths and principal points explicit.
import gtsam
import numpy as npFundamentalMatrix¶
Construct from a rank-two matrix, its U-s-V factors, or calibrated relative geometry. matrix() returns the normalized 3×3 representation used to map a point in one image to an epipolar line in the other.
U = gtsam.Rot3.Yaw(np.pi / 2).matrix()
V = gtsam.Rot3.Yaw(np.pi / 4).matrix()
fundamental = gtsam.FundamentalMatrix(U, 0.5, V)
F = fundamental.matrix()
print(F)
assert np.linalg.matrix_rank(F, tol=1e-9) == 2SimpleFundamentalMatrix¶
The simple form starts from an EssentialMatrix, one focal length per camera, and the two principal points. It is useful when those camera parameters should remain visible instead of being absorbed into an arbitrary matrix scale.
essential = gtsam.EssentialMatrix(gtsam.Rot3(), gtsam.Unit3(1.0, 0.0, 0.0))
simple = gtsam.SimpleFundamentalMatrix(
essential, 800.0, 820.0, np.array([320.0, 240.0]), np.array([300.0, 250.0])
)
converted = gtsam.FundamentalMatrix(simple.matrix())
print("simple rank:", np.linalg.matrix_rank(simple.matrix(), tol=1e-9))Epipolar lines and manifold operations¶
For a homogeneous image point xb, F @ xb is its epipolar line in image A. Both classes expose retract() and localCoordinates(); use those instead of perturbing matrix entries, because an arbitrary perturbation does not preserve rank two.
point_b = np.array([350.0, 200.0, 1.0])
line_a = simple.matrix() @ point_b
print("epipolar line coefficients:", line_a)
delta = np.array([0.01, -0.01, 0.02, 0.01, -0.02, 0.005, 0.01])
perturbed = fundamental.retract(delta)
np.testing.assert_allclose(
fundamental.localCoordinates(perturbed), delta, atol=1e-8
)