mirror of
https://github.com/EyeTrackVR/EyeTrackVR.git
synced 2025-09-26 23:09:28 +08:00
130 lines
5.0 KiB
Python
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}") |