ExperimentalSetup#

class pypillometry.ExperimentalSetup(screen_resolution=None, physical_screen_size=None, screen_offset=None, eye_to_screen_center=None, screen_pitch=None, screen_yaw=None, camera_offset=None, camera_spherical=None, ipd=None)[source]#

Bases: Stateful

Complete geometric model of an eye-tracking experimental setup.

Encapsulates:

  • Screen geometry (resolution, physical size)

  • Eye position relative to screen (perpendicular distance and offset)

  • Screen orientation (pitch and yaw tilt angles)

  • Camera position (relative to screen or eye)

  • Inter-pupillary distance (for binocular setups)

Provides:

  • Coordinate conversion between pixels and mm

  • Transformation from screen coordinates to eye-centered 3D frame

  • Validation of parameter consistency

  • Visualization of the setup geometry

  • Serialization for saving/loading

All parameters are optional; an incomplete setup can be created and used for whatever is set (accessing a missing value raises ValueError). Lengths and angles can be given as plain numbers, as strings with units (e.g., "52 cm", "-5 deg") or with a unit (e.g., 52 * pp.u.cm). Plain numbers are interpreted as mm (lengths) or radians (angles), with a one-time warning.

Parameters:
  • screen_resolution (Optional[Tuple[int, int]]) – Screen resolution in pixels (width, height).

  • physical_screen_size (Optional[Tuple[Union[float, str], Union[float, str]]]) – Physical screen dimensions (width, height); each element a length (default unit mm), e.g., ("52 cm", "29 cm").

  • screen_offset (Optional[Tuple[Union[float, str], Union[float, str]]]) – Offset of screen center from the eye reference point (delta_x, delta_y); each element a length (default unit mm). Default (0, 0). Positive delta_x = screen center is to the right of eye. Positive delta_y = screen center is above eye. Note: For binocular setups, the reference is the midpoint between the two eyes (root of nose).

  • eye_to_screen_center (Union[float, str, None]) – Distance from eye to screen center (default unit mm), e.g., "65 cm". The perpendicular distance d between eye and screen plane is computed from d = sqrt(eye_to_screen_center**2 - delta_x**2 - delta_y**2). If screen_offset is (0, 0) or not set, d equals eye_to_screen_center. Raises ValueError if it is smaller than the screen offset. Note: For binocular setups, “eye” refers to the midpoint between the two eyes (root of nose).

  • screen_pitch (Union[float, str, None]) – Screen pitch angle (alpha_tilt; default unit radians), e.g., "-5 deg". Rotation about horizontal axis. Positive = top of screen tilted away from eye. Default 0.

  • screen_yaw (Union[float, str, None]) – Screen yaw angle (beta_tilt; default unit radians), e.g., "3 deg". Rotation about vertical axis. Positive = right side of screen closer to eye. Default 0.

  • camera_offset (Optional[Tuple[Union[float, str], Union[float, str], Union[float, str]]]) – Camera position as (x, y, z) offset from eye; each element a length (default unit mm). z-axis points toward screen center. Takes precedence over camera_spherical if both are given.

  • camera_spherical (Optional[Tuple[Union[float, str], Union[float, str], Union[float, str]]]) – Alternative: camera position in spherical coordinates (theta, phi, r) relative to eye: theta is the polar angle from the z-axis and phi the azimuthal angle in the x-y plane (angles, default unit radians); r is the distance from eye (length, default unit mm).

  • ipd (Union[float, str, None]) – Inter-pupillary distance for binocular setups (default unit mm), e.g., "63 mm".

Attributes:
screen_resolutiontuple

Screen resolution (width, height) in pixels, or None if not set.

physical_screen_sizetuple

Physical screen size (width, height) in mm, or None if not set.

dfloat

Perpendicular eye-to-screen distance in mm.

delta_xfloat

Screen center x-offset from eye in mm (positive = screen right of eye).

delta_yfloat

Screen center y-offset from eye in mm (positive = screen above eye).

alpha_tiltfloat

Screen pitch angle in radians (positive = top tilted away).

beta_tiltfloat

Screen yaw angle in radians (positive = right side closer).

rfloat

Eye-to-camera distance in mm.

thetafloat

Camera polar angle from z-axis in eye-centered coords (radians).

phifloat

Camera azimuthal angle in eye-centered coords (radians).

ipdfloat or None

Inter-pupillary distance in mm, or None if not set.

Notes

All coordinates are in an eye-centric reference frame where:

  • The origin is at the eye position

  • For binocular setups, this is the midpoint between the two eyes (root of nose)

  • z-axis points toward the screen center

  • x-axis points to the right

  • y-axis points upward

Examples

>>> # Basic setup with distance to screen center
>>> setup = ExperimentalSetup(
...     screen_resolution=(1920, 1080),
...     physical_screen_size=("52 cm", "29 cm"),
...     eye_to_screen_center="65 cm",
...     camera_offset=("0 cm", "-30 cm", "0 cm"),  # camera below screen
... )
>>> print(f"d = {setup.d} mm")
d = 650.0 mm
>>> # Setup with screen offset and tilt
>>> setup = ExperimentalSetup(
...     screen_resolution=(1920, 1080),
...     physical_screen_size=("52 cm", "29 cm"),
...     eye_to_screen_center="65 cm",
...     screen_offset=("3 cm", "-1.5 cm"),  # screen right and below eye
...     screen_pitch="-5 deg",  # screen tilted back
...     camera_offset=("0 cm", "-30 cm", "0 cm"),
... )
>>> # Visualize the setup
>>> fig = setup.plot()

Methods

degrees_to_mm

Convert visual angles to mm coordinates (relative to screen center).

degrees_to_pixels

Convert visual angles to pixel coordinates.

from_dict

Create ExperimentalSetup from dictionary.

has_camera_position

Check if camera position is set.

has_eye_distance

Check if eye-to-screen distance is set.

has_screen_info

Check if screen resolution and physical size are set.

is_complete

Check if setup has all parameters needed for foreshortening correction.

mm_to_degrees

Convert mm coordinates (relative to screen center) to visual angles.

mm_to_pixels

Convert mm coordinates to pixels.

pixels_to_degrees

Convert pixel coordinates to visual angles.

pixels_to_mm

Convert pixel coordinates to mm.

plot

Visualize the experimental setup geometry.

screen_point_to_eye_frame

Transform screen point(s) from screen-centered mm to eye-centered 3D frame.

summary

Return a summary of the experimental setup as a dictionary.

to_dict

Export setup to dictionary for JSON serialization.

validate

Validate parameter consistency.

Details

classmethod from_dict(d)[source]#

Create ExperimentalSetup from dictionary.

Parameters:

d (Dict[str, Any]) – Dictionary from to_dict()

Return type:

ExperimentalSetup

degrees_to_mm(angle_x, angle_y)[source]#

Convert visual angles to mm coordinates (relative to screen center).

Parameters:
  • angle_x (Union[float, str, ndarray]) – Visual angles. Plain numbers and arrays are in degrees (like the output of mm_to_degrees()). Strings with units, e.g., "5 deg", "0.087 rad", or quantities are converted. Positive x = right, positive y = up.

  • angle_y (Union[float, str, ndarray]) – Visual angles. Plain numbers and arrays are in degrees (like the output of mm_to_degrees()). Strings with units, e.g., "5 deg", "0.087 rad", or quantities are converted. Positive x = right, positive y = up.

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray]]

Returns:

x_mm, y_mm (float or array) – Coordinates in mm relative to screen center. Positive x = right, positive y = up.

Notes

Inverse of mm_to_degrees: x_mm = d * tan(angle)

degrees_to_pixels(angle_x, angle_y)[source]#

Convert visual angles to pixel coordinates.

Parameters:
  • angle_x (Union[float, str, ndarray]) – Visual angles, in the same forms as for degrees_to_mm(): plain numbers and arrays are degrees, strings and Quantities carry their units. Positive x = right, positive y = up.

  • angle_y (Union[float, str, ndarray]) – Visual angles, in the same forms as for degrees_to_mm(): plain numbers and arrays are degrees, strings and Quantities carry their units. Positive x = right, positive y = up.

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray]]

Returns:

x_px, y_px (float or array) – Coordinates in pixels (origin at upper-left, y increases downward).

Notes

Combines degrees_to_mm and mm_to_pixels (centered). The y-axis is flipped (in mm_to_pixels): visual angle y increases upward, but pixel y increases downward.

has_camera_position()[source]#

Check if camera position is set.

Return type:

bool

Returns:

True if camera_offset or camera_spherical was given.

has_eye_distance()[source]#

Check if eye-to-screen distance is set.

Return type:

bool

Returns:

True if the eye-to-screen distance (d) is set.

has_screen_info()[source]#

Check if screen resolution and physical size are set.

Return type:

bool

Returns:

True if both screen_resolution and physical_screen_size are set.

is_complete()[source]#

Check if setup has all parameters needed for foreshortening correction.

Return type:

bool

Returns:

True if d and camera position are set.

mm_to_degrees(x_mm, y_mm)[source]#

Convert mm coordinates (relative to screen center) to visual angles.

Parameters:
  • x_mm (Union[float, ndarray]) – Coordinates in mm relative to screen center. Positive x = right, positive y = up.

  • y_mm (Union[float, ndarray]) – Coordinates in mm relative to screen center. Positive x = right, positive y = up.

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray]]

Returns:

angle_x, angle_y (float or array) – Visual angles in degrees. Positive x = right, positive y = up.

Notes

Computes angle = arctan(distance / d) separately for x and y, where d is the perpendicular eye-to-screen distance. This assumes the eye is opposite the screen center; screen_offset and screen tilt are not taken into account.

mm_to_pixels(x_mm, y_mm, centered=True)[source]#

Convert mm coordinates to pixels.

Parameters:
  • x_mm (Union[float, ndarray]) – Coordinates in mm; positive x = right, positive y = up

  • y_mm (Union[float, ndarray]) – Coordinates in mm; positive x = right, positive y = up

  • centered (bool) – If True, input is relative to screen center. If False, input is relative to the lower-left screen corner.

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray]]

Returns:

x_px, y_px (float or array) – Coordinates in pixels (origin at upper-left, y increases downward)

Notes

Requires screen_resolution and physical_screen_size. Inverse of pixels_to_mm() (the y-axis is flipped).

pixels_to_degrees(x_px, y_px)[source]#

Convert pixel coordinates to visual angles.

Parameters:
  • x_px (Union[float, ndarray]) – Coordinates in pixels (origin at upper-left, y increases downward).

  • y_px (Union[float, ndarray]) – Coordinates in pixels (origin at upper-left, y increases downward).

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray]]

Returns:

angle_x, angle_y (float or array) – Visual angles in degrees relative to screen center. Positive x = right, positive y = up.

Notes

Combines pixels_to_mm (centered) and mm_to_degrees. The y-axis is flipped (in pixels_to_mm): pixel y increases downward, but visual angle y increases upward.

pixels_to_mm(x_px, y_px, centered=True)[source]#

Convert pixel coordinates to mm.

Parameters:
  • x_px (Union[float, ndarray]) – Coordinates in pixels (origin at upper-left, y increases downward)

  • y_px (Union[float, ndarray]) – Coordinates in pixels (origin at upper-left, y increases downward)

  • centered (bool) – If True, output is relative to screen center. If False, output is relative to the lower-left screen corner.

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray]]

Returns:

x_mm, y_mm (float or array) – Coordinates in mm; positive x = right, positive y = up

Notes

Requires screen_resolution and physical_screen_size. The y-axis is flipped: pixel y increases downward, but mm y increases upward (as everywhere in the eye/screen geometry).

plot(theta=None, phi=None, projection='2d', show_gaze_samples=True, n_gaze_samples=9, viewing_angle=None, title=None, axes=None, figsize=None)[source]#

Visualize the experimental setup geometry.

Parameters:
  • theta (float) – Camera polar angle in radians (plain number only). If None, uses setup’s theta. The camera distance is always taken from the setup (0 if no camera position is set).

  • phi (float) – Camera azimuthal angle in radians (plain number only). If None, uses setup’s phi.

  • projection (str) –

    Type of projection (case-insensitive):

    • ’2d’: Three orthogonal 2D views (front, top, side)

    • ’3d’: Interactive 3D view

  • show_gaze_samples (bool) – Whether to show sample gaze vectors to screen

  • n_gaze_samples (int) – Number of sample gaze positions; drawn as a square grid with int(sqrt(n_gaze_samples)) points per side.

  • viewing_angle (tuple) – Viewing angle for 3D projection (elev, azim) or (elev, azim, roll) in degrees. Default is (-49, 50, 45) for 3D.

  • title (str) – title of the figure; default: the camera angles

  • axes (matplotlib Axes or list of Axes, optional) – axes to plot on: three axes (front, top, side view) for ‘2d’, one 3D axes for ‘3d’; default: a new figure

  • figsize (tuple) – size of the new figure; default (15, 5) for ‘2d’, (10, 8) for ‘3d’

Return type:

matplotlib.figure.Figure

Returns:

the figure (fig.axes are the axes of the views)

Raises:

ValueError – If screen info or eye-to-screen distance is not set, if theta or phi is None and no camera position is set, or if projection is not ‘2d’ or ‘3d’.

Examples

>>> import pypillometry as pp
>>> import matplotlib.pyplot as plt
>>> setup = pp.ExperimentalSetup(
...     screen_resolution=(1920, 1080),
...     physical_screen_size=("52 cm", "29 cm"),
...     eye_to_screen_center="60 cm",
...     camera_spherical=("20 deg", "-90 deg", "70 cm"),
... )
>>> fig = setup.plot()  # 2D views
>>> fig = setup.plot(projection='3d')  # 3D view
../../_images/pypillometry-ExperimentalSetup-1_00.png
../../_images/pypillometry-ExperimentalSetup-1_01.png
screen_point_to_eye_frame(x_mm, y_mm)[source]#

Transform screen point(s) from screen-centered mm to eye-centered 3D frame.

Applies screen tilt rotation and eye offset translation.

Parameters:
  • x_mm (Union[float, ndarray]) – Screen coordinates in mm (relative to screen center)

  • y_mm (Union[float, ndarray]) – Screen coordinates in mm (relative to screen center)

Return type:

Tuple[Union[float, ndarray], Union[float, ndarray], Union[float, ndarray]]

Returns:

T_x, T_y, T_z (float or array) – 3D coordinates in eye-centered frame (mm)

Notes

Uses the transformation from algorithm document Section 10.7.3: T(x_s, y_s) = R(alpha, beta) @ [x_s, y_s, 0]^T + [delta_x, delta_y, d]^T, i.e., the screen center is at (delta_x, delta_y, d) for any tilt.

summary()[source]#

Return a summary of the experimental setup as a dictionary.

Return type:

Dict[str, Any]

Returns:

Dictionary containing setup parameters in human-readable form.

to_dict()[source]#

Export setup to dictionary for JSON serialization.

Return type:

Dict[str, Any]

Returns:

All setup parameters as a dictionary

validate()[source]#

Validate parameter consistency. Raises ValueError if invalid.

Only validates parameters that are set; incomplete setups are allowed.

Return type:

None

property alpha_tilt: float#

Screen pitch angle in radians (positive = top tilted away).

property beta_tilt: float#

Screen yaw angle in radians (positive = right side closer).

property camera_position: Optional[Tuple[float, float, float]]#

Camera position (cx, cy, cz) in eye-centered frame in mm.

Eye is at origin, z-axis points toward screen center. Returns Cartesian coordinates computed from either camera_offset or camera_spherical.

property d: float#

Perpendicular eye-to-screen distance in mm.

property delta_x: float#

Screen center x-offset from eye in mm (positive = screen right of eye).

property delta_y: float#

Screen center y-offset from eye in mm (positive = screen above eye).

property eye_to_screen_center: float#

Direct distance from eye to screen center in mm.

property ipd: Optional[float]#

Inter-pupillary distance in mm, or None if not set.

property left_eye_offset: float#

X-offset for left eye in binocular setup (mm).

property mm_per_pixel_x: float#

Conversion factor: mm per pixel in x direction.

property mm_per_pixel_y: float#

Conversion factor: mm per pixel in y direction.

property phi: float#

Camera azimuthal angle in eye-centered coords (radians).

property physical_screen_height: float#

Physical screen height in mm.

property physical_screen_size: Optional[Tuple[float, float]]#

Physical screen size (width, height) in mm, or None if not set.

property physical_screen_width: float#

Physical screen width in mm.

property r: float#

Eye-to-camera distance in mm.

property right_eye_offset: float#

X-offset for right eye in binocular setup (mm).

property screen_height: int#

Screen height in pixels.

property screen_offset: Tuple[float, float]#

Screen center offset from eye (delta_x, delta_y) in mm.

property screen_pitch: float#

Alias for alpha_tilt (screen pitch in radians).

property screen_resolution: Optional[Tuple[int, int]]#

Screen resolution (width, height) in pixels, or None if not set.

property screen_width: int#

Screen width in pixels.

property screen_xlim: Tuple[int, int]#

Screen x-limits in pixels (0, width).

property screen_yaw: float#

Alias for beta_tilt (screen yaw in radians).

property screen_ylim: Tuple[int, int]#

Screen y-limits in pixels (0, height).

property theta: float#

Camera polar angle from z-axis in eye-centered coords (radians).