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:

  1. Inherit from EyeTracker - base class that handles the eye tracking workflow

  2. Store calibration state - parameters learned during calibration

  3. Implement calibration - learn the mapping from eye features to gaze positions

  4. 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() and deserialize() 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 EyeTracker and store your custom AlgorithmState.

  • Implement the calibrate method to learn your model parameters.

  • Implement the predict_gaze method 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