EyeTrackVR/EyeTrackApp/utils/calibration_3d.py
2024-06-23 17:54:47 -05:00

130 lines
5.0 KiB
Python

# calibration_module.py
import numpy as np
class CalibrationProcessor:
def __init__(self):
self.left_eye_data = None
self.right_eye_data = None
self.P_left = None
self.P_right = None
self.gt_3d = np.array([
(0.8, 0.8, 1), (0, 0.8, 1), (-0.8, 0.8, 1), (0.8, 0, 1), (0, 0, 1),
(-0.8, 0, 1), (0.8, -0.8, 1), (0, -0.8, 1), (-0.8, -0.8, 1)
])
def estimate_projection_matrix(self, eye_data, gt_3d):
# Ensure the input data is a numpy array
eye_data = np.array(eye_data)
gt_3d = np.array(gt_3d)
# Append ones for homogeneous coordinates
gt_3d_h = np.hstack((gt_3d, np.ones((gt_3d.shape[0], 1))))
eye_data_h = np.hstack((eye_data, np.ones((eye_data.shape[0], 1))))
# Debug: Print the shapes of the matrices
print("Shape of gt_3d_h:", gt_3d_h.shape)
print("Shape of eye_data_h:", eye_data_h.shape)
# Solve for the projection matrix using least squares
P, _, _, _ = np.linalg.lstsq(gt_3d_h, eye_data_h, rcond=None)
return P
def receive_calibration_data(self, eye_id, data):
if eye_id == 1:
self.left_eye_data = data
elif eye_id == 0:
self.right_eye_data = data
# print('receive',len(self.left_eye_data), self.left_eye_data, self.right_eye_data, data, eye_id)
# Check if both sets of data have been received
if self.left_eye_data is not None and self.right_eye_data is not None:
if len(self.left_eye_data) == 8 and len(self.right_eye_data) == 8:
self.process_calibration_data()
def process_calibration_data(self):
# Ensure both data are present
if self.left_eye_data is None or self.right_eye_data is None:
raise ValueError("Calibration data for both eyes must be provided")
print("Processing calibration data for both eyes...")
print(f"Left Eye Data: {self.left_eye_data}")
print(f"Right Eye Data: {self.right_eye_data}")
self.left_eye_data = np.array(self.left_eye_data)
self.right_eye_data = np.array(self.right_eye_data)
if len(self.left_eye_data) != len(self.gt_3d):
raise ValueError(
f"Number of left eye points ({len(self.left_eye_data)}) does not match number of 3D points ({len(self.gt_3d)}).")
if len(self.right_eye_data) != len(self.gt_3d):
raise ValueError(
f"Number of right eye points ({len(self.right_eye_data)}) does not match number of 3D points ({len(self.gt_3d)}).")
# After processing, reset the data
# self.left_eye_data = None
# self.right_eye_data = None
# Function to compute the 3D gaze direction from 2D points
def compute_gaze_direction(self, P, point_2d):
print(P, point_2d)
# Convert 2D point to homogeneous coordinates
point_2d_h = np.append(point_2d, 1)
# Solve for 3D direction (Ax = b, where A is the projection matrix and b is the 2D point)
direction, _, _, _ = np.linalg.lstsq(P[:, :-1], point_2d_h, rcond=None)
direction /= np.linalg.norm(direction)
return direction
# Compute the convergence point given 2D points for both eyes
def compute_convergence_point(self, left_point_2d, right_point_2d, P_left, P_right, IPD):
left_eye_pos = np.array([-IPD / 2, 0, 0])
right_eye_pos = np.array([IPD / 2, 0, 0])
gaze_left = self.compute_gaze_direction(P_left, left_point_2d)
gaze_right = self.compute_gaze_direction(P_right, right_point_2d)
# Parameterize the gaze directions as lines
def line_parametric_form(point, direction, t):
return point + t * direction
# Find the closest point between two lines
t_values = np.linspace(-10, 10, 1000)
min_distance = float('inf')
best_point = None
for t1 in t_values:
for t2 in t_values:
point1 = line_parametric_form(left_eye_pos, gaze_left, t1)
point2 = line_parametric_form(right_eye_pos, gaze_right, t2)
distance = np.linalg.norm(point1 - point2)
if distance < min_distance:
min_distance = distance
best_point = (point1 + point2) / 2
return best_point
def set_P(self):
self.P_left = self.estimate_projection_matrix(self.left_eye_data, self.gt_3d)
self.P_right = self.estimate_projection_matrix(self.right_eye_data, self.gt_3d)
# Global instance of CalibrationProcessor
calibration_processor = CalibrationProcessor()
def receive_calibration_data(data, eye_id):
global calibration_processor
calibration_processor.receive_calibration_data(eye_id, data)
def converge_3d():
IPD = 0.058
left_point_2d = (120, 100)
right_point_2d = (118, 65)
# estimate_projection_matrix
calibration_processor.set_P()
convergence_point = calibration_processor.compute_convergence_point(left_point_2d, right_point_2d, calibration_processor.P_left, calibration_processor.P_right, IPD)
print(f"Convergence Point: {convergence_point}")