| """Simple image augmentations for training.""" |
|
|
| import numpy as np |
| import torch |
|
|
|
|
| def brightness_augment( |
| imgs: torch.Tensor, |
| coords: torch.Tensor, |
| masks: torch.Tensor, |
| *, |
| rng: np.random.Generator, |
| shift_range: float = 0.1, |
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| """Random additive brightness shift. |
| |
| Parameters |
| ---------- |
| imgs : torch.Tensor |
| (W, *spatial) normalised images. |
| coords : torch.Tensor |
| (W, M, 3) node coordinates — passed through unchanged. |
| masks : torch.Tensor |
| (W, M) boolean masks — passed through unchanged. |
| rng : np.random.Generator |
| Random number generator. |
| shift_range : float |
| Maximum absolute shift sampled from [-shift_range, shift_range]. |
| """ |
| shift = rng.uniform(-shift_range, shift_range) |
| return imgs + shift, coords, masks |
|
|
|
|
| def flip_augment( |
| imgs: torch.Tensor, |
| coords: torch.Tensor, |
| masks: torch.Tensor, |
| *, |
| rng: np.random.Generator, |
| ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| """Random spatial flip: samples uniformly from all 8 axis-aligned symmetries. |
| |
| Each of Z, Y, X is independently flipped with probability 0.5, |
| giving 2^3 = 8 equally likely outcomes (including identity). |
| |
| Creates a copy of coords so the underlying dataset is not mutated. |
| Only real (non-padded) coordinates are flipped; padding stays at zero. |
| """ |
| flip_mask = rng.random(3) < 0.5 |
| dims_to_flip = [1 + dim for dim, flip in enumerate(flip_mask) if flip] |
|
|
| if not dims_to_flip: |
| return imgs, coords, masks |
|
|
| imgs = imgs.flip(dims=dims_to_flip) |
|
|
| coords = coords.clone() |
| shape = imgs.shape[1:] |
| for dim in range(3): |
| if flip_mask[dim]: |
| dim_coords = coords[..., dim] |
| dim_coords[masks] = shape[dim] - dim_coords[masks] - 1 |
| coords[..., dim] = dim_coords |
|
|
| return imgs, coords, masks |
|
|