blinklinmult.overlay

Draw what the pipeline saw onto the frames it saw it in.

A number in a log says a model fired; an overlay says where it was looking when it did. That distinction is what makes the demo diagnostic rather than decorative – a red eye box explains a missing score far better than a gap in a plot.

The colour scheme is the whole legend:

colour

meaning

blue

the tracked face

green

an eye that was scored

red

an eye that was found but suppressed (head turned)

magenta

FaceMesh landmarks

grey

the readout, top-left

The readout sits on a translucent light panel, so it stays legible over any footage without a drop shadow – which was tried and reads as visual noise.

Attributes

logger

Module-level logger.

FACE_COLOUR

Blue, RGB, for the tracked face box.

USED_COLOUR

Green, RGB, for an eye that was scored.

DROPPED_COLOUR

Red, RGB, for an eye found but suppressed.

LANDMARK_COLOUR

Magenta, RGB, for the FaceMesh points.

TEXT_COLOUR

Mid grey, RGB, for the readout.

PANEL_COLOUR

Near-white, RGB, behind the readout.

PANEL_ALPHA

How opaque the panel is. Enough for contrast, translucent enough to see through.

FONT_SCALE

Text size. Small deliberately -- the readout must not cover the face.

LINE_HEIGHT

Pixels between readout lines.

TEXT_ORIGIN

Top-left corner of the readout.

LANDMARK_RADIUS

478 larger dots would hide the face.

Functions

draw(→ numpy.ndarray)

Annotate one frame.

render(→ pathlib.Path)

Write the annotated video.

plot(result)

Plot both eyes: closeness, thresholds and the binary prediction.

Module Contents

blinklinmult.overlay.logger

Module-level logger.

blinklinmult.overlay.FACE_COLOUR = (60, 130, 255)

Blue, RGB, for the tracked face box.

blinklinmult.overlay.USED_COLOUR = (60, 220, 90)

Green, RGB, for an eye that was scored.

blinklinmult.overlay.DROPPED_COLOUR = (240, 60, 60)

Red, RGB, for an eye found but suppressed.

blinklinmult.overlay.LANDMARK_COLOUR = (230, 60, 230)

Magenta, RGB, for the FaceMesh points.

blinklinmult.overlay.TEXT_COLOUR = (110, 110, 110)

Mid grey, RGB, for the readout.

Darker than it first appears it should be. The readout is drawn on a panel (PANEL_COLOUR) rather than straight onto the frame, so it needs contrast against that panel, not against arbitrary video. A light grey chosen to survive a dark background is what made the text vanish on a bright one.

blinklinmult.overlay.PANEL_COLOUR = (245, 245, 245)

Near-white, RGB, behind the readout.

Text alone cannot be legible over unknown video: whatever colour is picked, some frame defeats it. A drop shadow is one fix and was rejected as visual noise; a flat panel is the other, and it makes legibility a property of the panel rather than a gamble on the footage.

blinklinmult.overlay.PANEL_ALPHA = 0.55

How opaque the panel is. Enough for contrast, translucent enough to see through.

blinklinmult.overlay.FONT_SCALE = 0.5

Text size. Small deliberately – the readout must not cover the face.

blinklinmult.overlay.LINE_HEIGHT = 18

Pixels between readout lines.

blinklinmult.overlay.TEXT_ORIGIN = (8, 38)

Top-left corner of the readout.

One line lower than the frame’s top edge: Gradio draws its own component label over the top of the video, which would hide a first line placed there.

blinklinmult.overlay.LANDMARK_RADIUS = 1

478 larger dots would hide the face.

Type:

Landmark dot radius. One pixel

blinklinmult.overlay.draw(frame: numpy.ndarray, result: blinklinmult.pipeline.Result, index: int) → numpy.ndarray[source]

Annotate one frame.

Parameters:
  • frame (np.ndarray) – (H, W, 3) uint8 RGB. Not modified.

  • result (Result) – The finished analysis.

  • index (int) – Which frame this is.

Returns:

A new annotated frame.

Return type:

np.ndarray

blinklinmult.overlay.render(frames: numpy.ndarray, result: blinklinmult.pipeline.Result, output_path: str | pathlib.Path) → pathlib.Path[source]

Write the annotated video.

Parameters:
  • frames (np.ndarray) – (T, H, W, 3) uint8 RGB, the source frames.

  • result (Result) – The finished analysis, same length.

  • output_path (str | Path) – Where to write the mp4.

Returns:

The written file.

Return type:

Path

blinklinmult.overlay.plot(result: blinklinmult.pipeline.Result)[source]

Plot both eyes: closeness, thresholds and the binary prediction.

Two stacked panels sharing the frame axis, left eye above right, so the two can be read against each other – eyes blink together, and a disagreement is usually one eye being suppressed rather than a real difference.

Suppressed spans are shaded and the line breaks, because a gap means the eye was not looked at. Drawing through it would assert a measurement that was never taken.

Parameters:

result (Result) – The finished analysis.

Returns:

The figure. The caller owns it and should close it; leaving figures open leaks memory across repeated runs.

Return type:

matplotlib.figure.Figure