blinklinmult.data.augment¶
Augmentation for eye crops, calibrated against the corpora it trains on.
Every constant here is measured rather than chosen. Rotation matches the roll the corpora actually contain; brightness and contrast match the gap a shared encoder has to absorb between them. Augmenting outside those ranges would teach invariance to variation that never occurs, at the cost of the capacity to separate the variation that does.
Measured across the built corpora
corpus |
roll p05 |
roll p95 |
mean level |
|---|---|---|---|
CEW |
-12.2 |
9.8 |
0.556 |
RN15 |
-8.9 |
13.4 |
0.413 |
RN30 |
-9.3 |
11.4 |
0.403 |
TalkingFace |
-9.7 |
3.7 |
0.521 |
MRL-Eye |
0.346 |
So roll spans roughly ±13 degrees, and corpus mean level spans 0.346 to 0.556 – a 60% gap, which is what a model transferring between them must absorb.
One draw per sample, applied to every frame of it. A sample is one eye’s
whole window, and geometry and lighting are continuous in time: a head does not
jump orientation between adjacent frames, and a light source does not flicker.
Sampling per frame would manufacture exactly the chatter that
box_jitter() measures and that
temporal_smoothness() penalises. The frame-wise
path gets per-frame variation for free, because there a sample is one frame.
No horizontal flip. Flipping a left eye produces something that looks like a right eye, which is the usual argument for it – but here samples are eye-wise precisely so that winks and per-eye patterns are detectable, and the eye side is part of the label. Flipping without swapping the side teaches that side does not matter; flipping with it is a separate decision, deliberately not taken here.
No vertical flip, ever: eyelids close downward and that asymmetry is the signal.
Attributes¶
Rotation range. Halved from 12.0. |
|
Additive brightness range. Reduced from 0.20. |
|
Multiplicative contrast range around 1.0. Reduced from 0.20, as brightness. |
|
Shift range as a fraction of the crop. Reduced from 0.05. |
|
Zoom range around 1.0, for box-size error. Reduced from 0.10. |
|
How often to blur. Halved from 0.2 -- blur destroys the lid edge the task reads. |
|
Gaussian sigma at full strength, in pixels of a 64 px crop. |
Exceptions¶
Raised when an augmentation is configured out of range. |
Classes¶
How strongly to augment, as a fraction of each measured range. |
|
OmniLoader transform applying |
Functions¶
|
Augment one eye window, every frame identically. |
Module Contents¶
- blinklinmult.data.augment.MAX_ROTATION_DEGREES = 6.0¶
Rotation range. Halved from 12.0.
The corpora’s own roll spans ~13 degrees p05-p95, so 12 covered the full observed range – but sampling the extremes of a distribution as routine training noise is stronger than reproducing it. Half the range still covers the bulk of real head roll.
- blinklinmult.data.augment.MAX_BRIGHTNESS_DELTA = 0.15¶
Additive brightness range. Reduced from 0.20.
Kept near the measured gap between corpus means (0.346 to 0.556): this one describes a genuine domain difference the model must survive, so it is trimmed rather than halved.
- blinklinmult.data.augment.MAX_CONTRAST_DELTA = 0.15¶
Multiplicative contrast range around 1.0. Reduced from 0.20, as brightness.
- blinklinmult.data.augment.MAX_TRANSLATE_FRACTION = 0.03¶
Shift range as a fraction of the crop. Reduced from 0.05.
Localisation jitter is real, but a 64 px crop shifted 5% moves the lid edge by 3 px – comparable to the closure it is meant to detect.
- blinklinmult.data.augment.MAX_SCALE_DELTA = 0.06¶
Zoom range around 1.0, for box-size error. Reduced from 0.10.
- blinklinmult.data.augment.BLUR_PROBABILITY = 0.1¶
How often to blur. Halved from 0.2 – blur destroys the lid edge the task reads.
- blinklinmult.data.augment.MAX_BLUR_SIGMA = 0.5¶
Gaussian sigma at full strength, in pixels of a 64 px crop.
Cut hardest of all (from 0.8). Motion blur is the one corruption that removes the evidence rather than transforming it: a blurred closing lid and a blurred open eye look alike, so a strong blur trains the model on frames whose label it cannot see.
- exception blinklinmult.data.augment.AugmentError¶
Bases:
ValueErrorRaised when an augmentation is configured out of range.
Initialize self. See help(type(self)) for accurate signature.
- class blinklinmult.data.augment.AugmentConfig¶
How strongly to augment, as a fraction of each measured range.
- Parameters:
strength (float) – Scales every range.
0disables augmentation entirely;1uses the measured ranges above.rotate (bool) – Rotate within
MAX_ROTATION_DEGREES.photometric (bool) – Jitter brightness and contrast.
translate (bool) – Shift and zoom, for localisation error.
blur (bool) – Occasionally blur.
- __post_init__() None¶
Validate the configuration.
- Raises:
AugmentError – If the strength is negative or above one.
- property geometric: bool¶
Whether this configuration moves pixels around.
- Returns:
Truewhen rotation or translation is on. These are the transforms that invalidate a handcrafted descriptor, because 152 of its 160 dimensions are pixel-space landmark coordinates describing the unrotated crop.- Return type:
bool
- without_geometry() AugmentConfig¶
This configuration with the geometry-changing transforms removed.
Brightness, contrast and blur leave every landmark where it was, so they are safe alongside a handcrafted descriptor; rotation, translation and scale are not.
- Returns:
A copy with
rotateandtranslateoff.- Return type:
- property enabled: bool¶
Whether this configuration changes anything.
- Returns:
Falsewhen the strength is zero or every transform is off.- Return type:
bool
- blinklinmult.data.augment.augment_window(images: torch.Tensor, config: AugmentConfig, generator: torch.Generator) torch.Tensor¶
Augment one eye window, every frame identically.
- Parameters:
images (torch.Tensor) –
(T, C, H, W)crops in[0, 1].config (AugmentConfig) – How strongly to augment.
generator (torch.Generator) – The sample’s seeded generator, so the same sample augments the same way given the same epoch.
- Returns:
The augmented window, same shape, clamped to
[0, 1].- Return type:
torch.Tensor
- Raises:
AugmentError – If the window is not
(T, C, H, W).
- class blinklinmult.data.augment.EyeAugmentation(config: AugmentConfig | None = None)¶
Bases:
omniloader.transforms.base.TransformOmniLoader transform applying
augment_window()to a sample.Subclasses OmniLoader’s
Transformso the train/eval gate is the loader’s own:train_onlymeans a validation or test loader sees the crops exactly as built, with no flag to forget.The generator OmniLoader passes is seeded from
(seed, epoch, index), so a sample augments identically given the same epoch and differently across epochs – which is whatEpochPropagatorexists to advance.- Parameters:
config (AugmentConfig) – How strongly to augment.
- apply(sample: dict, generator: torch.Generator | None) dict¶
Augment one sample’s eye crops.
- Parameters:
sample (dict) – A unified sample.
generator (torch.Generator | None) – The sample’s seeded generator.
Noneleaves the sample untouched rather than drawing from global state, which would make a run unreproducible.
- Returns:
The sample, with
EYE_IMAGEaugmented. A shallow copy, so the caller’s dict is not mutated.- Return type:
dict