This header defines several factors related to the Essential matrix (), which encodes the relative rotation and translation direction between two calibrated cameras. They are primarily used in Structure from Motion (SfM) problems where point correspondences between views are available but the 3D structure and/or camera poses/calibration are unknown.
The core constraint is the epipolar constraint: , where and are corresponding points in normalized (calibrated) image coordinates.
Factors defined here:
EssentialMatrixFactor
: Constrains an unknownEssentialMatrix
variable using a single point correspondence .EssentialMatrixFactor2
: Constrains anEssentialMatrix
and an unknown inverse depth variable using a point correspondence.EssentialMatrixFactor3
: Like Factor2, but incorporates an additional fixed extrinsic rotation (useful for camera rigs).EssentialMatrixFactor4<CALIBRATION>
: Constrains anEssentialMatrix
and a sharedCALIBRATION
variable using a point correspondence given in pixel coordinates.EssentialMatrixFactor5<CALIBRATION>
: Constrains anEssentialMatrix
and two unknownCALIBRATION
variables (one for each camera) using a point correspondence given in pixel coordinates.
import gtsam
import numpy as np
from gtsam import (EssentialMatrix, Point2, Point3, Rot3, Unit3, Pose3, Cal3_S2, EssentialMatrixFactor,
EssentialMatrixFactor2, EssentialMatrixFactor3, EssentialMatrixFactor4Cal3_S2,
EssentialMatrixFactor5Cal3_S2, Values)
from gtsam import symbol_shorthand
E = symbol_shorthand.E
K = symbol_shorthand.K
D = symbol_shorthand.D # For inverse depth
1. EssentialMatrixFactor
¶
This factor involves a single EssentialMatrix
variable. It takes a pair of corresponding points in normalized (calibrated) image coordinates and penalizes deviations from the epipolar constraint .
The error is .
# Assume normalized coordinates
pA_calibrated = Point2(0.5, 0.2)
pB_calibrated = Point2(0.4, 0.25)
# Noise model on the epipolar error (scalar)
epipolar_noise = gtsam.noiseModel.Isotropic.Sigma(1, 0.01)
# Key for the unknown Essential Matrix
keyE = E(0)
factor1 = EssentialMatrixFactor(keyE, pA_calibrated, pB_calibrated, epipolar_noise)
factor1.print("Factor 1: ")
# Evaluate error (requires an EssentialMatrix value)
values = Values()
# Example: E for identity rotation, translation (1,0,0)
example_E = EssentialMatrix(Rot3(), Unit3(1, 0, 0))
values.insert(keyE, example_E)
error1 = factor1.error(values)
print(f"\nError for Factor 1: {error1}")
Factor 1: keys = { e0 }
isotropic dim=1 sigma=0.01
EssentialMatrixFactor with measurements
(0.5 0.2 1)' and ( 0.4 0.25 1)'
Error for Factor 1: 12.499999999999995
2. EssentialMatrixFactor2
¶
This factor involves an EssentialMatrix
variable and a double
variable representing the inverse depth of the 3D point corresponding to the measurement in the first camera’s frame.
It assumes the measurement is perfect and calculates the reprojection error of the point (reconstructed using and the inverse depth) in the first camera image, after projecting it into the second camera and back.
It requires point correspondences , which can be provided in either calibrated or pixel coordinates (if a calibration object K
is provided).
The error is a 2D reprojection error in the first image plane (typically in pixels if K is provided).
# Assume pixel coordinates and known calibration
K_cal = Cal3_S2(500, 500, 0, 320, 240)
pA_pixels = Point2(480, 288) # Corresponds to (0.5, 0.2) calibrated
pB_pixels = Point2(464, 312) # Corresponds to (0.4, 0.25) calibrated
# Noise model on the 2D reprojection error (pixels)
reprojection_noise = gtsam.noiseModel.Isotropic.Sigma(2, 1.0)
# Key for inverse depth
keyD = D(0)
factor2 = EssentialMatrixFactor2(keyE, keyD, pA_pixels, pB_pixels, reprojection_noise)
factor2.print("\nFactor 2: ")
# Evaluate error (requires E and inverse depth d)
values.insert(keyD, 0.2) # Assume inverse depth d = 1/Z = 1/5 = 0.2
error2 = factor2.error(values)
print(f"\nError for Factor 2: {error2}")
Factor 2: keys = { e0 d0 }
noise model: unit (2)
EssentialMatrixFactor2 with measurements
(480 288 1)' and (464 312)'
Error for Factor 2: 412.82000000000016
3. EssentialMatrixFactor3
¶
This is identical to EssentialMatrixFactor2
but includes an additional fixed Rot3
representing the rotation from the ‘body’ frame (where the Essential matrix is defined) to the ‘camera’ frame (where the measurements are made).
iRc
: Rotation from camera frame to body frame (inverse of body-to-camera).
The Essential matrix is transformed to the camera frame before use: .
body_R_cam = Rot3.Yaw(0.05) # Example fixed rotation
factor3 = EssentialMatrixFactor3(keyE, keyD, pA_pixels, pB_pixels, body_R_cam, reprojection_noise)
factor3.print("\nFactor 3: ")
# Evaluate error (uses same E and d from values)
error3 = factor3.error(values)
print(f"\nError for Factor 3: {error3}")
Factor 3: keys = { e0 d0 }
noise model: unit (2)
EssentialMatrixFactor2 with measurements
(480 288 1)' and (464 312)'
EssentialMatrixFactor3 with rotation [
0.99875, -0.0499792, 0;
0.0499792, 0.99875, 0;
0, 0, 1
]
Error for Factor 3: 413.0638991792357
4. EssentialMatrixFactor4<CALIBRATION>
¶
This factor involves an EssentialMatrix
variable and a single unknown CALIBRATION
variable (e.g., Cal3_S2
) that is assumed to be shared by both cameras.
It takes point correspondences in pixel coordinates.
The error is the algebraic epipolar error .
Note: Recovering calibration from 2D correspondences alone is often ill-posed. This factor typically requires strong priors on the calibration.
# Key for the unknown shared Calibration
keyK = K(0)
factor4 = EssentialMatrixFactor4Cal3_S2(keyE, keyK, pA_pixels, pB_pixels, epipolar_noise)
factor4.print("\nFactor 4: ")
# Evaluate error (requires E and K)
values.insert(keyK, K_cal) # Use the known K for this example
error4 = factor4.error(values)
print(f"\nError for Factor 4: {error4}")
Factor 4: keys = { e0 k0 }
isotropic dim=1 sigma=0.01
EssentialMatrixFactor4 with measurements
(480 288)' and (464 312)'
Error for Factor 4: 11.520000000000007
5. EssentialMatrixFactor5<CALIBRATION>
¶
Similar to Factor4, but allows for two different unknown CALIBRATION
variables, one for each camera ( and ).
It takes point correspondences in pixel coordinates.
The error is the algebraic epipolar error .
Note: Like Factor4, this is often ill-posed without strong priors.
# Keys for potentially different calibrations
keyKA = K(0) # Can reuse keyK if they are actually the same
keyKB = K(1)
factor5 = EssentialMatrixFactor5Cal3_S2(keyE, keyKA, keyKB, pA_pixels, pB_pixels, epipolar_noise)
factor5.print("\nFactor 5: ")
# Evaluate error (requires E, KA, KB)
# values already contains E(0) and K(0)
values.insert(keyKB, K_cal) # Assume KB is also the same known K
error5 = factor5.error(values)
print(f"\nError for Factor 5: {error5}")
Factor 5: keys = { e0 k0 k1 }
isotropic dim=1 sigma=0.01
EssentialMatrixFactor5 with measurements
(480 288)' and (464 312)'
Error for Factor 5: 11.520000000000007