exordium.video.face.landmark.iris

MediaPipe Iris landmark detector wrapper.

Attributes

logger

Module-level logger.

Classes

IrisWrapper

MediaPipe Iris landmark detector wrapper.

IrisBlock

Main building block for Iris architecture.

MediaPipeIris

The IrisLandmark face landmark model from MediaPipe.

Functions

calculate_iris_diameters(→ numpy.ndarray | torch.Tensor)

Calculate iris diameters from MediaPipe Iris landmarks.

calculate_eyelid_pupil_distances(...)

Calculate eyelid-to-pupil distances from iris and eye landmarks.

calculate_eye_aspect_ratio(→ float | torch.Tensor)

Calculate the Eye Aspect Ratio (EAR).

visualize_iris(→ numpy.ndarray | torch.Tensor)

Draw face landmarks and iris landmarks onto an image.

Module Contents

exordium.video.face.landmark.iris.logger

Module-level logger.

exordium.video.face.landmark.iris.calculate_iris_diameters(iris_landmarks: numpy.ndarray | torch.Tensor) → numpy.ndarray | torch.Tensor

Calculate iris diameters from MediaPipe Iris landmarks.

Computes the horizontal (left–right) and vertical (top–bottom) diameter of the iris from the 5-point iris landmark set.

Type-preserving: np.ndarray input returns np.ndarray; torch.Tensor input returns torch.Tensor.

Parameters:

iris_landmarks – Iris landmarks of shape (5, 2).

Returns:

1-D array / tensor of shape (2,) containing [horizontal_diameter, vertical_diameter].

exordium.video.face.landmark.iris.calculate_eyelid_pupil_distances(iris_landmarks: numpy.ndarray | torch.Tensor, eye_landmarks: numpy.ndarray | torch.Tensor) → numpy.ndarray | torch.Tensor

Calculate eyelid-to-pupil distances from iris and eye landmarks.

Measures the Euclidean distance from the iris centre to the top and bottom eyelid landmarks, giving an estimate of how open the eye is.

Type-preserving: np.ndarray inputs return np.ndarray; torch.Tensor inputs return torch.Tensor.

Parameters:
  • iris_landmarks – Iris landmarks of shape (5, 2).

  • eye_landmarks – FaceMesh eye landmarks of shape (71, 2) or (16, 2).

Returns:

1-D array / tensor of shape (2,) containing [top_eyelid_distance, bottom_eyelid_distance].

exordium.video.face.landmark.iris.calculate_eye_aspect_ratio(landmarks: numpy.ndarray | torch.Tensor) → float | torch.Tensor

Calculate the Eye Aspect Ratio (EAR).

EAR is defined as the mean of three vertical landmark distances divided by the horizontal landmark distance. A value close to zero indicates a closed eye; typical open-eye values are in the range 0.25–0.40.

Usage:

ear_right = calculate_eye_aspect_ratio(right_eye_lmks)
ear_left  = calculate_eye_aspect_ratio(left_eye_lmks)
ear_mean  = (ear_right + ear_left) / 2.0

Type-preserving: np.ndarray input returns float; torch.Tensor input returns a scalar torch.Tensor.

Parameters:

landmarks – Eye landmarks of shape (71, 2) or (16, 2). Both shapes correspond to MediaPipe FaceMesh eye subsets.

Returns:

Eye aspect ratio — float for numpy input, scalar torch.Tensor for tensor input.

Raises:

ValueError – If landmarks.shape is not (71, 2) or (16, 2).

class exordium.video.face.landmark.iris.IrisWrapper(device_id: int | None = None, pretrained: bool = True)

MediaPipe Iris landmark detector wrapper.

Detects 71 eye landmarks and 5 iris landmarks from 64×64 eye patches using a PyTorch port of the MediaPipe Iris model.

Supported input types for __call__():

  • torch.Tensor — (3, H, W) or (B, 3, H, W) uint8 RGB; fastest path, stays on self.device end-to-end.

  • np.ndarray — (H, W, 3) or (B, H, W, 3) uint8 RGB.

  • Sequence[np.ndarray] — list of (H, W, 3) uint8 arrays.

  • Sequence[str | Path] — list of image file paths.

Design contract:

  • preprocess() — convert any input to a (B, 3, 64, 64) float32 tensor in [0, 1] on self.device.

  • inference() — model forward pass; returns (eye_landmarks, iris_landmarks) as (B, 71, 2) and (B, 5, 2) tensors on self.device.

  • __call__() — chains both under torch.inference_mode.

Parameters:

device_id – GPU device index. None or -1 uses CPU.

preprocess(frames) → torch.Tensor

Resize eye patches to 64×64 and normalise to [0, 1].

Accepts variable-size inputs — each patch is resized individually before stacking, so eye crops of different sizes are handled correctly.

Parameters:

frames – Any input accepted by _to_uint8_tensor().

Returns:

Float32 tensor of shape (B, 3, 64, 64) on self.device with values in [0, 1].

inference(tensor: torch.Tensor) → tuple[torch.Tensor, torch.Tensor]

Run the Iris model and return landmark tensors.

Parameters:

tensor – Float32 tensor of shape (B, 3, 64, 64) on self.device with values in [0, 1].

Returns:

  • eye_landmarks — (B, 71, 2) float32 tensor containing (x, y) pixel coordinates of the 71 eye landmarks.

  • iris_landmarks — (B, 5, 2) float32 tensor containing (x, y) pixel coordinates of the 5 iris landmarks.

Both tensors are on self.device.

Return type:

Tuple of

__call__(frames) → tuple[torch.Tensor, torch.Tensor]

Preprocess and run inference on eye patches.

Parameters:

frames – Any supported input (see class docstring).

Returns:

Tuple of (eye_landmarks, iris_landmarks) tensors of shape (B, 71, 2) and (B, 5, 2) on self.device.

eye_to_feature(eye: torch.Tensor) → dict[str, torch.Tensor]

Compute iris and eye features from a single eye crop.

Runs the full pipeline — preprocess → inference → metric computation — for a single eye patch and returns all intermediate and derived features as torch tensors. Landmarks are in 64×64 model space.

Parameters:

eye – Eye crop of shape (3, H, W) uint8 RGB. Any spatial size is accepted; the model resizes to 64×64 internally.

Returns:

  • "eye_original" — (3, H, W) uint8 — original input crop

  • "eye" — (3, 64, 64) uint8 — 64×64 resized crop

  • "eye_region_landmarks" — (71, 2) float32 — eye landmarks in 64×64 space

  • "iris_landmarks" — (5, 2) float32 — iris landmarks in 64×64 space

  • "iris_diameters" — (2,) float32 — [horizontal, vertical] diameter

  • "eyelid_pupil_distances" — (2,) float32 — [top, bottom] eyelid–pupil dist

  • "ear" — scalar float32 — eye aspect ratio

Return type:

Dictionary of torch tensors

exordium.video.face.landmark.iris.visualize_iris(image: numpy.ndarray | torch.Tensor, landmarks: numpy.ndarray | torch.Tensor, iris_landmarks: numpy.ndarray | torch.Tensor, output_path: str | os.PathLike | None = None, show_indices: bool = False) → numpy.ndarray | torch.Tensor

Draw face landmarks and iris landmarks onto an image.

Face landmarks are rendered in green and iris landmarks in blue. Accepts (H, W, C) numpy arrays or (C, H, W) uint8 torch tensors; returns the same type.

Parameters:
  • image – Input image — np.ndarray (H, W, C) or torch.Tensor (C, H, W) uint8.

  • landmarks – Face landmark coordinates of shape (N, 2) — np.ndarray or torch.Tensor.

  • iris_landmarks – Iris landmark coordinates of shape (5, 2) — np.ndarray or torch.Tensor.

  • output_path – Path to save the output image. None skips saving.

  • show_indices – Draw landmark indices next to each point.

Returns:

Copy of the image with face and iris landmarks drawn, same type as input.

class exordium.video.face.landmark.iris.IrisBlock(in_channels: int, out_channels: int, kernel_size: int = 3, stride: int = 1)

Bases: torch.nn.Module

Main building block for Iris architecture.

Initialize internal Module state, shared by both nn.Module and ScriptModule.

forward(x)

Forward pass through Iris block.

Parameters:

x – Input tensor.

Returns:

Tensor after convolution, pooling, and activation.

class exordium.video.face.landmark.iris.MediaPipeIris

Bases: torch.nn.Module

The IrisLandmark face landmark model from MediaPipe.

Because we won’t be training this model, it doesn’t need to have batchnorm layers. These have already been “folded” into the conv weights by TFLite. The conversion to PyTorch is fairly straightforward, but there are some small differences between TFLite and PyTorch in how they handle padding on conv layers with stride 2. This version works on batches, while the MediaPipe version can only handle a single image at a time.

Initialize internal Module state, shared by both nn.Module and ScriptModule.

forward(x)

Forward pass through MediaPipe Iris model.

Parameters:

x – Input tensor of shape (b, c, h, w).

Returns:

List of [eye_landmarks, iris_landmarks] predictions.

predict_on_image(img)

Makes a prediction on a single image.

Parameters:

img – a NumPy array of shape (H, W, 3) or a PyTorch tensor of shape (3, H, W). The image’s height and width should be 64 pixels.

Returns:

A tensor with face detections.

predict_on_batch(x)

Makes a prediction on a batch of images.

Parameters:

x – a NumPy array of shape (b, H, W, 3) or a PyTorch tensor of shape (b, 3, H, W). The height and width should be 64 pixels.

Returns:

A list containing a tensor of face detections for each image in the batch. If no faces are found for an image, returns a tensor of shape (0, 17).

Each face detection is a PyTorch tensor consisting of 17 numbers:
  • ymin, xmin, ymax, xmax

  • x,y-coordinates for the 6 keypoints

  • confidence score