Skip to content

feat: z-score the SynthSeg synthesis on request with zscore_output - #146

Open
Hendrik-code wants to merge 1 commit into
mainfrom
hm/synthseg-zscore-output
Open

Hendrik-code wants to merge 1 commit into
mainfrom
hm/synthseg-zscore-output

Conversation

@Hendrik-code

Copy link
Copy Markdown
Collaborator

What

RandomSynthSegGPU takes zscore_output (default False). When set, each synthesised sample is standardised over its patch before it replaces the image:

"RandomSynthSegGPU": { "p": 1.0, "zscore_output": true, ... }

Why

With SynthSeg's default normalise=True the synthesis is min-max scaled to [0, 1], while the real patches beside it are z-scored. Measured on the 128³ training patches of four SpineGAN datasets (two cases each, five draws): the image std after SynthSeg is 0.16-0.20, against 0.86-0.91 for the Dixon in-phase and fat inputs.

Every later transform that works in absolute units then acts on the synthetic samples at several times its strength on the real ones. The visible case is RandomGaussianNoiseGPU, whose std is drawn from U(0, std): at the paper config's std: 1.0, the noise std exceeds the image's in about 80% of firings on a SynthSeg sample. In the measured sample, 16 of 20 noise firings after SynthSeg left the label boundaries indistinguishable from the background gradient (mean |∇I| on label boundaries ÷ mean |∇I| over the slice fell from 1.8-2.8 to 1.0-1.3). That affects the published SynthSeg (post) configuration (transform_params_paper-synthseg.json), where noise is on at p: 0.1 and SynthSeg at p: 1.0.

Behaviour

  • Draws no random numbers: the same seed gives the same synthesis, up to the rescale (tested).
  • Default False: shipped configs and their seeded outputs are unchanged.
  • all_augmentations.json regenerated (smauglab matrix --write).

Tests

unit_tests/test_synthseg_zscore_output.py:

  • the default still returns [0, 1];
  • with the option set, every sample has mean 0 and std 1;
  • the output is exactly the standardised default synthesis;
  • the registry accepts the key.

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

🤖 Generated with Claude Code

With SynthSeg's default `normalise=True` the synthesis is min-max scaled to
[0, 1]. The real training patches it is mixed with are z-scored, so the
synthetic samples reach every later transform at about a fifth of the spread
of a real one: measured 0.16-0.20 against 0.86-0.91 on two Dixon datasets.
Transforms that work in absolute units then act on them at several times
their strength on real samples. RandomGaussianNoiseGPU draws its std from
U(0, std), and with the paper config's std 1.0 the synthesis was buried under
noise in 16 of 20 firings in a measured sample (label boundaries no longer
stood out from the background gradient).

`zscore_output=True` standardises each synthesised sample over its patch
before it replaces the image. It draws no random numbers, so the same seed
gives the same synthesis up to that rescale. Default False keeps the [0, 1]
output and every shipped config's seeded result.

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