blinklinmult.train.phases

Derive blink phase and event shape from a predicted eye-state signal.

The model emits one continuous value per frame. That value alone is ambiguous – 0.5 could be an eye halfway shut or halfway open – but its trajectory is not: falling means closing, rising means opening, flat-and-low means closed. This module reads phase off the signal’s derivative rather than off the annotation’s geometry, which matters because no corpus annotates phase and inventing it from interval position would assert a closure shape rather than measure one.

The same trajectory separates event kinds. A complete blink descends, holds briefly, and ascends; an incomplete one reverses before it bottoms out; a double blink shows two minima inside one elevated span – which no single-threshold extractor can split, because thresholding sees one run either way.

These categories are derived, not annotated. They are measurements of the predicted signal and carry no ground truth, so they belong in analysis and in downstream state estimation, never in a benchmark column.

Attributes

OPEN

the eye is open and not moving.

CLOSING

the lid is descending.

CLOSED

the lid is down and still.

OPENING

the lid is rising.

PHASE_NAMES

Human-readable phase names, for reports and plots.

MOTION_EPSILON

Per-frame change below which the lid counts as still.

CLOSED_LEVEL

Signal value above which a still frame is closed rather than open.

Exceptions

PhaseError

Raised when a signal cannot be analysed.

Functions

smooth(→ numpy.ndarray)

Moving average, for a derivative that is not dominated by noise.

phases(→ numpy.ndarray)

Label every frame open, closing, closed, or opening.

describe_event(→ dict[str, float])

Measure the shape of one predicted event.

classify_event(→ str)

Name an event from its measured shape.

Module Contents

blinklinmult.train.phases.OPEN = 0

the eye is open and not moving.

Type:

Phase

blinklinmult.train.phases.CLOSING = 1

the lid is descending.

Type:

Phase

blinklinmult.train.phases.CLOSED = 2

the lid is down and still.

Type:

Phase

blinklinmult.train.phases.OPENING = 3

the lid is rising.

Type:

Phase

blinklinmult.train.phases.PHASE_NAMES: dict[int, str]

Human-readable phase names, for reports and plots.

blinklinmult.train.phases.MOTION_EPSILON = 0.02

Per-frame change below which the lid counts as still.

Separates a plateau from a ramp. Small enough that a genuine closure – which moves through most of its range in three or four frames – is never called still, large enough that prediction noise on an open eye is not called motion.

blinklinmult.train.phases.CLOSED_LEVEL = 0.5

Signal value above which a still frame is closed rather than open.

exception blinklinmult.train.phases.PhaseError[source]

Bases: ValueError

Raised when a signal cannot be analysed.

Initialize self. See help(type(self)) for accurate signature.

blinklinmult.train.phases.smooth(signal: numpy.ndarray, window: int = 3) → numpy.ndarray[source]

Moving average, for a derivative that is not dominated by noise.

Parameters:
  • signal (np.ndarray) – (T,) per-frame values.

  • window (int) – Frames to average over; 1 disables smoothing.

Returns:

(T,) smoothed, same length as the input.

Return type:

np.ndarray

Raises:

PhaseError – If the window is not a positive odd integer.

blinklinmult.train.phases.phases(signal: numpy.ndarray, window: int = 3, motion_epsilon: float = MOTION_EPSILON, closed_level: float = CLOSED_LEVEL) → numpy.ndarray[source]

Label every frame open, closing, closed, or opening.

Phase comes from the sign of the smoothed derivative, with the value used only to tell a still-and-low frame from a still-and-high one. That ordering is deliberate: the value is ambiguous mid-blink and the direction is not.

Parameters:
  • signal (np.ndarray) – (T,) predicted eye-state values, higher meaning more closed.

  • window (int) – Smoothing window before differentiating.

  • motion_epsilon (float) – Per-frame change below which the lid is still.

  • closed_level (float) – Value above which a still frame is closed.

Returns:

(T,) int8 of OPEN, CLOSING, CLOSED, OPENING.

Return type:

np.ndarray

blinklinmult.train.phases.describe_event(signal: numpy.ndarray, start: int, stop: int) → dict[str, float][source]

Measure the shape of one predicted event.

What distinguishes a complete blink from an incomplete or a double one is the curve, not the extent: peak depth says whether the lid ever really shut, the minima count says whether it shut twice, and the asymmetry says whether it reopened as fast as it closed (real blinks close faster than they open).

Parameters:
  • signal (np.ndarray) – (T,) values for the whole recording.

  • start (int) – First frame of the event, inclusive.

  • stop (int) – Last frame, inclusive.

Returns:

frames, peak, mean, n_peaks, rise_frames, fall_frames, and asymmetry – the fall minus the rise, in frames, positive when reopening takes longer.

Return type:

dict[str, float]

Raises:

PhaseError – If the span is empty or outside the signal.

blinklinmult.train.phases.classify_event(shape: dict[str, float], complete_peak: float = 0.7, min_frames: float = 2.0) → str[source]

Name an event from its measured shape.

Parameters:
  • shape (dict[str, float]) – As describe_event() returns.

  • complete_peak (float) – Peak below which the lid never fully shut.

  • min_frames (float) – Spans shorter than this are too brief to judge.

Returns:

"double", "incomplete", "long", "brief", or "complete". Derived from the prediction, never from an annotation.

Return type:

str