Skip to content

feat: strength ranges for the five elementwise TAs, and bounds for the unsharp mask - #148

Open
Hendrik-code wants to merge 2 commits into
mainfrom
hm/function-ta-strength
Open

Hendrik-code wants to merge 2 commits into
mainfrom
hm/function-ta-strength

Conversation

@Hendrik-code

Copy link
Copy Markdown
Collaborator

What

Optional strength ranges for the five elementwise TAs, and lower bounds plus a kernel size for the unsharp mask. Every default is the old transform.

transform new keys map, with the range set
RandomLog1pGPU scale_range log(1 + k·x)
RandomSqrtGPU exponent_range x**g
RandomSinGPU frequency_range sin(2π·f·x + φ), φ ~ U(0, 2π)
RandomExpGPU rate_range exp(k·x)
RandomSigmoidGPU slope_range, center_quantile_range sigmoid(k·(x − c)), c = the sample's q-quantile
RandomUnsharpMaskGPU sigma_min, amount_min, kernel_size σ ~ U(sigma_min, sigma), amount ~ U(amount_min, unsharp_amount), kernel_size: null → 2⌈3σ⌉+1

Draws are per sample, or once per batch with same_on_batch.

Why

Each elementwise TA min-max scales a sample to [0, 1], applies a fixed function and, with retain_stats, restores mean and std. Only the function's curvature on [0, 1] survives. The table gives the share of output variance a linear map of the input cannot explain (1 − r²).

Sigmoid Sin Log1p Exp Sqrt
uniform input (analytic) 0.1% 0.5% 0.8% 1.6% 4.0%
SpineGAN MR/CT patches (measured) 0.04% 0.2% 0.5% 1.0% 1.9-3.0%

A gamma of 0.7 or 1.5 is already 1.2-2.0%. At p: 0.1 each (the paper config), 41% of samples get at least one of the five and are effectively unchanged by it.

The unsharp mask measured 0.01-0.9% with a gradient-magnitude correlation of 0.996 to its input. It draws σ from U(0, σ) onto a fixed 3-wide kernel, which truncates σ above about 0.5, and draws its amount from zero up.

  • Sigmoid centre: a quantile rather than a fixed point, because the min-max scaling is set by the extremes. On water-weighted Dixon the bulk of the tissue sits in the bottom fifth of [0, 1].
  • Sin: non-monotonic from about a quarter cycle up, so it can swap the order of tissue intensities.

Behaviour

  • With no range set, each leaf falls through to its fixed function and draws nothing. Tested: identical output and RNG stream to the generic _RandomFunctionBaseGPU(func=...).
  • Unsharp defaults reduce to the old rand * x exactly (* (x − 0.0) + 0.0).
  • all_augmentations.json regenerated.

Tests

unit_tests/test_function_strength.py:

  • defaults match the fixed functions and the generic base's RNG stream;
  • a strong range is more nonlinear than the fixed function;
  • draws are per sample unless same_on_batch;
  • the sigmoid centre lands on the drawn quantile;
  • sin folds above half a cycle;
  • bad ranges are refused;
  • unsharp kernel sizing, the σ lower bound, and "wider sharpens more".

Full suite: 486 passed, 13 skipped. mypy smauglab/ clean, pre-commit passes.

🤖 Generated with Claude Code

…e unsharp mask

Log1p, Sqrt, Sin, Exp and Sigmoid min-max scale each sample to [0, 1],
apply a fixed function and, with retain_stats, restore mean and std. Only
the function's curvature on [0, 1] survives that, and it is small: the share
of output variance a linear map of the input cannot explain is 0.8%, 4.0%,
0.5%, 1.6% and 0.1% on a uniform input, and 0.5%, 1.9-3.0%, 0.2%, 1.0% and
0.04% measured on real spine MR and CT patches. A gamma of 0.7 or 1.5 is
already 1.2-2.0%. At the paper config's p=0.1 each, 41% of samples get at
least one of the five and are effectively unchanged by it.

Each now takes an optional range that draws a stronger member of its family
per sample (once with same_on_batch):

* RandomLog1pGPU(scale_range)      log(1 + k*x)
* RandomSqrtGPU(exponent_range)    x**g
* RandomSinGPU(frequency_range)    sin(2*pi*f*x + phi), phi ~ U(0, 2*pi)
* RandomExpGPU(rate_range)         exp(k*x)
* RandomSigmoidGPU(slope_range, center_quantile_range)
                                   sigmoid(k*(x - c)), c the sample's q-quantile

The sigmoid centre is a quantile because the min-max scaling is set by the
extremes: with a bright tail the bulk of the tissue sits in the bottom fifth
of [0, 1], where a fixed centre would saturate it.

RandomUnsharpMaskGPU drew its per-axis sigma from U(0, sigma) onto a fixed
3-wide kernel and its amount from U(0, unsharp_amount), which measured at
0.01-0.9% nonlinear share and a gradient-magnitude correlation of 0.996 with
the input. It now takes `sigma_min`, `amount_min` and `kernel_size` (None
sizes the kernel 2*ceil(3*sigma)+1).

Every default is the old transform: the leaves fall through to the fixed
function and draw nothing, and the unsharp draws reduce to `rand * x` exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
exp(k*x) is convex at the bright end, so once retain_stats restores mean
and std the brightest voxels land far out. Measured on 128^3 spine patches
(Dixon in-phase / CT): largest |z| 5.6 / 8.4 at k=1, 9.3 / 11.8 at k=2.5,
21 / 19 at k=5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant