Template for Custom Gaze Estimation Models
This guide provides a template for creating a custom gaze estimation model in PyEtSimul. It outlines the essential structure and expected return types, allowing you to fill in your specific implementation details.
Core Architecture
PyEtSimul gaze estimation models follow a simple pattern:
Inherit from
EyeTracker- base class that handles the eye tracking workflowStore calibration state - parameters learned during calibration
Implement calibration - learn the mapping from eye features to gaze positions
Implement prediction - apply the learned mapping to new measurements
Block 1: Algorithm State
Purpose: Store your calibration parameters. This class is responsible for holding any data that your model learns during the calibration phase and needs for prediction.
What you need:
Inherit from
AlgorithmState.Add fields for your calibration parameters (e.g., matrices, coefficients, model weights).
Implement
serialize()anddeserialize()to allow your tracker to be saved to and loaded from disk.
Placeholder Code
class MyAlgorithmState(AlgorithmState): # Inherit from pyetsimul.types.algorithms.AlgorithmState
"""
State for your custom gaze model.
TODO: Add fields to store your calibration parameters.
"""
def serialize(self) -> dict:
"""Serialize the state to a dictionary for saving."""
# TODO: Add your custom parameters to the dictionary.
# Ensure they are JSON-serializable.
return {} # Return a dictionary of your state
@classmethod
def deserialize(cls, data: dict) -> "MyAlgorithmState":
"""Deserialize the dictionary back into a state object."""
state = cls()
# TODO: Restore your custom parameters from the dictionary.
return state
Blocks 2, 3, and 4: The Gaze Model Class and its Logic
Purpose: The main EyeTracker class orchestrates calibration and prediction.
What you need:
Inherit from
EyeTrackerand store your customAlgorithmState.Implement the
calibratemethod to learn your model parameters.Implement the
predict_gazemethod to apply the learned model.
Placeholder Code
class MyGazeModel(EyeTracker): # Inherit from pyetsimul.core.EyeTracker
"""
A template for a custom gaze estimation model.
TODO: Describe what your model does.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.algorithm_state = MyAlgorithmState() # Instance of your custom AlgorithmState
@property
def algorithm_name(self) -> str:
"""Return a unique identifier for your algorithm."""
# TODO: Choose a unique name for your model
return "my_custom_gaze_model"
@classmethod
def create(
cls,
# TODO: Add any specific parameters your model needs for instantiation
) -> "MyGazeModel":
"""A factory method for clean instantiation."""
return cls() # Return an instance of your model
# --- Block 3: Calibration Logic ---
# Purpose: Learn the mapping from eye features to world coordinates.
# This method modifies the internal state of the tracker, specifically `self.algorithm_state`.
# It does not return a value.
def calibrate(self, calibration_measurements: list[EyeMeasurement]) -> None: # EyeMeasurement from pyetsimul.types
"""Calibrate the model by learning the mapping from eye features to gaze."""
# TODO: Implement your calibration logic here.
# This method should update `self.algorithm_state` with learned parameters.
# Set `self.algorithm_state.is_calibrated = True` when calibration is complete.
pass
# --- Block 4: Prediction Logic ---
# Purpose: Estimate gaze from a single eye measurement using the learned model.
# This method returns a `GazePrediction` object.
def predict_gaze(self, measurement: EyeMeasurement) -> GazePrediction: # GazePrediction from pyetsimul.types
"""Predict gaze position from a single eye measurement."""
# TODO: Implement your gaze prediction logic here.
# Extract features from `measurement`.
# Apply your learned model (from `self.algorithm_state`) to predict gaze.
# Handle cases where essential data is missing (e.g., return confidence=0.0).
# Return a GazePrediction object
return GazePrediction(
gaze_point=Point3D(0.0, 0.0, 0.0), # Point3D from pyetsimul.types
confidence=0.0,
algorithm_name=self.algorithm_name,
processing_time=0.0,
intermediate_results={},
)
# --- Serialization ---
def serialize(self) -> dict:
"""Serialize the entire tracker for saving to disk."""
return {
"algorithm_state": self.algorithm_state.serialize(),
# TODO: Add any other tracker-specific state you need to serialize
}
@classmethod
def deserialize(cls, data: dict) -> "MyGazeModel":
"""Restore the tracker from serialized data."""
tracker = cls()
tracker.algorithm_state = MyAlgorithmState.deserialize(data["algorithm_state"])
# TODO: Restore any other tracker-specific state you serialized
return tracker