Skip to content

Instantly share code, notes, and snippets.

@FreeFly19
Created February 6, 2025 17:51
Show Gist options
  • Save FreeFly19/54ba5cfe9d1b716b3f24cb70239f0e99 to your computer and use it in GitHub Desktop.
Save FreeFly19/54ba5cfe9d1b716b3f24cb70239f0e99 to your computer and use it in GitHub Desktop.
essential_mat_decomposition
import numpy as np
import cv2
from scipy.spatial.transform import Rotation as R
if __name__ == '__main__':
# Generating a random point cloud within specified spatial limits
num_points = 100 # Reduced number of points for clarity
points_3d = np.random.rand(num_points, 3) # Random points in 3D
# Scale and shift points to fit within the specified range
points_3d[:, 0] = points_3d[:, 0] * 1000 - 500 # X: -500 to 500 mm
points_3d[:, 1] = points_3d[:, 1] * 1000 - 500 # Y: -500 to 500 mm
points_3d[:, 2] = points_3d[:, 2] * 4500 + 500 # Z: 500 to 5000 mm
# Defining camera intrinsic parameters
fov_x, fov_y = 80, 45 # Field of view in degrees for X and Y axes
resolution_x, resolution_y = 1920, 1080 # Camera resolution
# Focal lengths derived from field of view and sensor resolution
fx = resolution_x / (2 * np.tan(np.deg2rad(fov_x) / 2))
fy = resolution_y / (2 * np.tan(np.deg2rad(fov_y) / 2))
# Assuming the principal point is at the center of the image
cx, cy = resolution_x / 2, resolution_y / 2
intrinsic_matrix = np.array([
[fx, 0, cx],
[0, fy, cy],
[0, 0, 1] # Homogeneous component
])
# Camera 1 (Origin) - no rotation or translation
R1_identity = np.eye(3) # Identity rotation matrix
t1_zero = np.zeros((3, 1)) # Zero translation
# Camera 2 - positioned 50mm to the right and rotated -20 degrees around Y-axis
t2_translation = np.array([[10], [10], [0]]) # Translation vector
R2_rotation = R.from_euler('y', -20, degrees=True).as_matrix() # Rotation matrix
# Constructing projection matrices without considering intrinsic parameters for simplicity
P1_projection = np.dot(np.eye(3), np.hstack((R1_identity, t1_zero)))
P2_projection = np.dot(np.eye(3), np.hstack((R2_rotation, t2_translation)))
# Projecting 3D points to each camera's coordinate system
points_3d_homogeneous = np.hstack((points_3d, np.ones((num_points, 1)))) # Homogeneous coordinates
points_in_cam1 = (P1_projection @ points_3d_homogeneous.T).T
points_in_cam2 = (P2_projection @ points_3d_homogeneous.T).T
# Converting homogeneous coordinates to 2D image points
points_in_cam1_2d = points_in_cam1[:, :2] / points_in_cam1[:, 2, np.newaxis]
points_in_cam2_2d = points_in_cam2[:, :2] / points_in_cam2[:, 2, np.newaxis]
# Estimating the essential matrix from point correspondences
E_matrix, inliers = cv2.findEssentialMat(
points_in_cam1_2d, points_in_cam2_2d,
focal=1, pp=(0., 0.), method=cv2.LMEDS, prob=0.999, threshold=1.0
)
# Decomposing the essential matrix to find relative rotation and translation
_, recovered_R_matrix, recovered_t_vector, _ = cv2.recoverPose(E_matrix, points_in_cam1_2d, points_in_cam2_2d)
# Converting rotation matrices to Euler angles for easier interpretation
recovered_rotation_euler = R.from_matrix(recovered_R_matrix).as_euler('xyz', degrees=True)
groundtruth_rotation_euler = R.from_matrix(R2_rotation).as_euler('xyz', degrees=True)
print('Recovered Rotation (Euler angles):', recovered_rotation_euler)
print('Ground Truth Rotation (Euler angles):', groundtruth_rotation_euler)
# Calculating Camera 2's position relative to Camera 1
camera2_position = - np.linalg.inv(R2_rotation) @ t2_translation
camera2_orientation_in_world = R2_rotation.T # Orientation relative to the world
# Projection matrix of Camera 2 with intrinsic parameters
P2_with_intrinsic = np.dot(intrinsic_matrix, np.hstack((R2_rotation, t2_translation)))
M_orientation = P2_with_intrinsic[:, :3] # Orientation component of the projection matrix
principal_ray_direction = M_orientation[2, :] # Camera 2's principal ray
# Testing which rotation gives the correct principal ray direction
incorrect_principal_ray = R2_rotation @ np.array([0, 0, 1])
correct_principal_ray = R2_rotation.T @ np.array([0, 0, 1])
print('Camera 2 Principal Ray Direction:', principal_ray_direction)
print('Incorrect Principal Ray (Using R2):', incorrect_principal_ray)
print('Correct Principal Ray (Using R2.T):', correct_principal_ray)
print('Recovered translation', (recovered_t_vector * np.linalg.norm(t2_translation)).tolist())
print('Ground Truth translation', t2_translation.tolist())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment