From 96a9ec88b8e37b28070d88c3dda7d75c332e87fd Mon Sep 17 00:00:00 2001 From: iback Date: Wed, 19 Aug 2026 06:27:22 +0000 Subject: [PATCH 1/2] refactor: drive every augmentation from a registry instead of four if ladders Which augmentations exist, what they accept, and where they sit in the pipeline were encoded four times over: one dispatch ladder in gpu/transforms.py, two in gpu/transforms_list.py, one in cpu/transforms.py, ~900 lines between them. They had already drifted -- the list pipelines passed a `crop=` argument no transform accepts, and ordered SimulateLowRes differently from the sequential one. Each augmentation class now registers itself. The registry (smauglab/registry.py) is the single source of truth for the class a config key maps to, the parameters it accepts (read from the constructor signature, so there is no second schema to drift), its pipeline order, and its GEO/GE/TA group. smauglab/transforms/build.py does the dispatch once for all three pipeline modes. Config handling moves to smauglab/config.py: a sectioned GPU/CPU/pipeline schema, validation that reports every problem in a file at once rather than one per run, and "did you mean" suggestions. A flat, section-less config is now rejected -- the two namespaces overlapped enough that `GaussianBlurTransform` meant different transforms depending on which builder read it. The three nnU-Net trainers collapse into one. Which sections a config populates decides whether augmentation runs on the dataloader, on the batch, or both, so the CPU/GPU/Hybrid split no longer needs a class each. The class keeps the name nnUNetTrainerDAExtGPU: nnU-Net writes it into every checkpoint and resolves the class from it at inference, so renaming it would strand trained models. A `smauglab` CLI answers what exists and whether a config is valid by reading the registry, so it cannot go out of date, and generates the README coverage matrix and the all-augmentations template config that CI checks for staleness. The .gitignore ignores *.json repo-wide to keep per-experiment configs out (see 7ff2088). That silently swallowed three things this change adds and needs tracked -- the generated template, the test fixtures, and the migrator the config error messages point at -- so each gets an explicit un-ignore. Co-Authored-By: Claude Opus 5 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/workflows/lint.yml | 8 + .gitignore | 18 + CONTRIBUTING.md | 100 +- README.md | 78 +- migration/README.md | 29 + migration/hash_migration.json | 337 + migration/migrate.py | 471 + pyproject.toml | 13 +- smauglab/__init__.py | 18 + smauglab/cli.py | 277 + smauglab/config.py | 300 + smauglab/configs/all_augmentations.json | 669 + smauglab/configs/transform_params.json | 252 +- smauglab/configs/transform_params_gpu.json | 483 +- smauglab/configs/transform_params_hybrid.json | 452 +- .../configs/transform_params_hybrid_TAGE.json | 461 +- ...rams_one-sequence-to-segment-them-all.json | 389 +- smauglab/registry.py | 551 + smauglab/trainers/nnUNetTrainerDAExt.py | 426 +- smauglab/transforms/__init__.py | 23 + smauglab/transforms/build.py | 205 + smauglab/transforms/cpu/__init__.py | 1 + smauglab/transforms/cpu/artifact.py | 8 + smauglab/transforms/cpu/contrast.py | 172 +- smauglab/transforms/cpu/external.py | 76 + smauglab/transforms/cpu/fromSeg.py | 8 + smauglab/transforms/cpu/spatial.py | 14 + smauglab/transforms/cpu/transforms.py | 318 +- smauglab/transforms/gpu/__init__.py | 1 + smauglab/transforms/gpu/contrast.py | 593 +- smauglab/transforms/gpu/domain_transfer.py | 50 +- smauglab/transforms/gpu/fromSeg.py | 60 +- smauglab/transforms/gpu/spatial.py | 75 +- smauglab/transforms/gpu/transforms.py | 500 +- smauglab/transforms/gpu/transforms_list.py | 686 +- smauglab/transforms/synthseg/README.md | 6 +- smauglab/transforms/synthseg/functional.py | 2 +- smauglab/transforms/synthseg/transforms.py | 81 +- smauglab/utils/__init__.py | 13 + .../configs/synthseg_params.json | 50 + .../configs/transform_params.json | 98 + .../configs/transform_params_gpu.json | 209 + ...sform_params_gpu_basedon3d250af0-noGE.json | 165 + ...pu_basedon3d250af0-noTA-heavyFunction.json | 165 + ...arams_gpu_basedon3d250af0-noTA-nobias.json | 165 + ...sform_params_gpu_basedon3d250af0-noTA.json | 165 + .../transform_params_gpu_basedon3d250af0.json | 165 + ...orm_params_gpu_default01-23-List-TA05.json | 171 + ...orm_params_gpu_default01-23-List-TA08.json | 169 + ...ransform_params_gpu_default01-23-List.json | 169 + ...nsform_params_gpu_default01-23-Scharr.json | 108 + ...ansform_params_gpu_default01-23-focus.json | 165 + ...ransform_params_gpu_default01-23-noGE.json | 96 + ...ms_gpu_default01-23-noMirror-RandConv.json | 165 + ...form_params_gpu_default01-23-noMirror.json | 165 + ...ransform_params_gpu_default01-23-noTA.json | 98 + .../transform_params_gpu_default01-23.json | 165 + ...params_gpu_default01-23_CropTransform.json | 171 + ...orm_params_gpu_default01-23_ICGT_plus.json | 170 + ...efault01-23_ImageContrastGPUTransform.json | 170 + ..._default01-23_RandomDomainTransferGPU.json | 180 + ...form_params_gpu_default01-23_Synthseg.json | 213 + ..._params_gpu_default01-23_SynthsegPlus.json | 213 + .../transform_params_gpu_inoutseg.json | 155 + .../transform_params_gpu_palette-em.json | 232 + .../configs/transform_params_hybrid.json | 262 + .../configs/transform_params_hybrid_TAGE.json | 264 + ...rams_one-sequence-to-segment-them-all.json | 165 + .../transform_params_gpu_default01-23.json | 165 + ...1-23_ImageContrastV26_6_2GPUTransform.json | 179 + ...form_params_gpu_default01-23_Synthseg.json | 213 + ...m_params_gpu_default01-23_Synthseg_EM.json | 213 + ...eContrastV26_6_2GPUTransform_train025.json | 252 + .../fixtures/legacy_effective_kwargs.json | 11951 ++++++++++++++++ unit_tests/helpers.py | 30 +- unit_tests/test_builder.py | 203 + unit_tests/test_cli.py | 139 + unit_tests/test_imports.py | 36 +- unit_tests/test_registered_augmentations.py | 176 + unit_tests/test_registry.py | 257 + unit_tests/test_trainers.py | 198 + unit_tests/test_transforms_gpu.py | 120 +- unit_tests/test_variant_leaves.py | 148 + 84 files changed, 24467 insertions(+), 2887 deletions(-) create mode 100644 migration/README.md create mode 100644 migration/hash_migration.json create mode 100644 migration/migrate.py create mode 100644 smauglab/__init__.py create mode 100644 smauglab/cli.py create mode 100644 smauglab/config.py create mode 100644 smauglab/configs/all_augmentations.json create mode 100644 smauglab/registry.py create mode 100644 smauglab/transforms/__init__.py create mode 100644 smauglab/transforms/build.py create mode 100644 smauglab/transforms/cpu/__init__.py create mode 100644 smauglab/transforms/cpu/external.py create mode 100644 smauglab/transforms/gpu/__init__.py create mode 100644 smauglab/utils/__init__.py create mode 100644 unit_tests/fixtures/legacy_configs/configs/synthseg_params.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noGE.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-nobias.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA05.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA08.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List.json create mode 100755 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-Scharr.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-focus.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noGE.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror-RandConv.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noTA.json create mode 100755 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_CropTransform.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ICGT_plus.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ImageContrastGPUTransform.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_RandomDomainTransferGPU.json create mode 100755 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_Synthseg.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_SynthsegPlus.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_inoutseg.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_palette-em.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid.json create mode 100644 unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid_TAGE.json create mode 100755 unit_tests/fixtures/legacy_configs/configs/transform_params_one-sequence-to-segment-them-all.json create mode 100755 unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23.json create mode 100755 unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_ImageContrastV26_6_2GPUTransform.json create mode 100755 unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg.json create mode 100755 unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg_EM.json create mode 100755 unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_smauglabAug_ImageContrastV26_6_2GPUTransform_train025.json create mode 100644 unit_tests/fixtures/legacy_effective_kwargs.json create mode 100644 unit_tests/test_builder.py create mode 100644 unit_tests/test_cli.py create mode 100644 unit_tests/test_registered_augmentations.py create mode 100644 unit_tests/test_registry.py create mode 100644 unit_tests/test_trainers.py create mode 100644 unit_tests/test_variant_leaves.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ba301a9..031c5cc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -13,7 +13,7 @@ A clear and concise description of what goes wrong. Steps to reproduce the behaviour, ideally with the exact command: ```bash -# e.g. SMAUGLAB_PARAMS_GPU_JSON=/abs/path/params.json nnUNetv2_train 100 3d_fullres 0 -tr nnUNetTrainerDAExtGPU +# e.g. SMAUGLAB_PARAMS_JSON=/abs/path/params.json nnUNetv2_train 100 3d_fullres 0 -tr nnUNetTrainerDAExtGPU ``` **Config JSON** diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 221a5ff..ea8368a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -68,3 +68,11 @@ jobs: - name: Run mypy run: mypy smauglab/ + + # The README augmentation matrix and smauglab/configs/all_augmentations.json are + # generated from the registry. Checked here rather than in a pre-commit hook: the + # generator has to import torch and kornia to populate the registry, which is far + # too slow to run on every commit. Contributors run the script when they add an + # augmentation; this is the backstop when they forget. + - name: Check generated registry artifacts are current + run: smauglab matrix --check diff --git a/.gitignore b/.gitignore index 60f1785..8dcd0ee 100644 --- a/.gitignore +++ b/.gitignore @@ -40,11 +40,29 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +# Repo-wide, so per-experiment config JSONs and personal scratch directories stay +# out (see 7ff2088). The cost is that a genuinely new *package* file needs an +# explicit un-ignore below -- `git add -f` alone is not enough, because anything +# still ignored is invisible to `git status` and gets silently left behind. *.json *.yaml *.nii *.nii.gz +# Generated from the registry and checked by CI (`smauglab matrix --check`), and +# shipped as package data by pyproject's `include`. +!smauglab/configs/all_augmentations.json + +# Test fixtures. unit_tests/test_cli.py points LEGACY at one of these. +!unit_tests/fixtures/** + +# The pre-registry config migrator, which config.py's MIGRATE_HINT and the README +# both point users at. Listed by extension rather than `migration/**`, which would +# re-include migration/__pycache__ -- an un-ignore beats the earlier __pycache__/ rule. +!migration/*.py +!migration/*.json +!migration/*.md + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5568e0e..830f26a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,8 +32,18 @@ pytest -m "not slow" # skip the wheel-building packaging tests pre-commit run --all-files # everything CI's lint job runs ruff check . # lint only ruff format . # format in place +mypy smauglab/ # CI's typecheck job +smauglab matrix --check # are the generated README matrix and template current? ``` +`smauglab matrix --check` is a CI step rather than a pre-commit hook: populating +the registry imports torch and kornia, which is far too slow to pay on every +commit. Run `smauglab matrix --write` when you add an augmentation. + +Some tests need the domain-transfer LUT bank, which is built offline and not +shipped. They skip without it; export `SMAUGLAB_DOMAIN_BANK=/path/to/bank.npz` +to run them. + ## The test suite `unit_tests/` runs entirely on CPU with 24×24×24 volumes and needs no image @@ -43,7 +53,13 @@ data on disk, so it is fast enough to gate every pull request. | --- | --- | | `helpers.py` | `SmaugLabTestCase` base class (RNG seeding, test volumes) and config lookup | | `test_imports.py` | Every module under `smauglab/` imports cleanly | +| `test_registry.py` | Registry mechanics, against synthetic classes | +| `test_registered_augmentations.py` | The real registry, and that the generated matrix and template are current | +| `test_variant_leaves.py` | Each variant leaf fixes its variant and hides it from the config surface | +| `test_builder.py` | Pipeline order, bucketing, runtime context, and strict validation | +| `test_migration.py` | Migrated configs reproduce the pre-registry builder exactly | | `test_configs.py` | Every shipped config parses, builds a pipeline, runs a forward pass, and is reproducible under a fixed seed | +| `test_trainers.py` | The nnU-Net trainer builds what each config asks for, and puts deep supervision in the right place | | `test_transforms_gpu.py` | Each GPU transform in isolation | | `test_packaging.py` | Builds the real wheel and checks its contents | @@ -60,16 +76,90 @@ does not hide the rest and the failure names the offending item — look for `SmaugLabTestCase` to get seeded RNGs and the shared `tiny_volume()` / `tiny_seg()` helpers. -Transforms in `test_transforms_gpu.py` are discovered by introspection, so a -new transform class is covered as soon as it lands — as long as it can be built -with default arguments. If yours needs configuration, cover it by adding a -config JSON under `smauglab/configs/`, which `test_configs.py` picks up -automatically. +Transforms in `test_transforms_gpu.py` come from the registry, so a new +augmentation is covered the moment it is registered — no list to update, and no +denylist of helper classes. Transforms with a required constructor argument are +covered through `test_configs.py` instead, which picks up any config JSON added +under `smauglab/configs/`. + +`test_migration.py` replays `fixtures/legacy_effective_kwargs.json`: a record of +every constructor call the pre-registry builder made, captured while it still +existed. It is what proves the config rename changed no behaviour, so treat it as +append-only — regenerating it from current code would make it prove nothing. Note that these are smoke and contract tests: they check that transforms run, preserve shape, stay finite, and do not corrupt the segmentation labels. They do not verify that an augmentation is *visually* or *statistically* correct. +## The nnU-Net trainer + +There is one trainer class, `nnUNetTrainerDAExtGPU`. It reads a config and builds +whatever the config names: the `CPU` section runs in the dataloader worker, the `GPU` +section runs on the batch in `train_step`. Three classes used to encode that split in +their names; the config already carried it. + +The name is load-bearing and must not change: nnU-Net writes the trainer class name +into every checkpoint and resolves the class from it at inference, so renaming it +would make every previously trained model unloadable. + +Two behaviours are decided from the config rather than hardcoded: + +* **Deep supervision** is downsampled in `train_step` when there are GPU + augmentations (the mask is deformed there) and in the dataloader when there are + not. Getting this backwards trains against targets that no longer match the image. +* **Pipeline arrangement** comes from `pipeline.mode` -- `sequential`, + `random_order` or `random_order_ta`. + +## Adding an augmentation + +Config keys are class names, exactly, and parameters are constructor arguments, +exactly. Both are checked against the registry, so an augmentation is reachable +from a config only once it is registered. + +1. **Write the class.** No `**kwargs` — parameter validation reads the signature, + so anything hidden behind it is invisible to a config and to `smauglab show`. + Declare `p` and `p_batch` explicitly on GPU transforms. Give every parameter a + default that is the value you actually want, not a `None` sentinel you resolve + in the body: the signature is what the generated template advertises. + +2. **Register it.** + + ```python + @register( + aug_id=AugId.SCHARR, # add a member if the concept is new + backend=Backend.GPU, + group=AugType.TA, # GEO / GE / TA, used for random-order bucketing + order=90, # pipeline position; 10-spaced, unique per backend + ) + class RandomScharrGPU(_RandomConvBaseGPU): ... + ``` + + Third-party classes cannot be decorated; register those in + `smauglab/transforms/cpu/external.py` instead. + + Less common fields: `forwards_to` when the constructor genuinely passes kwargs + on to another class, `context_params` for values the trainer supplies at + runtime, `param_adapters` when a value must be wrapped before use, and + `external_asset` when it needs a file the wheel does not ship. + +3. **Regenerate and commit the artefacts.** + + ```bash + smauglab matrix --write + ``` + +4. **Check it.** + + ```bash + smauglab show YourTransform + pytest unit_tests/test_registry.py unit_tests/test_registered_augmentations.py + ``` + +If a variant differs only by one fixed argument — a kernel, a function, an +inversion flag — give it its own thin subclass rather than exposing the argument. +One class per config key is what keeps a config from expressing the same +augmentation two different ways. + ## Style Ruff handles both linting and formatting; the configuration lives in diff --git a/README.md b/README.md index e2e1853..646931f 100644 --- a/README.md +++ b/README.md @@ -81,10 +81,14 @@ Then, when you run nnUNet training as usual, specifying the SmaugLab trainer, fo nnUNetv2_train 100 3d_fullres 0 -tr nnUNetTrainerDAExtGPU -p nnUNetPlans ``` -You can also specify your data augmentation parameters by providing a JSON file using the environment variable `SMAUGLAB_PARAMS_GPU_JSON`: +There is one trainer. Which augmentations run is decided by the config, not by the +trainer: a config's `CPU` section runs in the dataloader worker and its `GPU` section +runs on the batch, so the same trainer covers CPU-only, GPU-only and mixed setups. + +Point it at a config with `SMAUGLAB_PARAMS_JSON`: > **Note:** By default [smauglab/configs/transform_params_gpu.json](https://github.com/neuropoly/SmaugLab/blob/main/smauglab/configs/transform_params_gpu.json) is used if no file is specified. ```bash -SMAUGLAB_PARAMS_GPU_JSON=/path/to/your/params.json nnUNetv2_train 100 3d_fullres 0 -tr nnUNetTrainerDAExtGPU -p nnUNetPlans +SMAUGLAB_PARAMS_JSON=/path/to/your/params.json nnUNetv2_train 100 3d_fullres 0 -tr nnUNetTrainerDAExtGPU -p nnUNetPlans ``` > ⚠️ **Warning** : To avoid any paths issues, please specify an absolute path to your JSON file. @@ -168,3 +172,73 @@ If you use SmaugLab, please make sure to cite the following paper: year={2026} } ``` + +## Working with augmentations + +The `smauglab` command answers what exists and whether a config is valid, reading +the registry directly so it cannot go out of date: + +```bash +smauglab list --backend gpu # what a GPU config can name, in pipeline order +smauglab list --group TA # just the transfer augmentations +smauglab show RandomScharrGPU # one augmentation's parameters and defaults +smauglab validate my_config.json # strict check, every problem reported at once +smauglab template --backend gpu # a config naming everything, at defaults +smauglab migrate old_run/params.json # bring a pre-registry config forward +smauglab hash my_config.json # content-addressed config identity +``` + +Config keys are class names, exactly, and parameters are constructor arguments, +exactly. Anything else is an error rather than a silent no-op: + +``` +my_config.json: 2 problem(s) + - GPU.unknown GPU augmentation 'ScharrTransform'. + Did you mean: RandomScharrGPU? + - GPU.RandomGaussianNoiseGPU: unknown parameter 'probability'. + 'probability' -> p +``` + +## Available augmentations + +Which augmentations exist, and which backends implement each one. An empty cell +means no implementation on that backend yet. Regenerate with `smauglab matrix --write`. + + +| Augmentation | Group | GPU | CPU | MONAI | +| --- | --- | --- | --- | --- | +| flip | GEO | `RandomFlipTransformGPU` | — | — | +| affine | GEO | `RandomAffineGPU` | — | — | +| crop | GEO | `RandomCropTransformGPU` | — | — | +| spatial | GEO | — | `SpatialTransform` | — | +| gaussian_noise | GE | `RandomGaussianNoiseGPU` | `GaussianNoiseTransform` | — | +| gaussian_blur | GE | `RandomGaussianBlurGPU` | `GaussianBlurTransform` | — | +| brightness | GE | `RandomBrightnessGPU` | `MultiplicativeBrightnessTransform` | — | +| contrast | GE | `RandomContrastGPU` | `ContrastTransform` | — | +| gamma | GE | `RandomGammaGPU` | `GammaTransform` | — | +| inv_gamma | GE | `RandomInvGammaGPU` | `InvertedGammaTransform` | — | +| clamp | GE | `RandomClampGPU` | — | — | +| low_res | GE | `RandomLowResTransformGPU` | `SimulateLowResolutionTransform` | — | +| acq | GE | `RandomAcqTransformGPU` | — | — | +| zscore | GE | `ZscoreNormalizationGPU` | `ZscoreNormalization` | — | +| mirror | GEO | — | `MirrorTransform` | — | +| scharr | TA | `RandomScharrGPU` | `ScharrConvTransform` | — | +| laplace | TA | `RandomLaplaceGPU` | `LaplaceConvTransform` | — | +| unsharp_mask | TA | `RandomUnsharpMaskGPU` | — | — | +| rand_conv | TA | `RandomRandConvGPU` | — | — | +| bias_field | TA | `RandomBiasFieldGPU` | — | — | +| inverse | TA | `RandomInverseGPU` | — | — | +| histogram_equal | TA | `RandomHistogramEqualizationGPU` | `HistogramEqualTransform` | — | +| redistribute_seg | TA | `RandomRedistributeSegGPU` | `RedistributeTransform` | — | +| palette | TA | `RandomPaletteGPU` | — | — | +| domain_transfer | TA | `RandomDomainTransferGPU` | — | — | +| synthseg | TA | `RandomSynthSegGPU` | — | — | +| artifact | TA | — | `ArtifactTransform` | — | +| spatial_custom | GEO | — | `SpatialCustomTransform` | — | +| shape | GE | — | `ShapeTransform` | — | +| func_log1p | TA | `RandomLog1pGPU` | `Log1pTransform` | — | +| func_sqrt | TA | `RandomSqrtGPU` | `SqrtTransform` | — | +| func_sin | TA | `RandomSinGPU` | `SinTransform` | — | +| func_exp | TA | `RandomExpGPU` | `ExpTransform` | — | +| func_sigmoid | TA | `RandomSigmoidGPU` | `SigmoidTransform` | — | + diff --git a/migration/README.md b/migration/README.md new file mode 100644 index 0000000..2e0f309 --- /dev/null +++ b/migration/README.md @@ -0,0 +1,29 @@ +# docs + +## `hash_migration.json` + +`segtransferaug/run_trainings.py` names each experiment directory + + Dataset{id}-{trainer}-aug-{transform_hash}-c-{config_hash} + +where `config_hash` is a sha256 over the canonical JSON of the config and +`transform_hash` is a sha256 over the text of `smauglab/transforms/gpu/transforms.py`. + +The registry migration moved both: every config was rewritten to class-name keys, and +the GPU builder that file contained was replaced by a registry-driven one. Runs made +before the migration keep their old directory names, so anything that looks a run up +by hash needs this table. The canonicalisation itself is unchanged and is asserted +byte-for-byte against the original implementation, so a config that did not change +would still hash the same. + +Two old hashes map to configs that are now byte-identical: + +* `0b639cf1` covered `transform_params_gpu_default01-23.json` and + `transform_params_one-sequence-to-segment-them-all.json`, which had the same content + all along under different names. +* `transform_params_gpu_default01-23_ICGT_plus.json` had its own hash only because of + the dead `ImageContrastGPUTransform` block; with that gone it is a duplicate of + `default01-23`. + +Known consumer, updated to accept both: `segtransferaug/refinement/config.py`, whose +`DOMAIN_TRANSFER_HASHES` selects the fusion source pool. diff --git a/migration/hash_migration.json b/migration/hash_migration.json new file mode 100644 index 0000000..8f14d2d --- /dev/null +++ b/migration/hash_migration.json @@ -0,0 +1,337 @@ +{ + "_comment": "Experiment directories are named Dataset{id}-{trainer}-aug-{transform_hash}-c-{config_hash}. Renaming the config keys changed every config's content, and rewriting the GPU builder changed the transform source file, so both halves moved. Runs made before the migration keep their old names; this maps them onto the configs that now produce the same augmentations.", + "transform_hash": { + "old": "7579cee8", + "new": "44975815", + "_comment": "sha256 of smauglab/transforms/gpu/transforms.py, which the registry-driven builder replaced" + }, + "config_hash": { + "configs/synthseg_params.json": { + "old": "bbab46b4", + "new": "074e798a" + }, + "configs/transform_params.json": { + "old": "f08f10c9", + "new": "10c4c547" + }, + "configs/transform_params_gpu.json": { + "old": "27056a10", + "new": "49138fe4" + }, + "configs/transform_params_gpu_basedon3d250af0-noGE.json": { + "old": "5b7d3b56", + "new": "f5e5afe9" + }, + "configs/transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json": { + "old": "de5a089c", + "new": "cf003b53" + }, + "configs/transform_params_gpu_basedon3d250af0-noTA-nobias.json": { + "old": "1d5604d8", + "new": "a98193c9" + }, + "configs/transform_params_gpu_basedon3d250af0-noTA.json": { + "old": "dc49d802", + "new": "a646512e" + }, + "configs/transform_params_gpu_basedon3d250af0.json": { + "old": "f87d9f40", + "new": "bdf7884c" + }, + "configs/transform_params_gpu_default01-23-List-TA05.json": { + "old": "6d730468", + "new": "27bbd673" + }, + "configs/transform_params_gpu_default01-23-List-TA08.json": { + "old": "f9d53901", + "new": "02364ce2" + }, + "configs/transform_params_gpu_default01-23-List.json": { + "old": "dbb6c7bd", + "new": "7731d57a" + }, + "configs/transform_params_gpu_default01-23-Scharr.json": { + "old": "34798f3b", + "new": "981ef0b2" + }, + "configs/transform_params_gpu_default01-23-focus.json": { + "old": "fce90d66", + "new": "b82fffdb" + }, + "configs/transform_params_gpu_default01-23-noGE.json": { + "old": "fb357cc3", + "new": "e7451c63" + }, + "configs/transform_params_gpu_default01-23-noMirror-RandConv.json": { + "old": "7ef2f45d", + "new": "cb016cfb" + }, + "configs/transform_params_gpu_default01-23-noMirror.json": { + "old": "7b1f514e", + "new": "7def1352" + }, + "configs/transform_params_gpu_default01-23-noTA.json": { + "old": "256756f0", + "new": "15e5bf37" + }, + "configs/transform_params_gpu_default01-23.json": { + "old": "0b639cf1", + "new": "c69b9872" + }, + "configs/transform_params_gpu_default01-23_CropTransform.json": { + "old": "ce22c512", + "new": "37c71641" + }, + "configs/transform_params_gpu_default01-23_ICGT_plus.json": { + "old": "69826763", + "new": "c69b9872" + }, + "configs/transform_params_gpu_default01-23_ImageContrastGPUTransform.json": { + "old": "4d0a6975", + "new": "b7078b73" + }, + "configs/transform_params_gpu_default01-23_RandomDomainTransferGPU.json": { + "old": "b7860a93", + "new": "9e7d8db6" + }, + "configs/transform_params_gpu_default01-23_Synthseg.json": { + "old": "1d9aebee", + "new": "4bafcfdd" + }, + "configs/transform_params_gpu_default01-23_SynthsegPlus.json": { + "old": "a7433b53", + "new": "3414ba9f" + }, + "configs/transform_params_gpu_inoutseg.json": { + "old": "638a4d3b", + "new": "82f778f3" + }, + "configs/transform_params_gpu_palette-em.json": { + "old": "011beee2", + "new": "cf699dc2" + }, + "configs/transform_params_hybrid.json": { + "old": "736ef018", + "new": "c95704cd" + }, + "configs/transform_params_hybrid_TAGE.json": { + "old": "181aeca5", + "new": "c5fb06c0" + }, + "configs/transform_params_one-sequence-to-segment-them-all.json": { + "old": "0b639cf1", + "new": "c69b9872" + }, + "configs_paul/transform_params_gpu_default01-23.json": { + "old": "0b639cf1", + "new": "c69b9872" + }, + "configs_paul/transform_params_gpu_default01-23_ImageContrastV26_6_2GPUTransform.json": { + "old": "edee2361", + "new": "910e29a0" + }, + "configs_paul/transform_params_gpu_default01-23_Synthseg.json": { + "old": "1d9aebee", + "new": "4bafcfdd" + }, + "configs_paul/transform_params_gpu_default01-23_Synthseg_EM.json": { + "old": "77e8ea61", + "new": "0d5d802f" + }, + "configs_paul/transform_params_gpu_default01-23_smauglabAug_ImageContrastV26_6_2GPUTransform_train025.json": { + "old": "7d620635", + "new": "6a4219e8" + } + }, + "old_to_new": { + "bbab46b4": { + "new": "074e798a", + "configs": [ + "configs/synthseg_params.json" + ] + }, + "f08f10c9": { + "new": "10c4c547", + "configs": [ + "configs/transform_params.json" + ] + }, + "27056a10": { + "new": "49138fe4", + "configs": [ + "configs/transform_params_gpu.json" + ] + }, + "5b7d3b56": { + "new": "f5e5afe9", + "configs": [ + "configs/transform_params_gpu_basedon3d250af0-noGE.json" + ] + }, + "de5a089c": { + "new": "cf003b53", + "configs": [ + "configs/transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json" + ] + }, + "1d5604d8": { + "new": "a98193c9", + "configs": [ + "configs/transform_params_gpu_basedon3d250af0-noTA-nobias.json" + ] + }, + "dc49d802": { + "new": "a646512e", + "configs": [ + "configs/transform_params_gpu_basedon3d250af0-noTA.json" + ] + }, + "f87d9f40": { + "new": "bdf7884c", + "configs": [ + "configs/transform_params_gpu_basedon3d250af0.json" + ] + }, + "6d730468": { + "new": "27bbd673", + "configs": [ + "configs/transform_params_gpu_default01-23-List-TA05.json" + ] + }, + "f9d53901": { + "new": "02364ce2", + "configs": [ + "configs/transform_params_gpu_default01-23-List-TA08.json" + ] + }, + "dbb6c7bd": { + "new": "7731d57a", + "configs": [ + "configs/transform_params_gpu_default01-23-List.json" + ] + }, + "34798f3b": { + "new": "981ef0b2", + "configs": [ + "configs/transform_params_gpu_default01-23-Scharr.json" + ] + }, + "fce90d66": { + "new": "b82fffdb", + "configs": [ + "configs/transform_params_gpu_default01-23-focus.json" + ] + }, + "fb357cc3": { + "new": "e7451c63", + "configs": [ + "configs/transform_params_gpu_default01-23-noGE.json" + ] + }, + "7ef2f45d": { + "new": "cb016cfb", + "configs": [ + "configs/transform_params_gpu_default01-23-noMirror-RandConv.json" + ] + }, + "7b1f514e": { + "new": "7def1352", + "configs": [ + "configs/transform_params_gpu_default01-23-noMirror.json" + ] + }, + "256756f0": { + "new": "15e5bf37", + "configs": [ + "configs/transform_params_gpu_default01-23-noTA.json" + ] + }, + "0b639cf1": { + "new": "c69b9872", + "configs": [ + "configs/transform_params_gpu_default01-23.json", + "configs/transform_params_one-sequence-to-segment-them-all.json", + "configs_paul/transform_params_gpu_default01-23.json" + ] + }, + "ce22c512": { + "new": "37c71641", + "configs": [ + "configs/transform_params_gpu_default01-23_CropTransform.json" + ] + }, + "69826763": { + "new": "c69b9872", + "configs": [ + "configs/transform_params_gpu_default01-23_ICGT_plus.json" + ] + }, + "4d0a6975": { + "new": "b7078b73", + "configs": [ + "configs/transform_params_gpu_default01-23_ImageContrastGPUTransform.json" + ] + }, + "b7860a93": { + "new": "9e7d8db6", + "configs": [ + "configs/transform_params_gpu_default01-23_RandomDomainTransferGPU.json" + ] + }, + "1d9aebee": { + "new": "4bafcfdd", + "configs": [ + "configs/transform_params_gpu_default01-23_Synthseg.json", + "configs_paul/transform_params_gpu_default01-23_Synthseg.json" + ] + }, + "a7433b53": { + "new": "3414ba9f", + "configs": [ + "configs/transform_params_gpu_default01-23_SynthsegPlus.json" + ] + }, + "638a4d3b": { + "new": "82f778f3", + "configs": [ + "configs/transform_params_gpu_inoutseg.json" + ] + }, + "011beee2": { + "new": "cf699dc2", + "configs": [ + "configs/transform_params_gpu_palette-em.json" + ] + }, + "736ef018": { + "new": "c95704cd", + "configs": [ + "configs/transform_params_hybrid.json" + ] + }, + "181aeca5": { + "new": "c5fb06c0", + "configs": [ + "configs/transform_params_hybrid_TAGE.json" + ] + }, + "edee2361": { + "new": "910e29a0", + "configs": [ + "configs_paul/transform_params_gpu_default01-23_ImageContrastV26_6_2GPUTransform.json" + ] + }, + "77e8ea61": { + "new": "0d5d802f", + "configs": [ + "configs_paul/transform_params_gpu_default01-23_Synthseg_EM.json" + ] + }, + "7d620635": { + "new": "6a4219e8", + "configs": [ + "configs_paul/transform_params_gpu_default01-23_smauglabAug_ImageContrastV26_6_2GPUTransform_train025.json" + ] + } + } +} diff --git a/migration/migrate.py b/migration/migrate.py new file mode 100644 index 0000000..bda700a --- /dev/null +++ b/migration/migrate.py @@ -0,0 +1,471 @@ +"""Rewrite a pre-registry config into the current format. + +Config keys used to be loosely-related labels: `ScharrTransform` built a +`RandomConvTransformGPU(kernel_type="Scharr")`, `SynthSeg` built a +`RandomSynthSegGPU`, and `nnUNetSpatialTransform` built nothing at all in the GPU +pipeline. A key is now the class name, exactly, and a parameter is a constructor +argument, exactly. + +Nothing here is consulted when *loading* a config -- old spellings do not work, by +design. This module exists only to rewrite files, so that configs sitting in old +experiment output folders can be brought forward instead of silently rotting. A +test asserts that no legacy key in `LEGACY_KEYS` resolves through the registry. + +Usage: + smauglab migrate OLD.json -o NEW.json + smauglab migrate --check smauglab/configs/*.json +""" + +from __future__ import annotations + +import argparse +import copy +import json +import sys +from pathlib import Path +from typing import Any + +from smauglab import registry +from smauglab.registry import Backend + +# --- key mapping ----------------------------------------------------------------- + +#: Legacy GPU config key -> current class name. +LEGACY_GPU_KEYS: dict[str, str] = { + "FlipTransform": "RandomFlipTransformGPU", + "AffineTransform": "RandomAffineGPU", + "SynthSeg": "RandomSynthSegGPU", + "RandomPALETTETransform": "RandomPaletteGPU", + # The class was renamed twice before the registry existed; both spellings appear + # in configs_paul and both mean what is now RandomPaletteGPU. + "ImageContrastV26_6_2GPUTransform": "RandomPaletteGPU", + "RandomDomainTransferGPU": "RandomDomainTransferGPU", + "DomainTransferTransform": "RandomDomainTransferGPU", + "InverseTransform": "RandomInverseGPU", + "HistogramEqualizationTransform": "RandomHistogramEqualizationGPU", + "RedistributeSegTransform": "RandomRedistributeSegGPU", + "ScharrTransform": "RandomScharrGPU", + "UnsharpMaskTransform": "RandomUnsharpMaskGPU", + "RandomConvTransform": "RandomRandConvGPU", + "ClampTransform": "RandomClampGPU", + "GaussianNoiseTransform": "RandomGaussianNoiseGPU", + "GaussianBlurTransform": "RandomGaussianBlurGPU", + "BrightnessTransform": "RandomBrightnessGPU", + "GammaTransform": "RandomGammaGPU", + "InvGammaTransform": "RandomInvGammaGPU", + "ContrastTransform": "RandomContrastGPU", + "SimulateLowResTransform": "RandomLowResTransformGPU", + "AcqTransform": "RandomAcqTransformGPU", + "CropTransform": "RandomCropTransformGPU", + "BiasFieldTransform": "RandomBiasFieldGPU", + "ZscoreNormalizationTransform": "ZscoreNormalizationGPU", +} + +#: Legacy CPU config key -> current class name. Most already matched their class. +LEGACY_CPU_KEYS: dict[str, str] = { + "HistogramEqualTransform": "HistogramEqualTransform", + "RedistributeTransform": "RedistributeTransform", + "ShapeTransform": "ShapeTransform", + "ArtifactTransform": "ArtifactTransform", + "SpatialCustomTransform": "SpatialCustomTransform", + "SpatialTransform": "SpatialTransform", + "GaussianNoiseTransform": "GaussianNoiseTransform", + "GaussianBlurTransform": "GaussianBlurTransform", + "MultiplicativeBrightnessTransform": "MultiplicativeBrightnessTransform", + "ContrastTransform": "ContrastTransform", + "SimulateLowResolutionTransform": "SimulateLowResolutionTransform", + "GammaTransform": "GammaTransform", + "GammaTransform_invert": "InvertedGammaTransform", +} + +#: `FunctionTransform` was one key that the builder fanned out over a hardcoded +#: lambda list, so it becomes five blocks -- one per function, in ladder order. +FUNCTION_LEAVES = { + Backend.GPU: ["RandomLog1pGPU", "RandomSqrtGPU", "RandomSinGPU", "RandomExpGPU", "RandomSigmoidGPU"], + Backend.CPU: ["Log1pTransform", "SqrtTransform", "SinTransform", "ExpTransform", "SigmoidTransform"], +} + +#: `ConvTransform` carried its kernel in a parameter; the kernel now picks the class. +CPU_CONV_BY_KERNEL = {"Laplace": "LaplaceConvTransform", "Scharr": "ScharrConvTransform"} + +#: Which config parameters the old builder actually forwarded, per legacy key. +#: +#: Anything a config set outside these sets was silently discarded -- the builder +#: simply never read it, and `**kwargs` swallowed the rest. Carrying such a value +#: forward would *activate* a setting that has never had any effect, so the migrator +#: drops it and says so. `transform_params_gpu_inoutseg.json` really does set +#: `mix_prob` on `GaussianBlurTransform`, which the builder never passed on. +#: +#: Derived mechanically from `_build_transforms` while it still existed. +FORWARDED_GPU_PARAMS: dict[str, set[str]] = { + "FlipTransform": {"flip_axis", "keepdim", "probability", "same_on_batch"}, + "AffineTransform": {"degrees", "probability", "resample", "scale", "shear", "translate"}, + "SynthSeg": {"probability"}, + "RandomPALETTETransform": { + "alpha_magnitude_range", + "blur_sigmas", + "c_choices", + "dark_threshold", + "label_classes", + "label_remap_prob", + "min_label_voxels", + "n_kmeans_subsample", + "probability", + "s_choices", + "skip_parcellation_prob", + "skip_sub_parc_prob", + }, + "RandomDomainTransferGPU": { + "any_source", + "apply_to_channel", + "bank_path", + "bias_field_std", + "bias_scale", + "blend_concentration", + "blend_targets", + "include_self", + "p_class_mix", + "p_spatial_mix", + "pct", + "probability", + "same_on_batch", + "sigma", + "source_label", + "spatial_mix_gain", + "spatial_mix_scale", + "targets", + "zscore_io", + }, + "InverseTransform": {"in_seg", "mix_in_out", "mix_prob", "out_seg", "probability", "retain_stats"}, + "HistogramEqualizationTransform": {"in_seg", "mix_in_out", "mix_prob", "out_seg", "probability", "retain_stats"}, + "RedistributeSegTransform": {"dilation_iterations_range", "in_seg", "probability", "retain_stats", "std_noise_range"}, + "ScharrTransform": {"absolute", "in_seg", "mix_in_out", "mix_prob", "out_seg", "probability", "retain_stats"}, + "UnsharpMaskTransform": {"in_seg", "mix_in_out", "mix_prob", "out_seg", "probability", "sigma", "unsharp_amount"}, + "RandomConvTransform": {"in_seg", "kernel_sizes", "mix_in_out", "mix_prob", "out_seg", "probability", "retain_stats"}, + "ClampTransform": {"in_seg", "max_clamp_amount", "mix_in_out", "out_seg", "probability", "retain_stats"}, + "GaussianNoiseTransform": {"in_seg", "mean", "mix_in_out", "out_seg", "probability", "std"}, + "GaussianBlurTransform": {"in_seg", "mix_in_out", "out_seg", "probability", "sigma"}, + "BrightnessTransform": {"brightness_range", "in_seg", "mix_in_out", "out_seg", "probability"}, + "GammaTransform": {"gamma_range", "in_seg", "mix_in_out", "out_seg", "probability", "retain_stats"}, + "InvGammaTransform": {"gamma_range", "in_seg", "mix_in_out", "out_seg", "probability", "retain_stats"}, + "ContrastTransform": {"contrast_range", "in_seg", "mix_in_out", "out_seg", "probability", "retain_stats"}, + "FunctionTransform": {"in_seg", "mix_in_out", "out_seg", "probability", "retain_stats"}, + "SimulateLowResTransform": {"probability", "same_on_batch", "scale"}, + "AcqTransform": {"probability", "same_on_batch", "scale"}, + "CropTransform": {"crop", "pos", "probability", "same_on_batch"}, + "BiasFieldTransform": {"coefficients", "in_seg", "mix_in_out", "out_seg", "probability", "retain_stats"}, + "ZscoreNormalizationTransform": {"probability"}, +} + +FORWARDED_CPU_PARAMS: dict[str, set[str]] = { + "ConvTransform": {"absolute", "kernel_type", "probability", "retain_stats"}, + "FunctionTransform": {"probability", "retain_stats"}, + "HistogramEqualTransform": {"probability", "retain_stats"}, + "RedistributeTransform": {"in_seg", "probability", "retain_stats"}, + "ShapeTransform": {"ignore_axes", "probability", "shape_min"}, + "ArtifactTransform": {"bias_field", "blur", "ghosting", "motion", "noise", "probability", "random_pick", "spike", "swap"}, + "SpatialCustomTransform": {"affine", "anisotropy", "elastic", "flip", "probability", "random_pick"}, + "SpatialTransform": { + "bg_style_seg_sampling", + "p_elastic_deform", + "p_rotation", + "p_scaling", + "p_synchronize_scaling_across_axes", + "patch_center_dist_from_border", + "random_crop", + "scaling", + }, + "GaussianNoiseTransform": {"noise_variance", "p_per_channel", "probability", "synchronize_channels"}, + "GaussianBlurTransform": {"benchmark", "blur_sigma", "p_per_channel", "probability", "synchronize_axes", "synchronize_channels"}, + "MultiplicativeBrightnessTransform": {"multiplier_range", "p_per_channel", "probability", "synchronize_channels"}, + "ContrastTransform": {"contrast_range", "p_per_channel", "preserve_range", "probability", "synchronize_channels"}, + "SimulateLowResolutionTransform": { + "allowed_channels", + "ignore_axes", + "p_per_channel", + "probability", + "scale", + "synchronize_axes", + "synchronize_channels", + }, + "GammaTransform_invert": {"gamma", "p_per_channel", "p_retain_stats", "probability", "synchronize_channels"}, + "GammaTransform": {"gamma", "p_invert_image", "p_per_channel", "p_retain_stats", "probability", "synchronize_channels"}, +} + +#: SynthSeg is the exception: the builder forwarded every key except `probability` +#: straight to SynthSegGenerator, so its accepted set is the generator's signature. +FORWARD_EVERYTHING = {"SynthSeg"} + +#: Renamed parameters. +PARAM_RENAMES = {"probability": "p", "shear": "shears"} + +#: Values the old builder hardcoded, which no config carried and no class defaults to. +#: Without these the migrated config would quietly change behaviour -- nnU-Net's +#: SpatialTransform defaults `mode_seg` to "bilinear", which would interpolate +#: segmentation labels into fractional values instead of resampling them nearest. +LADDER_CONSTANTS: dict[tuple[str, str], dict[str, Any]] = { + ("CPU", "SpatialTransform"): {"mode_seg": "nearest"}, +} + +#: Keys whose transform no longer exists at all. The classes were deleted or only +#: ever lived on a branch, so these blocks have been silently doing nothing. +DEAD_KEYS = { + "ImageContrastGPUTransform": "the class was removed in 0d6270d; its num_bins parameter maps to nothing that still exists", + "PaletteSynthesisTransform": "only ever existed on the palette-refactor branch", +} + +#: Every legacy spelling, for the test that proves none of them still resolve. +LEGACY_KEYS = set(LEGACY_GPU_KEYS) | set(LEGACY_CPU_KEYS) | set(DEAD_KEYS) | {"FunctionTransform", "ConvTransform"} + +RESERVED_SECTIONS = ("GPU", "CPU", "MONAI", "pipeline") + + +class MigrationError(Exception): + """The config cannot be migrated without a human deciding something.""" + + +#: Parameter names that only ever appear in a pre-registry config. +_LEGACY_PARAM_MARKERS = frozenset({"probability", "shear", "kernel_type"}) + + +def is_current(payload: dict) -> bool: + """True if the document is already in the current format. + + Decided per document, not per key, because most CPU legacy keys *are* also + current class names (`RedistributeTransform` names a class and named one + before). Keying off that coincidence made the migrator skip the + forwarded-parameter filter on legacy CPU configs and leak parameters the old + builder never passed on. + """ + if not any(section in payload for section in ("GPU", "CPU")): + return False # flat legacy layout, backend never stated + for backend in (Backend.GPU, Backend.CPU): + section = payload.get(backend.value) + if not isinstance(section, dict): + continue + known = set(registry.names(backend)) + for key, block in section.items(): + if key.startswith("_"): + continue + if key not in known: + return False + if isinstance(block, dict) and _LEGACY_PARAM_MARKERS & set(block): + return False + return True + + +def _looks_like_gpu(section: dict) -> bool: + """A flat legacy config carries no section marker, so infer from its keys.""" + gpu_only = {"FlipTransform", "AffineTransform", "ScharrTransform", "ZscoreNormalizationTransform", "AcqTransform"} + cpu_only = {"SpatialCustomTransform", "ArtifactTransform", "ShapeTransform", "GammaTransform_invert"} + return len(gpu_only & set(section)) >= len(cpu_only & set(section)) + + +def _migrate_block(new_name: str, params: dict, backend: Backend, source: str, legacy_key: str | None = None) -> dict[str, Any]: + """Rename parameters, and drop the ones that never reached the transform.""" + entry = registry.get(new_name, backend) + accepted = set(registry.accepted_params(entry)) + forwarded = (FORWARDED_GPU_PARAMS if backend is Backend.GPU else FORWARDED_CPU_PARAMS).get(legacy_key or "") + + out: dict[str, Any] = {} + for key, value in params.items(): + name = PARAM_RENAMES.get(key, key) + if name in entry.context_params: + # Supplied by the trainer at runtime; a config value was always ignored. + continue + if forwarded is not None and legacy_key not in FORWARD_EVERYTHING and key not in forwarded: + # The old builder never read this key, so it has never had an effect. + # Keeping it would switch on a setting that has always been dormant. + continue + if name not in accepted: + # Silently swallowed by **kwargs before, so dropping it changes nothing. + continue + out[name] = value + if backend is Backend.GPU and "p" not in out: + # The old builder defaulted an absent probability to 0 (the block was there + # but off). Spelling it out keeps that meaning now that an absent block is + # what "not in the pipeline" means. + out["p"] = 0 + # Values the builder hardcoded rather than reading from the config. + out.update(LADDER_CONSTANTS.get((backend.value, new_name), {})) + _ = source + return out + + +def _migrate_section(section: dict, backend: Backend, source: str) -> tuple[dict, list[str]]: + """Return the migrated section plus a list of human-readable notes.""" + keymap = LEGACY_GPU_KEYS if backend is Backend.GPU else LEGACY_CPU_KEYS + forwarded_table = FORWARDED_GPU_PARAMS if backend is Backend.GPU else FORWARDED_CPU_PARAMS + migrated: dict[str, Any] = {} + notes: list[str] = [] + + # The CPU builder read `retain_stats` from the *top level* of the config and + # applied it to every block that takes one, rather than reading it per block. + # Push it down so each transform states its own value. + shared_retain_stats = section.get("retain_stats") if backend is Backend.CPU else None + if shared_retain_stats is not None: + notes.append(f"pushed top-level retain_stats={shared_retain_stats} into the blocks that read it") + + for key, value in section.items(): + if key.startswith("_") or key in RESERVED_SECTIONS: + continue + if key in ("Comment1", "Comment2"): + migrated[f"_comment{key[-1]}"] = value + continue + if key in DEAD_KEYS: + notes.append(f"dropped {key!r}: {DEAD_KEYS[key]}") + continue + if key in ("mirror_axes", "retain_stats", "nnUNetSpatialTransform", "RandomChooseXTransforms"): + continue # relocated by the caller + if not isinstance(value, dict): + notes.append(f"dropped non-block key {key!r}") + continue + + block = value + if shared_retain_stats is not None and "retain_stats" in forwarded_table.get(key, ()): + block = {**value, "retain_stats": shared_retain_stats} + + if key == "FunctionTransform": + for leaf in FUNCTION_LEAVES[backend]: + migrated[leaf] = _migrate_block(leaf, block, backend, source, legacy_key=key) + notes.append(f"expanded FunctionTransform into {len(FUNCTION_LEAVES[backend])} blocks") + continue + + if key == "ConvTransform" and backend is Backend.CPU: + kernel = block.get("kernel_type", "Scharr") + if kernel not in CPU_CONV_BY_KERNEL: + raise MigrationError(f"{source}: ConvTransform has unsupported kernel_type {kernel!r}") + leaf = CPU_CONV_BY_KERNEL[kernel] + migrated[leaf] = _migrate_block(leaf, block, backend, source, legacy_key=key) + notes.append(f"ConvTransform(kernel_type={kernel!r}) -> {leaf}") + continue + + new_name = keymap.get(key) + if new_name is None: + raise MigrationError( + f"{source}: no migration rule for {backend.value} key {key!r}. " + "Add it to LEGACY_GPU_KEYS/LEGACY_CPU_KEYS, or delete the block if the transform is gone." + ) + migrated[new_name] = _migrate_block(new_name, block, backend, source, legacy_key=key) + + # Emit in registry order so the file reads in the order the pipeline runs. + order = {name: i for i, name in enumerate(registry.names(backend))} + ordered = {k: migrated[k] for k in sorted(migrated, key=lambda n: (n.startswith("_") is False, order.get(n, 10_000), n))} + return ordered, notes + + +def migrate(payload: dict, source: str = "") -> tuple[dict, list[str]]: + """Migrate a whole config document. Returns (new payload, notes).""" + payload = copy.deepcopy(payload) + out: dict[str, Any] = {} + notes: list[str] = [] + + if is_current(payload): + # Nothing to rewrite; only normalise section order so migrating twice + # produces the same bytes as migrating once. + return _ordered(payload), notes + + sections: dict[Backend, dict] = {} + if "GPU" in payload or "CPU" in payload: + if isinstance(payload.get("GPU"), dict): + sections[Backend.GPU] = payload["GPU"] + if isinstance(payload.get("CPU"), dict): + sections[Backend.CPU] = payload["CPU"] + else: + sections[Backend.GPU if _looks_like_gpu(payload) else Backend.CPU] = payload + + out.update({key: value for key, value in payload.items() if key.startswith("_")}) + + for backend, section in sections.items(): + migrated, section_notes = _migrate_section(section, backend, source) + notes.extend(section_notes) + + # nnUNetSpatialTransform is not a GPU augmentation -- AugTransformsGPU never + # read it. It configures nnU-Net's own CPU-side SpatialTransform, which the + # trainer used to fetch by re-opening the JSON a second time. + spatial = section.get("nnUNetSpatialTransform") + if isinstance(spatial, dict): + target = out.setdefault(Backend.CPU.value, {}) + target["SpatialTransform"] = _migrate_block("SpatialTransform", spatial, Backend.CPU, source, legacy_key="SpatialTransform") + notes.append("moved nnUNetSpatialTransform -> CPU.SpatialTransform") + + if backend is Backend.CPU: + axes = section.get("mirror_axes") + if axes: + migrated["MirrorTransform"] = {"allowed_axes": axes} + notes.append("moved mirror_axes -> CPU.MirrorTransform.allowed_axes") + + choose = section.get("RandomChooseXTransforms") + if isinstance(choose, dict): + out.setdefault("pipeline", {})["random_choose"] = choose + notes.append("moved RandomChooseXTransforms -> pipeline.random_choose") + + if migrated: + existing = out.get(backend.value, {}) + out[backend.value] = {**migrated, **existing} + + return _ordered(out), notes + + +def _ordered(payload: dict) -> dict: + """Fixed section order, so migrating twice gives the same bytes as once. + + Without this the order depends on whether a relocation + (nnUNetSpatialTransform -> CPU.SpatialTransform) created the CPU section first. + """ + order = ["GPU", "CPU", "MONAI", "pipeline"] + out = {k: payload[k] for k in payload if k.startswith("_")} + out.update({k: payload[k] for k in order if k in payload}) + out.update({k: v for k, v in payload.items() if k not in out}) + return out + + +def needs_migration(payload: dict) -> bool: + """True if the document is not already in the current format.""" + try: + migrated, _ = migrate(payload) + except MigrationError: + return True + return migrated != payload + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(prog="smauglab migrate", description=__doc__) + parser.add_argument("configs", nargs="+", type=Path) + parser.add_argument("-o", "--output", type=Path, help="write here instead of alongside the input") + parser.add_argument("--in-place", action="store_true") + parser.add_argument("--check", action="store_true", help="exit 1 if any config would change") + args = parser.parse_args(argv) + + if args.output and len(args.configs) > 1: + parser.error("-o takes a single input config") + + stale = 0 + for path in args.configs: + payload = json.loads(path.read_text()) + try: + migrated, notes = migrate(payload, source=path.name) + except MigrationError as exc: + print(f"{path}: {exc}", file=sys.stderr) + return 2 + + text = json.dumps(migrated, indent=4) + "\n" + if args.check: + if path.read_text() != text: + stale += 1 + print(f"needs migration: {path}") + for note in notes: + print(f" {note}") + continue + + target = args.output or (path if args.in_place else path.with_suffix(".migrated.json")) + target.write_text(text) + print(f"{path} -> {target}") + for note in notes: + print(f" {note}") + + return 1 if stale else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/pyproject.toml b/pyproject.toml index cdc6cec..bf6e556 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,9 +41,11 @@ keywords = [ "offline augmentation", "mri", ] -# smauglab has no __init__.py anywhere, so it is a PEP 420 namespace package. -# poetry-core still walks the tree correctly; the CI build job and -# unit_tests/test_packaging.py assert the wheel really contains every module. +# smauglab is a regular package: every directory has an __init__.py. It used to be a +# PEP 420 namespace package, which was dropped because the augmentation registry needs +# a deterministic import-time population point, and because a namespace package lets a +# stray smauglab/ elsewhere on sys.path silently merge into this one. +# The CI build job and unit_tests/test_packaging.py assert the wheel contains every module. packages = [{ include = "smauglab" }] # The config JSONs are package data, not code, so they need listing explicitly. # A wheel without them is broken: smauglab resolves its default config through @@ -92,6 +94,7 @@ all = ["monai", "tqdm", "wandb"] dev = ["build", "coverage", "mypy", "pre-commit", "pytest", "pytest-cov", "ruff", "twine"] [tool.poetry.scripts] +smauglab = "smauglab.cli:main" smauglab_add_nnunettrainer = "smauglab.add_trainer:main" [build-system] @@ -253,10 +256,6 @@ python_version = "3.10" # incrementally rather than in one pass. ignore_missing_imports = true warn_redundant_casts = true -# smauglab/, smauglab/transforms/ and smauglab/utils/ have no __init__.py (PEP 420, -# see the `packages` note above), so mypy cannot derive module names without these. -namespace_packages = true -explicit_package_bases = true # Only the shipped package is gated. Tests and standalone scripts are not installed # and assert against literals, which reads badly to a type checker. exclude = ["unit_tests/", "scripts/"] diff --git a/smauglab/__init__.py b/smauglab/__init__.py new file mode 100644 index 0000000..19fee45 --- /dev/null +++ b/smauglab/__init__.py @@ -0,0 +1,18 @@ +"""SmaugLab -- data augmentation strategies for MRI segmentation training. + +Deliberately kept free of imports from `smauglab.transforms`. Pulling the +transforms in here would make a bare `import smauglab` drag in torch, kornia and +batchgeneratorsv2 (several seconds), which every console-script invocation would +then pay for. Import the subpackage you actually need: + + from smauglab.transforms.gpu.transforms import AugTransformsGPU +""" + +from importlib.metadata import PackageNotFoundError, version + +try: + __version__ = version("smauglab") +except PackageNotFoundError: # running from a source tree that was never installed + __version__ = "0.0.0" + +__all__ = ["__version__"] diff --git a/smauglab/cli.py b/smauglab/cli.py new file mode 100644 index 0000000..18378bb --- /dev/null +++ b/smauglab/cli.py @@ -0,0 +1,277 @@ +"""The `smauglab` command line: what augmentations exist, and are my configs valid? + +Before the registry, both questions could only be answered by reading four +hand-written dispatch ladders side by side. These subcommands read the registry, so +they cannot go out of date: + + smauglab list --backend gpu what a GPU config can name, in pipeline order + smauglab matrix which backends implement each augmentation + smauglab show RandomScharrGPU one augmentation's parameters and defaults + smauglab validate config.json strict check, every problem at once + smauglab template --backend gpu a config naming everything, at defaults + smauglab hash config.json content-addressed config identity + +Bringing a pre-registry config forward is a one-time job and is not a subcommand: the +migrator lives in `migration/` in the repository, not in the wheel. + +`hash` is the only one that does not need the registry; the rest import torch and +kornia to populate it, which takes a few seconds on first use. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + +REPO = Path(__file__).resolve().parent.parent +README = REPO / "README.md" +TEMPLATE_PATH = Path(__file__).resolve().parent / "configs" / "all_augmentations.json" +BEGIN = "" +END = "" + + +def _backend(name: str): + from smauglab.registry import Backend + + return Backend[name.upper()] + + +# --- generated artefacts ---------------------------------------------------------- + + +def matrix_block() -> str: + from smauglab import registry + + return f"{BEGIN}\n{registry.render_matrix('md')}\n{END}" + + +def readme_with_matrix(text: str) -> str: + """Replace the marked block, or append the section if it is not there yet.""" + if BEGIN in text and END in text: + head, rest = text.split(BEGIN, 1) + _, tail = rest.split(END, 1) + return f"{head}{matrix_block()}{tail}" + section = ( + "\n## Available augmentations\n\n" + "Which augmentations exist, and which backends implement each one. An empty cell\n" + "means no implementation on that backend yet. Regenerate with `smauglab matrix --write`.\n\n" + matrix_block() + "\n" + ) + return text.rstrip("\n") + "\n" + section + + +def template_json() -> str: + from smauglab import registry + from smauglab.registry import Backend + + payload = {b.value: registry.render_template(b) for b in (Backend.GPU, Backend.CPU)} + return json.dumps(payload, indent=4) + "\n" + + +def _sync(targets: dict[Path, str], *, write: bool, check: bool) -> int: + stale = [path for path, content in targets.items() if not path.is_file() or path.read_text() != content] + if check: + for path in stale: + print(f"out of date: {path.relative_to(REPO)}") + if stale: + print("Run `smauglab matrix --write` / `smauglab template --write` and commit the result.") + return 1 if stale else 0 + if write: + for path in stale: + path.write_text(targets[path]) + print(f"wrote {path.relative_to(REPO)}") + if not stale: + print("already up to date") + return 0 + + +# --- subcommands ------------------------------------------------------------------ + + +def cmd_list(args) -> int: + from smauglab import registry + from smauglab.registry import AugType + + backend = _backend(args.backend) if args.backend else None + group = AugType[args.group.upper()] if args.group else None + entries = registry.entries(backend=backend, group=group) + + if args.json: + print( + json.dumps( + [ + { + "name": e.name, + "backend": e.backend.value, + "aug_id": e.aug_id.value, + "group": e.group.value, + "order": e.order, + "summary": e.summary, + } + for e in entries + ], + indent=2, + ) + ) + return 0 + + if not entries: + print("no augmentations match") + return 0 + width = max(len(e.name) for e in entries) + current = None + for entry in entries: + if entry.backend is not current: + current = entry.backend + print(f"\n{current.value} ({len([e for e in entries if e.backend is current])}), in pipeline order:") + print(f" {entry.order:>4} {entry.name:<{width}} {entry.group.value:<4} {entry.summary}") + return 0 + + +def cmd_matrix(args) -> int: + from smauglab import registry + + if args.write or args.check: + return _sync( + {README: readme_with_matrix(README.read_text()), TEMPLATE_PATH: template_json()}, + write=args.write, + check=args.check, + ) + if args.format == "json": + table = registry.matrix() + print( + json.dumps( + {aug_id.value: {b.value: (e.name if e else None) for b, e in row.items()} for aug_id, row in table.items()}, + indent=2, + ) + ) + return 0 + print(registry.render_matrix(args.format)) + return 0 + + +def cmd_show(args) -> int: + from smauglab import registry + + try: + entry = registry.get(args.name) + except registry.UnknownAugmentationError as exc: + print(str(exc), file=sys.stderr) + return 1 + + print(f"{entry.name} ({entry.backend.value}, group {entry.group.value}, order {entry.order}, aug_id {entry.aug_id.value})") + if entry.summary: + print(f" {entry.summary}") + if entry.forwards_to is not None: + print(f" forwards extra parameters to {entry.forwards_to.__name__}") + if entry.external_asset: + print(f" needs an external asset; set ${entry.external_asset}") + print(f"\n module: {entry.cls.__module__}") + + params = registry.accepted_params(entry) + required = registry.required_params(entry) + print(f"\n parameters ({len(params)}):") + for name in sorted(params): + default = params[name].default + shown = "REQUIRED" if name in required else repr(default) + print(f" {name:<34} {shown}") + if entry.context_params: + print(f"\n supplied by the trainer, not the config: {', '.join(entry.context_params)}") + return 0 + + +def cmd_validate(args) -> int: + from smauglab.config import validate_file + + failed = 0 + for path in args.configs: + problems = validate_file(path) + if problems: + failed += 1 + print(f"{path}: {len(problems)} problem(s)") + for problem in problems: + print(f" - {problem}") + elif not args.quiet: + print(f"{path}: ok") + return 1 if failed else 0 + + +def cmd_template(args) -> int: + from smauglab import registry + + if args.write or args.check: + return _sync({TEMPLATE_PATH: template_json()}, write=args.write, check=args.check) + section = registry.render_template(_backend(args.backend)) + text = json.dumps({args.backend.upper(): section}, indent=4) + "\n" + if args.output: + Path(args.output).write_text(text) + print(f"wrote {args.output}") + else: + print(text, end="") + return 0 + + +def cmd_hash(args) -> int: + from smauglab.config import config_hash + + for path in args.configs: + payload = json.loads(Path(path).read_text()) + print(f"{config_hash(payload)[:8]} {path}") + return 0 + + +# --- wiring ----------------------------------------------------------------------- + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(prog="smauglab", description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + sub = parser.add_subparsers(dest="command", required=True) + + p = sub.add_parser("list", help="registered augmentations, in pipeline order") + p.add_argument("--backend", choices=["gpu", "cpu", "monai"]) + p.add_argument("--group", choices=["geo", "ge", "ta"]) + p.add_argument("--json", action="store_true") + p.set_defaults(func=cmd_list) + + p = sub.add_parser("matrix", help="which backends implement each augmentation") + p.add_argument("--format", choices=["md", "table", "json"], default="table") + p.add_argument("--write", action="store_true", help="update README.md and the template config") + p.add_argument("--check", action="store_true", help="exit 1 if either is out of date") + p.set_defaults(func=cmd_matrix) + + p = sub.add_parser("show", help="one augmentation's parameters and defaults") + p.add_argument("name") + p.set_defaults(func=cmd_show) + + p = sub.add_parser("validate", help="strict config check, reporting every problem") + p.add_argument("configs", nargs="+") + p.add_argument("-q", "--quiet", action="store_true", help="only report failures") + p.set_defaults(func=cmd_validate) + + p = sub.add_parser("template", help="a config naming every augmentation at its defaults") + p.add_argument("--backend", choices=["gpu", "cpu"], default="gpu") + p.add_argument("-o", "--output") + p.add_argument("--write", action="store_true", help="update the shipped template config") + p.add_argument("--check", action="store_true", help="exit 1 if it is out of date") + p.set_defaults(func=cmd_template) + + p = sub.add_parser("hash", help="content-addressed config identity") + p.add_argument("configs", nargs="+") + p.set_defaults(func=cmd_hash) + + return parser + + +def main(argv: list[str] | None = None) -> int: + args = build_parser().parse_args(argv) + if getattr(args, "output", None) and len(getattr(args, "configs", [])) > 1: + print("-o takes a single input config", file=sys.stderr) + return 2 + result: Any = args.func(args) + return int(result or 0) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/smauglab/config.py b/smauglab/config.py new file mode 100644 index 0000000..87413dc --- /dev/null +++ b/smauglab/config.py @@ -0,0 +1,300 @@ +"""Load and validate a SmaugLab augmentation config. + +A config is a JSON document with reserved top-level sections: + + { + "_comment": "...", // any key starting with '_' is ignored + "GPU": { "": { }, ... }, + "CPU": { ... }, + "pipeline": { "random_choose": { ... } } + } + +A key is a class name, exactly, and a parameter is a constructor argument, exactly. +Both are checked against the registry, and every problem in a file is reported at +once rather than one per run. + +A flat document with no section is rejected: it used to be interpreted as "GPU or +CPU, whichever the keys look like", and the two namespaces overlapped enough that +`GaussianBlurTransform` meant different transforms depending on which builder read +it. See `migration/` in the repository to bring an old file forward -- `MIGRATE_HINT` +below is the single source of truth for that pointer, and the error messages quote it. +""" + +from __future__ import annotations + +import copy +import difflib +import functools +import json +from pathlib import Path +from typing import Any + +from smauglab import registry +from smauglab.registry import Backend, InvalidConfigError +from smauglab.transforms.build import PipelineMode, validate_section + +#: Top-level keys that are not augmentation sections. +RESERVED_SECTIONS = ("pipeline",) + +#: Keys the `pipeline` section may hold. +PIPELINE_KEYS = ("mode", "random_choose") + +#: How to bring a pre-registry config forward. The migrator is a one-time tool kept +#: in the repository rather than shipped in the wheel, so this points at the repo. +MIGRATE_HINT = "see migration/ in the SmaugLab repository to bring an old config forward." + + +class SmaugConfig: + """A parsed, validated config document.""" + + def __init__(self, payload: dict, source: str = "") -> None: + self.payload = payload + self.source = source + self.validate() + + # -- construction ------------------------------------------------------------ + + @classmethod + def from_path(cls, path: str | Path) -> SmaugConfig: + path = Path(path) + return cls(json.loads(path.read_text()), source=path.name) + + # -- access ------------------------------------------------------------------ + + def section(self, backend: Backend) -> dict[str, Any]: + """The augmentation blocks for a backend. + + A deepcopy, because `load_config` caches documents and a caller that mutated + what it got back would poison every later read of the same file. + """ + return copy.deepcopy(self.payload.get(backend.value, {})) + + def pipeline_options(self, name: str) -> dict[str, Any]: + """Options for a named pipeline feature, e.g. `random_choose`. + + Always returns a dict. The old builder did `config.get("RandomChooseXTransforms")` + and then `.get()` on the result, which raised AttributeError on every config + that omitted the block. + """ + return copy.deepcopy(self.payload.get("pipeline", {}).get(name, {})) + + def pipeline_mode(self) -> PipelineMode: + """How the GPU pipeline arranges its transforms. + + This used to be encoded in the *trainer class* -- a separate subclass per + arrangement -- which meant the choice was baked into the name of every run + directory and could not be varied without a new class. It is a property of + the augmentation setup, so it belongs in the config next to it. + """ + raw = self.payload.get("pipeline", {}).get("mode") + return PipelineMode(raw) if raw else PipelineMode.SEQUENTIAL + + def names(self, backend: Backend) -> list[str]: + return [k for k in self.section(backend) if not k.startswith("_")] + + # -- validation -------------------------------------------------------------- + + def validate(self) -> None: + problems: list[str] = [] + + known_sections = {b.value for b in Backend} | set(RESERVED_SECTIONS) + for key in self.payload: + if key.startswith("_") or key in known_sections: + continue + problems.append( + f"unknown top-level key {key!r}. Expected one of " + f"{', '.join(sorted(known_sections))}, or a '_'-prefixed comment. " + f"A flat config without a backend section is no longer accepted -- {MIGRATE_HINT}" + ) + + if not any(b.value in self.payload for b in Backend): + problems.append(f"no GPU or CPU section; this looks like a pre-registry config. {MIGRATE_HINT}") + + problems.extend(self._pipeline_problems()) + + for backend in Backend: + section = self.payload.get(backend.value) + if section is None: + continue + if not isinstance(section, dict): + problems.append(f"{backend.value}: expected an object of augmentation blocks") + continue + problems.extend(f"{backend.value}.{p}" for p in validate_section(section, backend, source=self.source)) + + if problems: + raise InvalidConfigError(self.source, problems) + + def _pipeline_problems(self) -> list[str]: + """Check the `pipeline` section, which was previously accepted unchecked.""" + pipeline = self.payload.get("pipeline") + if pipeline is None: + return [] + if not isinstance(pipeline, dict): + return ["pipeline: expected an object"] + + problems = [] + for key in pipeline: + if key.startswith("_") or key in PIPELINE_KEYS: + continue + close = difflib.get_close_matches(key, PIPELINE_KEYS, n=2, cutoff=0.6) + hint = f" Did you mean: {', '.join(close)}?" if close else "" + problems.append(f"pipeline: unknown key {key!r}. Accepted: {', '.join(PIPELINE_KEYS)}.{hint}") + + mode = pipeline.get("mode") + if mode is not None: + valid = [m.value for m in PipelineMode] + if mode not in valid: + close = difflib.get_close_matches(str(mode), valid, n=2, cutoff=0.5) + hint = f" Did you mean: {', '.join(close)}?" if close else "" + problems.append(f"pipeline.mode: unknown mode {mode!r}. Accepted: {', '.join(valid)}.{hint}") + return problems + + +@functools.lru_cache(maxsize=8) +def load_config(path: str) -> SmaugConfig: + """Parse and validate a config, once per path. + + Cached because the nnU-Net trainer reads the same file twice: its + `get_training_transforms` is a staticmethod (nnU-Net's contract), so it cannot + reach the instance's already-parsed config and has to open the file itself. + `SmaugConfig.section` hands out copies, so sharing the parsed document is safe. + """ + return SmaugConfig.from_path(path) + + +def validate_file(path: str | Path) -> list[str]: + """Every problem in a config file, without raising. Empty means it is valid.""" + try: + SmaugConfig.from_path(path) + except InvalidConfigError as exc: + return exc.problems + except json.JSONDecodeError as exc: + return [f"not valid JSON: {exc}"] + return [] + + +def config_hash(payload: dict, algo: str = "sha256") -> str: + """Content-addressed identity for a config. + + Byte-for-byte the same canonicalisation segtransferaug has always used, because + experiment directories are named `...-aug--c-` and + changing it would orphan every existing run folder. + """ + import hashlib + + canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + digest = hashlib.new(algo) + digest.update(canonical) + return digest.hexdigest() + + +def file_hash(path: str | Path, algo: str = "sha256") -> str: + """Hash a source file's text. + + Used downstream to name experiment directories after the implementation that + produced them, so a change to the transforms is visible in the run name. + """ + import hashlib + + digest = hashlib.new(algo) + digest.update(Path(path).read_text().encode("utf-8")) + return digest.hexdigest() + + +def registered_names(backend: Backend) -> list[str]: + """Convenience re-export so callers need not import the registry directly.""" + return registry.names(backend) + + +# --- config manipulation ---------------------------------------------------------- +# +# Upstreamed from segtransferaug/utils/smauglab_config.py, which drove the sweep +# scripts. Three module-level absolute paths went away (the packaged config is +# resolved through importlib.resources now), and the hardcoded AUG2GROUP table +# became the registry's `group` field, so a new augmentation no longer has to be +# added to a dict in a different repository before the sweeps can see it. + + +def default_config_path() -> Path: + """The packaged default GPU config.""" + import importlib.resources + + from smauglab import configs + + return Path(str(importlib.resources.files(configs))) / "transform_params_gpu.json" + + +def load_json(path: str | Path) -> dict: + return json.loads(Path(path).read_text()) + + +def transform_names(payload: dict, backend: Backend = Backend.GPU) -> list[str]: + """The augmentations a config actually names.""" + return [k for k in payload.get(backend.value, {}) if not k.startswith("_")] + + +def single_transform_config(name: str, payload: dict, backend: Backend = Backend.GPU) -> dict: + """A copy of the config with only one augmentation left enabled.""" + registry.get(name, backend) # raises with a suggestion if the name is wrong + out = copy.deepcopy(payload) + section = out.get(backend.value, {}) + out[backend.value] = {k: v for k, v in section.items() if k == name or k.startswith("_")} + return out + + +def drop_zero_probability(payload: dict, backend: Backend = Backend.GPU) -> dict: + """Remove augmentations that would never fire, so the config says what it does.""" + out = copy.deepcopy(payload) + section = out.get(backend.value, {}) + out[backend.value] = {k: v for k, v in section.items() if k.startswith("_") or v.get("p", 1.0) != 0} + return out + + +def filter_by_group(payload: dict, group, backend: Backend = Backend.GPU) -> dict: + """Keep only the augmentations in one GEO/GE/TA group.""" + keep = set(registry.names(backend, group=group)) + out = copy.deepcopy(payload) + section = out.get(backend.value, {}) + out[backend.value] = {k: v for k, v in section.items() if k in keep or k.startswith("_")} + return out + + +def set_probabilities(payload: dict, p: float, group=None, backend: Backend = Backend.GPU) -> dict: + """Set `p` on every augmentation, or only on one group.""" + targets = set(registry.names(backend, group=group)) if group is not None else None + out = copy.deepcopy(payload) + for name, block in out.get(backend.value, {}).items(): + if name.startswith("_") or not isinstance(block, dict): + continue + if targets is None or name in targets: + block["p"] = p + return out + + +def write_temp_config(payload: dict, directory: str | Path | None = None) -> str: + """Materialise a config so it can be handed to a subprocess by path. + + Named after its content hash, so the same config reuses the same file and a + sweep does not fill the directory with near-duplicates. + """ + import tempfile + + target_dir = Path(directory) if directory else Path(tempfile.gettempdir()) / "smauglab_configs" + target_dir.mkdir(parents=True, exist_ok=True) + path = target_dir / f"transform_params_{config_hash(payload)[:8]}.json" + path.write_text(json.dumps(payload, indent=4) + "\n") + return str(path) + + +def remove_temp_configs(directory: str | Path | None = None) -> int: + """Delete configs written by `write_temp_config`. Returns how many went.""" + import tempfile + + target_dir = Path(directory) if directory else Path(tempfile.gettempdir()) / "smauglab_configs" + if not target_dir.is_dir(): + return 0 + removed = 0 + for path in target_dir.glob("transform_params_*.json"): + path.unlink() + removed += 1 + return removed diff --git a/smauglab/configs/all_augmentations.json b/smauglab/configs/all_augmentations.json new file mode 100644 index 0000000..8dae29f --- /dev/null +++ b/smauglab/configs/all_augmentations.json @@ -0,0 +1,669 @@ +{ + "GPU": { + "RandomFlipTransformGPU": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 1.0, + "p_batch": 1.0, + "same_on_batch": false + }, + "RandomAffineGPU": { + "align_corners": true, + "degrees": 10, + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "resample": "BILINEAR", + "same_on_batch": false, + "scale": [ + 0.9, + 1.1 + ], + "shears": [ + -10, + 10, + -10, + 10, + -10, + 10 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + }, + "RandomSynthSegGPU": { + "apply_bias_field": true, + "apply_intensity_augmentation": true, + "apply_resolution": true, + "apply_to_channel": null, + "atlas_res": 1.0, + "bias_field_std": 0.7, + "bias_scale": 0.025, + "blur_range": 1.03, + "clip": 300.0, + "data_res": null, + "em_background_clusters_range": [ + 3, + 10 + ], + "em_background_label": 0, + "em_label_completion": false, + "em_max_fit_voxels": 100000, + "em_n_foreground_clusters": 2, + "em_n_iters": 20, + "em_same_on_batch": false, + "flip_axis": 2, + "gamma_std": 0.5, + "generation_classes": null, + "generation_labels": null, + "keepdim": true, + "max_res_aniso": 8.0, + "max_res_iso": 4.0, + "n_channels": 1, + "n_neutral_labels": null, + "nonlin_scale": 0.04, + "nonlin_std": 4.0, + "normalise": true, + "output_labels": null, + "p": 0.5, + "p_batch": 1.0, + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + "randomise_res": true, + "rotation_bounds": 15.0, + "same_on_batch": false, + "scaling_bounds": 0.2, + "shearing_bounds": 0.012, + "svf_integration_steps": 7, + "thickness": null, + "translation_bounds": false + }, + "RandomPaletteGPU": { + "alpha_magnitude_range": [ + 0.5, + 2.0 + ], + "blur_sigmas": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "c_choices": [ + 2, + 3, + 4, + 5, + 6 + ], + "dark_threshold": 0.01, + "keepdim": false, + "label_classes": null, + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "n_kmeans_subsample": 10000, + "p": 1.0, + "p_batch": 1.0, + "s_choices": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "same_on_batch": false, + "skip_parcellation_prob": 0.1, + "skip_sub_parc_prob": 0.4 + }, + "RandomDomainTransferGPU": { + "any_source": false, + "apply_to_channel": [ + 0 + ], + "bank_path": null, + "bias_field_std": 0.0, + "bias_scale": 0.03, + "blend_concentration": 1.0, + "blend_targets": 1, + "include_self": false, + "keepdim": true, + "p": 0.2, + "p_batch": 1.0, + "p_class_mix": 0.0, + "p_spatial_mix": 0.0, + "pct": 1.0, + "same_on_batch": false, + "sigma": 2.0, + "source_label": null, + "spatial_mix_gain": 3.0, + "spatial_mix_scale": 0.03, + "targets": null, + "zscore_io": "auto" + }, + "RandomInverseGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomHistogramEqualizationGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomRedistributeSegGPU": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.2, + "keepdim": true, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + }, + "RandomScharrGPU": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + }, + "RandomUnsharpMaskGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + }, + "RandomRandConvGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 1, + 3, + 5, + 7 + ], + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomLaplaceGPU": { + "absolute": false, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomClampGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.0, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomGaussianNoiseGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + }, + "RandomGaussianBlurGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + }, + "RandomBrightnessGPU": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.5, + 1.5 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "same_on_batch": false + }, + "RandomGammaGPU": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomInvGammaGPU": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomContrastGPU": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomLog1pGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomSqrtGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomSinGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomExpGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomSigmoidGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "RandomLowResTransformGPU": { + "keepdim": true, + "p": 1.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.3, + 1.0 + ] + }, + "RandomAcqTransformGPU": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 1.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.3, + 1.0 + ] + }, + "RandomCropTransformGPU": { + "crop": [ + 1.0, + 1.0 + ], + "keepdim": true, + "p": 1.0, + "p_batch": 1.0, + "pos": [ + 0.0, + 1.0 + ], + "same_on_batch": false + }, + "RandomBiasFieldGPU": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.5, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": false, + "order": 3, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + }, + "ZscoreNormalizationGPU": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 1.0, + "p_batch": 1.0 + } + }, + "CPU": { + "LaplaceConvTransform": { + "absolute": false, + "p": 1.0, + "retain_stats": false + }, + "ScharrConvTransform": { + "absolute": true, + "p": 1.0, + "retain_stats": false + }, + "Log1pTransform": { + "p": 1.0, + "retain_stats": false + }, + "SqrtTransform": { + "p": 1.0, + "retain_stats": false + }, + "SinTransform": { + "p": 1.0, + "retain_stats": false + }, + "ExpTransform": { + "p": 1.0, + "retain_stats": false + }, + "SigmoidTransform": { + "p": 1.0, + "retain_stats": false + }, + "HistogramEqualTransform": { + "p": 1.0, + "retain_stats": false + }, + "RedistributeTransform": { + "classes": null, + "in_seg": 0.2, + "p": 1.0, + "retain_stats": false + }, + "ShapeTransform": { + "ignore_axes": [], + "p": 1.0, + "shape_min": 1 + }, + "ArtifactTransform": { + "bias_field": false, + "blur": false, + "ghosting": false, + "motion": false, + "noise": false, + "p": 1.0, + "random_pick": false, + "spike": false, + "swap": false + }, + "SpatialCustomTransform": { + "affine": false, + "anisotropy": false, + "elastic": false, + "flip": false, + "p": 1.0, + "random_pick": false + }, + "SpatialTransform": { + "align_corners": false, + "bg_style_seg_sampling": true, + "border_mode_seg": "zeros", + "center_deformation": true, + "elastic_deform_magnitude": [ + 0, + 0.2 + ], + "elastic_deform_scale": [ + 0, + 0.2 + ], + "mode_image": "bilinear", + "mode_seg": "bilinear", + "p_elastic_deform": 0, + "p_rot_per_axis": 1, + "p_rotation": 0, + "p_scaling": 0, + "p_synchronize_def_scale_across_axes": 0, + "p_synchronize_scaling_across_axes": 0, + "padding_mode_image": "zeros", + "padding_value_image": 0, + "padding_value_seg": 0, + "patch_center_dist_from_border": null, + "random_crop": null, + "scaling": [ + 0.7, + 1.3 + ] + }, + "GaussianNoiseTransform": { + "noise_variance": [ + 0, + 0.1 + ], + "p": 1.0, + "p_per_channel": 1.0, + "synchronize_channels": false + }, + "GaussianBlurTransform": { + "benchmark": false, + "blur_sigma": [ + 1, + 5 + ], + "p": 1.0, + "p_per_channel": 1, + "synchronize_axes": false, + "synchronize_channels": false + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": null, + "p": 1.0, + "p_per_channel": 1, + "synchronize_channels": null + }, + "ContrastTransform": { + "contrast_range": null, + "p": 1.0, + "p_per_channel": 1.0, + "preserve_range": null, + "synchronize_channels": null + }, + "SimulateLowResolutionTransform": { + "allowed_channels": null, + "ignore_axes": null, + "p": 1.0, + "p_per_channel": 1, + "scale": null, + "synchronize_axes": null, + "synchronize_channels": null + }, + "InvertedGammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "p": 1.0, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + }, + "GammaTransform": { + "gamma": null, + "p": 1.0, + "p_invert_image": null, + "p_per_channel": null, + "p_retain_stats": null, + "synchronize_channels": null + }, + "MirrorTransform": { + "allowed_axes": null + }, + "ZscoreNormalization": { + "p": 1.0 + } + } +} diff --git a/smauglab/configs/transform_params.json b/smauglab/configs/transform_params.json index 1c3b07b..0326f21 100644 --- a/smauglab/configs/transform_params.json +++ b/smauglab/configs/transform_params.json @@ -1,98 +1,158 @@ { - "retain_stats": true, - "mirror_axes": [0,1,2], - "ArtifactTransform": { - "motion": false, - "ghosting": false, - "spike": false, - "bias_field": false, - "blur": false, - "noise": false, - "swap": false, - "random_pick": false, - "probability": 0.7 - }, - "SpatialCustomTransform": { - "flip": false, - "affine": false, - "elastic": false, - "anisotropy": false, - "random_pick": false, - "probability": 0.6 - }, - "ConvTransform": { - "kernel_type": "Laplace", - "absolute": false, - "retain_stats": false, - "probability": 0.15 - }, - "RedistributeTransform": { - "classes": null, - "in_seg": 0.2, - "retain_stats": false, - "probability": 0.5 - }, - "ShapeTransform": { - "shape_min": 1, - "ignore_axes": [1,2], - "probability": 0.4 - }, - "HistogramEqualTransform": { - "probability": 0.1 - }, - "FunctionTransform": { - "probability": 0.05 - }, - "GaussianNoiseTransform": { - "noise_variance": [0, 0.1], - "p_per_channel": 1, - "synchronize_channels": true, - "probability": 0.1 - }, - "GaussianBlurTransform": { - "blur_sigma": [0.5, 1.0], - "synchronize_channels": false, - "synchronize_axes": false, - "p_per_channel": 0.5, - "benchmark": true, - "probability": 0.2 - }, - "MultiplicativeBrightnessTransform": { - "multiplier_range": [0.75, 1.25], - "synchronize_channels": false, - "p_per_channel": 1, - "probability": 0.15 - }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], - "preserve_range": true, - "synchronize_channels": false, - "p_per_channel": 1, - "probability": 0.15 - }, - "SimulateLowResolutionTransform": { - "scale": [0.3, 1], - "synchronize_channels": true, - "synchronize_axes": false, - "ignore_axes": [], - "allowed_channels": null, - "p_per_channel": 0.5, - "probability": 0.2 - }, - "GammaTransform_invert": { - "gamma": [0.7, 1.5], - "p_invert_image": 1, - "synchronize_channels": false, - "p_per_channel": 1, - "p_retain_stats": 1, - "probability": 0.1 - }, - "GammaTransform": { - "gamma": [0.7, 1.5], - "p_invert_image": 0, - "synchronize_channels": false, - "p_per_channel": 1, - "p_retain_stats": 1, - "probability": 0.3 - } + "CPU": { + "LaplaceConvTransform": { + "absolute": false, + "retain_stats": true, + "p": 0.15 + }, + "Log1pTransform": { + "p": 0.05, + "retain_stats": true + }, + "SqrtTransform": { + "p": 0.05, + "retain_stats": true + }, + "SinTransform": { + "p": 0.05, + "retain_stats": true + }, + "ExpTransform": { + "p": 0.05, + "retain_stats": true + }, + "SigmoidTransform": { + "p": 0.05, + "retain_stats": true + }, + "HistogramEqualTransform": { + "p": 0.1, + "retain_stats": true + }, + "RedistributeTransform": { + "in_seg": 0.2, + "retain_stats": true, + "p": 0.5 + }, + "ShapeTransform": { + "shape_min": 1, + "ignore_axes": [ + 1, + 2 + ], + "p": 0.4 + }, + "ArtifactTransform": { + "motion": false, + "ghosting": false, + "spike": false, + "bias_field": false, + "blur": false, + "noise": false, + "swap": false, + "random_pick": false, + "p": 0.7 + }, + "SpatialCustomTransform": { + "flip": false, + "affine": false, + "elastic": false, + "anisotropy": false, + "random_pick": false, + "p": 0.6 + }, + "GaussianNoiseTransform": { + "noise_variance": [ + 0, + 0.1 + ], + "p_per_channel": 1, + "synchronize_channels": true, + "p": 0.1 + }, + "GaussianBlurTransform": { + "blur_sigma": [ + 0.5, + 1.0 + ], + "synchronize_channels": false, + "synchronize_axes": false, + "p_per_channel": 0.5, + "benchmark": true, + "p": 0.2 + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": [ + 0.75, + 1.25 + ], + "synchronize_channels": false, + "p_per_channel": 1, + "p": 0.15 + }, + "ContrastTransform": { + "contrast_range": [ + 0.75, + 1.25 + ], + "preserve_range": true, + "synchronize_channels": false, + "p_per_channel": 1, + "p": 0.15 + }, + "SimulateLowResolutionTransform": { + "scale": [ + 0.3, + 1 + ], + "synchronize_channels": true, + "synchronize_axes": false, + "ignore_axes": [], + "allowed_channels": null, + "p_per_channel": 0.5, + "p": 0.2 + }, + "InvertedGammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "p": 0.1 + }, + "GammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "p_invert_image": 0, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "p": 0.3 + }, + "MirrorTransform": { + "allowed_axes": [ + 0, + 1, + 2 + ] + }, + "SpatialTransform": { + "patch_center_dist_from_border": 0, + "random_crop": false, + "p_elastic_deform": 0, + "p_rotation": 0, + "p_scaling": 0, + "scaling": [ + 0.7, + 1.4 + ], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false, + "mode_seg": "nearest" + } + } } diff --git a/smauglab/configs/transform_params_gpu.json b/smauglab/configs/transform_params_gpu.json index 397eb91..05e170b 100644 --- a/smauglab/configs/transform_params_gpu.json +++ b/smauglab/configs/transform_params_gpu.json @@ -1,209 +1,282 @@ { - "RandomPALETTETransform": { - "probability": 0.25, - "c_choices": [ - 2, - 3, - 4, - 5, - 6 - ], - "s_choices": [ - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10 - ], - "blur_sigmas": [ - 0.0, - 0.0, - 0.0, - 0.3, - 0.5, - 0.8 - ], - "dark_threshold": 0.01, - "n_kmeans_subsample": 10000, - "skip_parcellation_prob": 0.1, - "skip_sub_parc_prob": 0.4, - "alpha_magnitude_range": [ - 0.5, - 2.0 - ], - "label_remap_prob": 0.5, - "min_label_voxels": 4, - "label_classes": null + "GPU": { + "RandomFlipTransformGPU": { + "flip_axis": [ + 0 + ], + "same_on_batch": false, + "keepdim": true, + "p": 0.5 + }, + "RandomAffineGPU": { + "degrees": 5, + "translate": [ + 0.1, + 0.1, + 0.1 + ], + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "resample": "bilinear", + "p": 0.0 + }, + "RandomPaletteGPU": { + "p": 0.25, + "c_choices": [ + 2, + 3, + 4, + 5, + 6 + ], + "s_choices": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "blur_sigmas": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "dark_threshold": 0.01, + "n_kmeans_subsample": 10000, + "skip_parcellation_prob": 0.1, + "skip_sub_parc_prob": 0.4, + "alpha_magnitude_range": [ + 0.5, + 2.0 + ], + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "label_classes": null + }, + "RandomInverseGPU": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.0, + "p": 0.25 + }, + "RandomHistogramEqualizationGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.6, + "p": 0.25 + }, + "RandomRedistributeSegGPU": { + "in_seg": 0.25, + "retain_stats": true, + "p": 0.4 + }, + "RandomScharrGPU": { + "absolute": true, + "retain_stats": true, + "mix_prob": 0.5, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.25 + }, + "RandomUnsharpMaskGPU": { + "sigma": 1.0, + "unsharp_amount": 1.5, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.5, + "p": 0.25 + }, + "RandomRandConvGPU": { + "kernel_sizes": [ + 3, + 5, + 7 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.5, + "p": 0.2 + }, + "RandomClampGPU": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.0 + }, + "RandomGaussianNoiseGPU": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.1 + }, + "RandomGaussianBlurGPU": { + "sigma": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.2 + }, + "RandomBrightnessGPU": { + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.15 + }, + "RandomGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.3 + }, + "RandomInvGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.1 + }, + "RandomContrastGPU": { + "contrast_range": [ + 0.75, + 1.25 + ], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.15 + }, + "RandomLog1pGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomSqrtGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomSinGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomExpGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomSigmoidGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomLowResTransformGPU": { + "scale": [ + 0.5, + 1.0 + ], + "same_on_batch": false, + "p": 0.25 + }, + "RandomAcqTransformGPU": { + "scale": [ + 0.1, + 1.0 + ], + "same_on_batch": false, + "p": 0.2 + }, + "RandomCropTransformGPU": { + "crop": [ + 0.5, + 1.0 + ], + "pos": [ + 0.0, + 1.0 + ], + "same_on_batch": false, + "p": 0.25 + }, + "RandomBiasFieldGPU": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.15 + }, + "ZscoreNormalizationGPU": { + "p": 0.0 + } }, - "ScharrTransform": { - "kernel_type": "Scharr", - "absolute": true, - "retain_stats": true, - "mix_prob": 0.50, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.25 - }, - "GaussianBlurTransform": { - "kernel_type": "GaussianBlur", - "sigma": 1.0, - "retain_stats": false, - "mix_prob": 0.00, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.20 - }, - "UnsharpMaskTransform": { - "kernel_type": "UnsharpMask", - "sigma": 1.0, - "unsharp_amount": 1.5, - "retain_stats": false, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "mix_prob": 0.50, - "probability": 0.25 - }, - "RandomConvTransform": { - "kernel_type": "RandConv", - "kernel_sizes": [3,5,7], - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "mix_prob": 0.50, - "probability": 0.20 - }, - "RedistributeSegTransform": { - "in_seg": 0.25, - "retain_stats": true, - "probability": 0.4 - }, - "GaussianNoiseTransform": { - "mean": 0.0, - "std": 1.0, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.1 - }, - "ClampTransform": { - "max_clamp_amount": 0.2, - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.00 - }, - "BrightnessTransform": { - "brightness_range": [0.75, 1.25], - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.15 - }, - "GammaTransform": { - "gamma_range": [0.7, 1.5], - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.30 - }, - "InvGammaTransform": { - "gamma_range": [0.7, 1.5], - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.10 - }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], - "retain_stats": false, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.15 - }, - "FunctionTransform": { - "retain_stats": true, - "in_seg": 0, - "out_seg": 0, - "mix_in_out": false, - "probability": 0.1 - }, - "InverseTransform": { - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "mix_prob": 0.00, - "probability": 0.25 - }, - "HistogramEqualizationTransform": { - "retain_stats": true, - "in_seg": 0, - "out_seg": 0, - "mix_in_out": true, - "mix_prob": 0.60, - "probability": 0.25 - }, - "SimulateLowResTransform": { - "scale": [0.5, 1.0], - "same_on_batch": false, - "probability": 0.25 - }, - "AcqTransform": { - "scale": [0.1, 1.0], - "same_on_batch": false, - "probability": 0.2 - }, - "CropTransform": { - "crop": [0.5, 1.0], - "pos": [0.0, 1.0], - "same_on_batch": false, - "probability": 0.25 - }, - "BiasFieldTransform": { - "retain_stats": true, - "coefficients": 0.2, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.15 - }, - "FlipTransform": { - "flip_axis": [0], - "same_on_batch": false, - "keepdim": true, - "probability": 0.5 - }, - "AffineTransform": { - "degrees": 5, - "translate": [0.1, 0.1, 0.1], - "scale": [0.7, 1.4], - "shear": [-5, 5, -5, 5, -5, 5], - "resample": "bilinear", - "probability": 0.0 - }, - "nnUNetSpatialTransform": { - "patch_center_dist_from_border": 80, - "random_crop": false, - "p_elastic_deform": 0.0, - "p_rotation": 0.2, - "p_scaling": 0.2, - "scaling": [0.7, 1.4], - "p_synchronize_scaling_across_axes": 1, - "bg_style_seg_sampling": false - }, - "ZscoreNormalizationTransform": { - "probability": 0.00 + "CPU": { + "SpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [ + 0.7, + 1.4 + ], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false, + "mode_seg": "nearest" + } } } diff --git a/smauglab/configs/transform_params_hybrid.json b/smauglab/configs/transform_params_hybrid.json index d27cce8..14175bf 100644 --- a/smauglab/configs/transform_params_hybrid.json +++ b/smauglab/configs/transform_params_hybrid.json @@ -1,262 +1,362 @@ { - "CPU": { - "Comment1": "AugLab custom CPU augmentations", - "ConvTransform": { - "kernel_type": "Scharr", - "absolute": true, - "retain_stats": false, - "probability": 0.15 + "GPU": { + "_comment1": "AugLab custom GPU augmentations", + "RandomFlipTransformGPU": { + "flip_axis": [ + 0 + ], + "same_on_batch": false, + "keepdim": true, + "p": 0 }, - "FunctionTransform": { + "RandomAffineGPU": { + "degrees": 10, + "translate": [ + 0.1, + 0.1, + 0.1 + ], + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -10, + 10, + -10, + 10, + -10, + 10 + ], + "resample": "bilinear", + "p": 0 + }, + "RandomInverseGPU": { "retain_stats": false, - "probability": 0.05 + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.3 }, - "HistogramEqualTransform": { + "RandomHistogramEqualizationGPU": { "retain_stats": false, - "probability": 0.1 + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.2 }, - "RedistributeTransform": { + "RandomRedistributeSegGPU": { "in_seg": 0.2, "retain_stats": false, - "probability": 0.5 - }, - "ShapeTransform": { - "shape_min": 1, - "ignore_axes": [1,2], - "probability": 0.4 - }, - "ArtifactTransform":{ - "motion": true, - "ghosting": true, - "spike": true, - "bias_field": true, - "blur": true, - "noise": true, - "swap": false, - "random_pick": true, - "probability": 0 + "p": 0.3 }, - "SpatialCustomTransform": { - "flip": true, - "affine": true, - "elastic": true, - "anisotropy": true, - "random_pick": true, - "probability": 0 - }, - - "Comment2": "nnUNet default CPU augmentations", - "SpatialTransform": { - "patch_center_dist_from_border": 0, - "random_crop": false, - "p_elastic_deform": 0, - "p_rotation": 0.2, - "p_scaling": 0.2, - "scaling": [0.7, 1.4], - "p_synchronize_scaling_across_axes": 1, - "bg_style_seg_sampling": false - }, - "GaussianNoiseTransform": { - "noise_variance": [0, 0.1], - "p_per_channel": 1, - "synchronize_channels": true, - "probability": 0.1 - }, - "GaussianBlurTransform": { - "blur_sigma": [0.5, 1.0], - "synchronize_channels": false, - "synchronize_axes": false, - "p_per_channel": 0.5, - "benchmark": true, - "probability": 0.2 - }, - "MultiplicativeBrightnessTransform": { - "multiplier_range": [0.75, 1.25], - "synchronize_channels": false, - "p_per_channel": 1, - "probability": 0.15 - }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], - "preserve_range": true, - "synchronize_channels": false, - "p_per_channel": 1, - "probability": 0.15 - }, - "SimulateLowResolutionTransform": { - "scale": [0.3, 1], - "synchronize_channels": true, - "synchronize_axes": false, - "ignore_axes": [], - "p_per_channel": 0.5, - "probability": 0.2 - }, - "GammaTransform_invert": { - "gamma": [0.7, 1.5], - "p_invert_image": 1, - "synchronize_channels": false, - "p_per_channel": 1, - "p_retain_stats": 1, - "probability": 0.1 - }, - "GammaTransform": { - "gamma": [0.7, 1.5], - "p_invert_image": 0, - "synchronize_channels": false, - "p_per_channel": 1, - "p_retain_stats": 1, - "probability": 0.3 - } - }, - "GPU": { - "Comment1": "AugLab custom GPU augmentations", - "ScharrTransform": { - "kernel_type": "Scharr", + "RandomScharrGPU": { "absolute": true, "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.15 + "p": 0.15 }, - "GaussianBlurTransform": { - "kernel_type": "GaussianBlur", + "RandomUnsharpMaskGPU": { "sigma": 1.0, - "retain_stats": false, + "unsharp_amount": 1, "in_seg": 0.5, "out_seg": 0.5, - "mix_in_out": false, - "probability": 0.10 - }, - "UnsharpMaskTransform": { - "kernel_type": "UnsharpMask", - "sigma": 1.0, - "unsharp_amount": 1, + "mix_in_out": true, + "p": 0.1 + }, + "RandomRandConvGPU": { + "kernel_sizes": [ + 1, + 3, + 5, + 7 + ], "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.10 + "mix_prob": 0.2, + "p": 0.1 }, - "RandomConvTransform": { - "kernel_type": "RandConv", - "kernel_sizes": [1,3,5,7], + "RandomClampGPU": { + "max_clamp_amount": 0.2, "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "mix_prob": 0.20, - "probability": 0.10 - }, - "RedistributeSegTransform": { - "in_seg": 0.2, - "retain_stats": false, - "probability": 0.3 + "p": 0.4 }, - "GaussianNoiseTransform": { + "RandomGaussianNoiseGPU": { "mean": 0.0, "std": 0.1, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.10 + "p": 0.1 }, - "ClampTransform": { - "max_clamp_amount": 0.2, - "retain_stats": false, + "RandomGaussianBlurGPU": { + "sigma": 1.0, "in_seg": 0.5, "out_seg": 0.5, - "mix_in_out": true, - "probability": 0.40 + "mix_in_out": false, + "p": 0.1 }, - "BrightnessTransform": { - "brightness_range": [0.75, 1.25], + "RandomBrightnessGPU": { + "brightness_range": [ + 0.75, + 1.25 + ], "in_seg": 0.0, "out_seg": 0.0, "mix_in_out": false, - "probability": 0.15 + "p": 0.15 }, - "GammaTransform": { - "gamma_range": [0.7, 1.5], + "RandomGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.30 + "p": 0.3 }, - "InvGammaTransform": { - "gamma_range": [0.7, 1.5], + "RandomInvGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.10 + "p": 0.1 }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], + "RandomContrastGPU": { + "contrast_range": [ + 0.75, + 1.25 + ], "retain_stats": false, "in_seg": 0.0, "out_seg": 0.0, "mix_in_out": false, - "probability": 0.15 + "p": 0.15 + }, + "RandomLog1pGPU": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.05 }, - "FunctionTransform": { + "RandomSqrtGPU": { "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.05 + "p": 0.05 }, - "InverseTransform": { + "RandomSinGPU": { "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.30 + "p": 0.05 }, - "HistogramEqualizationTransform": { + "RandomExpGPU": { "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.20 + "p": 0.05 }, - "SimulateLowResTransform": { - "scale": [0.5, 1.0], - "crop": [0.8, 1.0], + "RandomSigmoidGPU": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.05 + }, + "RandomLowResTransformGPU": { + "scale": [ + 0.5, + 1.0 + ], "same_on_batch": false, - "probability": 0.25 + "p": 0.25 }, - "AcqTransform": { - "scale": [0.2, 1.0], - "crop": [1.0, 1.0], + "RandomAcqTransformGPU": { + "scale": [ + 0.2, + 1.0 + ], "same_on_batch": false, - "probability": 0.40 + "p": 0.4 }, - "BiasFieldTransform": { + "RandomBiasFieldGPU": { "retain_stats": false, "coefficients": 0.5, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": false, - "probability": 0.20 + "p": 0.2 }, - "FlipTransform": { - "flip_axis": [0], - "same_on_batch": false, - "keepdim": true, - "probability": 0 + "ZscoreNormalizationGPU": { + "p": 0.3 + } + }, + "CPU": { + "_comment1": "AugLab custom CPU augmentations", + "_comment2": "nnUNet default CPU augmentations", + "ScharrConvTransform": { + "absolute": true, + "retain_stats": false, + "p": 0.15 }, - "AffineTransform": { - "degrees": 10, - "translate": [0.1, 0.1, 0.1], - "scale": [0.7, 1.4], - "shear": [-10, 10, -10, 10, -10, 10], - "resample": "bilinear", - "probability": 0 + "Log1pTransform": { + "retain_stats": false, + "p": 0.05 + }, + "SqrtTransform": { + "retain_stats": false, + "p": 0.05 + }, + "SinTransform": { + "retain_stats": false, + "p": 0.05 + }, + "ExpTransform": { + "retain_stats": false, + "p": 0.05 + }, + "SigmoidTransform": { + "retain_stats": false, + "p": 0.05 + }, + "HistogramEqualTransform": { + "retain_stats": false, + "p": 0.1 + }, + "RedistributeTransform": { + "in_seg": 0.2, + "retain_stats": false, + "p": 0.5 + }, + "ShapeTransform": { + "shape_min": 1, + "ignore_axes": [ + 1, + 2 + ], + "p": 0.4 + }, + "ArtifactTransform": { + "motion": true, + "ghosting": true, + "spike": true, + "bias_field": true, + "blur": true, + "noise": true, + "swap": false, + "random_pick": true, + "p": 0 }, - "ZscoreNormalizationTransform": { - "probability": 0.3 + "SpatialCustomTransform": { + "flip": true, + "affine": true, + "elastic": true, + "anisotropy": true, + "random_pick": true, + "p": 0 + }, + "SpatialTransform": { + "patch_center_dist_from_border": 0, + "random_crop": false, + "p_elastic_deform": 0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [ + 0.7, + 1.4 + ], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false, + "mode_seg": "nearest" + }, + "GaussianNoiseTransform": { + "noise_variance": [ + 0, + 0.1 + ], + "p_per_channel": 1, + "synchronize_channels": true, + "p": 0.1 + }, + "GaussianBlurTransform": { + "blur_sigma": [ + 0.5, + 1.0 + ], + "synchronize_channels": false, + "synchronize_axes": false, + "p_per_channel": 0.5, + "benchmark": true, + "p": 0.2 + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": [ + 0.75, + 1.25 + ], + "synchronize_channels": false, + "p_per_channel": 1, + "p": 0.15 + }, + "ContrastTransform": { + "contrast_range": [ + 0.75, + 1.25 + ], + "preserve_range": true, + "synchronize_channels": false, + "p_per_channel": 1, + "p": 0.15 + }, + "SimulateLowResolutionTransform": { + "scale": [ + 0.3, + 1 + ], + "synchronize_channels": true, + "synchronize_axes": false, + "ignore_axes": [], + "p_per_channel": 0.5, + "p": 0.2 + }, + "InvertedGammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "p": 0.1 + }, + "GammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "p_invert_image": 0, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "p": 0.3 } } } diff --git a/smauglab/configs/transform_params_hybrid_TAGE.json b/smauglab/configs/transform_params_hybrid_TAGE.json index 9382fc6..2e2e052 100644 --- a/smauglab/configs/transform_params_hybrid_TAGE.json +++ b/smauglab/configs/transform_params_hybrid_TAGE.json @@ -1,264 +1,363 @@ { - "CPU": { - "Comment1": "CPU transfer augmentations (TA)", - "ConvTransform": { - "kernel_type": "Scharr", - "absolute": true, - "retain_stats": false, - "probability": 0.15 + "GPU": { + "_comment1": "GPU transfer augmentations (TA)", + "_comment2": "GPU general enhancement (GE)", + "RandomFlipTransformGPU": { + "flip_axis": [ + 0 + ], + "same_on_batch": false, + "keepdim": true, + "p": 0 }, - "HistogramEqualTransform": { - "retain_stats": false, - "probability": 0.1 + "RandomAffineGPU": { + "degrees": 10, + "translate": [ + 0.1, + 0.1, + 0.1 + ], + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -10, + 10, + -10, + 10, + -10, + 10 + ], + "resample": "bilinear", + "p": 0 }, - "RedistributeTransform": { - "in_seg": 0.2, + "RandomInverseGPU": { "retain_stats": false, - "probability": 0.5 + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.3 }, - - "Comment2": "CPU general enhancement (GE)", - "FunctionTransform": { + "RandomHistogramEqualizationGPU": { "retain_stats": false, - "probability": 0.05 - }, - "ShapeTransform": { - "shape_min": 1, - "ignore_axes": [1,2], - "probability": 0.4 - }, - "ArtifactTransform":{ - "motion": true, - "ghosting": true, - "spike": true, - "bias_field": true, - "blur": true, - "noise": true, - "swap": false, - "random_pick": true, - "probability": 0 - }, - "SpatialCustomTransform": { - "flip": true, - "affine": true, - "elastic": true, - "anisotropy": true, - "random_pick": true, - "probability": 0 - }, - "SpatialTransform": { - "patch_center_dist_from_border": 0, - "random_crop": false, - "p_elastic_deform": 0, - "p_rotation": 0.2, - "p_scaling": 0.2, - "scaling": [0.7, 1.4], - "p_synchronize_scaling_across_axes": 1, - "bg_style_seg_sampling": false - }, - "GaussianNoiseTransform": { - "noise_variance": [0, 0.1], - "p_per_channel": 1, - "synchronize_channels": true, - "probability": 0.1 - }, - "GaussianBlurTransform": { - "blur_sigma": [0.5, 1.0], - "synchronize_channels": false, - "synchronize_axes": false, - "p_per_channel": 0.5, - "benchmark": true, - "probability": 0.2 - }, - "MultiplicativeBrightnessTransform": { - "multiplier_range": [0.75, 1.25], - "synchronize_channels": false, - "p_per_channel": 1, - "probability": 0.15 - }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], - "preserve_range": true, - "synchronize_channels": false, - "p_per_channel": 1, - "probability": 0.15 - }, - "SimulateLowResolutionTransform": { - "scale": [0.3, 1], - "synchronize_channels": true, - "synchronize_axes": false, - "ignore_axes": [], - "p_per_channel": 0.5, - "probability": 0.2 + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.2 }, - "GammaTransform_invert": { - "gamma": [0.7, 1.5], - "p_invert_image": 1, - "synchronize_channels": false, - "p_per_channel": 1, - "p_retain_stats": 1, - "probability": 0.1 + "RandomRedistributeSegGPU": { + "in_seg": 0.2, + "retain_stats": false, + "p": 0.3 }, - "GammaTransform": { - "gamma": [0.7, 1.5], - "p_invert_image": 0, - "synchronize_channels": false, - "p_per_channel": 1, - "p_retain_stats": 1, - "probability": 0.3 - } - }, - "GPU": { - "Comment1": "GPU transfer augmentations (TA)", - "ScharrTransform": { - "kernel_type": "Scharr", + "RandomScharrGPU": { "absolute": true, "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.15 + "p": 0.15 }, - "UnsharpMaskTransform": { - "kernel_type": "UnsharpMask", + "RandomUnsharpMaskGPU": { "sigma": 1.0, "unsharp_amount": 1, - "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.10 - }, - "RandomConvTransform": { - "kernel_type": "RandConv", - "kernel_sizes": [1,3,5,7], + "p": 0.1 + }, + "RandomRandConvGPU": { + "kernel_sizes": [ + 1, + 3, + 5, + 7 + ], "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "mix_prob": 0.20, - "probability": 0.10 - }, - "RedistributeSegTransform": { - "in_seg": 0.2, - "retain_stats": false, - "probability": 0.3 + "mix_prob": 0.2, + "p": 0.1 }, - "InverseTransform": { + "RandomClampGPU": { + "max_clamp_amount": 0.2, "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.30 + "p": 0.4 }, - "HistogramEqualizationTransform": { - "retain_stats": false, + "RandomGaussianNoiseGPU": { + "mean": 0.0, + "std": 0.1, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.20 + "p": 0.1 }, - - "Comment2": "GPU general enhancement (GE)", - "GaussianBlurTransform": { - "kernel_type": "GaussianBlur", + "RandomGaussianBlurGPU": { "sigma": 1.0, - "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": false, - "probability": 0.10 + "p": 0.1 }, - "GaussianNoiseTransform": { - "mean": 0.0, - "std": 0.1, + "RandomBrightnessGPU": { + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": false, + "p": 0.15 + }, + "RandomGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.10 + "p": 0.3 }, - "ClampTransform": { - "max_clamp_amount": 0.2, + "RandomInvGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.40 + "p": 0.1 }, - "BrightnessTransform": { - "brightness_range": [0.75, 1.25], + "RandomContrastGPU": { + "contrast_range": [ + 0.75, + 1.25 + ], + "retain_stats": false, "in_seg": 0.0, "out_seg": 0.0, "mix_in_out": false, - "probability": 0.15 + "p": 0.15 }, - "GammaTransform": { - "gamma_range": [0.7, 1.5], + "RandomLog1pGPU": { "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.30 + "p": 0.05 }, - "InvGammaTransform": { - "gamma_range": [0.7, 1.5], + "RandomSqrtGPU": { "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.10 + "p": 0.05 }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], + "RandomSinGPU": { "retain_stats": false, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": false, - "probability": 0.15 + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.05 + }, + "RandomExpGPU": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "p": 0.05 }, - "FunctionTransform": { + "RandomSigmoidGPU": { "retain_stats": false, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": true, - "probability": 0.05 + "p": 0.05 }, - "SimulateLowResTransform": { - "scale": [0.5, 1.0], - "crop": [0.8, 1.0], + "RandomLowResTransformGPU": { + "scale": [ + 0.5, + 1.0 + ], "same_on_batch": false, - "probability": 0.25 + "p": 0.25 }, - "AcqTransform": { - "scale": [0.2, 1.0], - "crop": [1.0, 1.0], + "RandomAcqTransformGPU": { + "scale": [ + 0.2, + 1.0 + ], "same_on_batch": false, - "probability": 0.40 + "p": 0.4 }, - "BiasFieldTransform": { + "RandomBiasFieldGPU": { "retain_stats": false, "coefficients": 0.5, "in_seg": 0.5, "out_seg": 0.5, "mix_in_out": false, - "probability": 0.20 + "p": 0.2 }, - "FlipTransform": { - "flip_axis": [0], - "same_on_batch": false, - "keepdim": true, - "probability": 0 + "ZscoreNormalizationGPU": { + "p": 0 + } + }, + "CPU": { + "_comment1": "CPU transfer augmentations (TA)", + "_comment2": "CPU general enhancement (GE)", + "ScharrConvTransform": { + "absolute": true, + "retain_stats": false, + "p": 0.15 }, - "AffineTransform": { - "degrees": 10, - "translate": [0.1, 0.1, 0.1], - "scale": [0.7, 1.4], - "shear": [-10, 10, -10, 10, -10, 10], - "resample": "bilinear", - "probability": 0 + "Log1pTransform": { + "retain_stats": false, + "p": 0.05 }, - "ZscoreNormalizationTransform": { - "probability": 0 + "SqrtTransform": { + "retain_stats": false, + "p": 0.05 + }, + "SinTransform": { + "retain_stats": false, + "p": 0.05 + }, + "ExpTransform": { + "retain_stats": false, + "p": 0.05 + }, + "SigmoidTransform": { + "retain_stats": false, + "p": 0.05 + }, + "HistogramEqualTransform": { + "retain_stats": false, + "p": 0.1 + }, + "RedistributeTransform": { + "in_seg": 0.2, + "retain_stats": false, + "p": 0.5 + }, + "ShapeTransform": { + "shape_min": 1, + "ignore_axes": [ + 1, + 2 + ], + "p": 0.4 + }, + "ArtifactTransform": { + "motion": true, + "ghosting": true, + "spike": true, + "bias_field": true, + "blur": true, + "noise": true, + "swap": false, + "random_pick": true, + "p": 0 + }, + "SpatialCustomTransform": { + "flip": true, + "affine": true, + "elastic": true, + "anisotropy": true, + "random_pick": true, + "p": 0 + }, + "SpatialTransform": { + "patch_center_dist_from_border": 0, + "random_crop": false, + "p_elastic_deform": 0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [ + 0.7, + 1.4 + ], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false, + "mode_seg": "nearest" + }, + "GaussianNoiseTransform": { + "noise_variance": [ + 0, + 0.1 + ], + "p_per_channel": 1, + "synchronize_channels": true, + "p": 0.1 + }, + "GaussianBlurTransform": { + "blur_sigma": [ + 0.5, + 1.0 + ], + "synchronize_channels": false, + "synchronize_axes": false, + "p_per_channel": 0.5, + "benchmark": true, + "p": 0.2 + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": [ + 0.75, + 1.25 + ], + "synchronize_channels": false, + "p_per_channel": 1, + "p": 0.15 + }, + "ContrastTransform": { + "contrast_range": [ + 0.75, + 1.25 + ], + "preserve_range": true, + "synchronize_channels": false, + "p_per_channel": 1, + "p": 0.15 + }, + "SimulateLowResolutionTransform": { + "scale": [ + 0.3, + 1 + ], + "synchronize_channels": true, + "synchronize_axes": false, + "ignore_axes": [], + "p_per_channel": 0.5, + "p": 0.2 + }, + "InvertedGammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "p": 0.1 + }, + "GammaTransform": { + "gamma": [ + 0.7, + 1.5 + ], + "p_invert_image": 0, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "p": 0.3 } } } diff --git a/smauglab/configs/transform_params_one-sequence-to-segment-them-all.json b/smauglab/configs/transform_params_one-sequence-to-segment-them-all.json index c037ff6..6d124c9 100755 --- a/smauglab/configs/transform_params_one-sequence-to-segment-them-all.json +++ b/smauglab/configs/transform_params_one-sequence-to-segment-them-all.json @@ -1,165 +1,230 @@ { - "ScharrTransform": { - "kernel_type": "Scharr", - "absolute": true, - "retain_stats": true, - "mix_prob": 0.50, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.25 - }, - "GaussianBlurTransform": { - "kernel_type": "GaussianBlur", - "sigma": 1.0, - "retain_stats": false, - "mix_prob": 0.00, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.20 - }, - "UnsharpMaskTransform": { - "kernel_type": "UnsharpMask", - "sigma": 1.0, - "unsharp_amount": 1.5, - "retain_stats": false, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "mix_prob": 0.50, - "probability": 0.25 - }, - "RandomConvTransform": { - "kernel_type": "RandConv", - "kernel_sizes": [3,5,7], - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "mix_prob": 0.50, - "probability": 0.20 - }, - "RedistributeSegTransform": { - "in_seg": 0.25, - "retain_stats": true, - "probability": 0.4 - }, - "GaussianNoiseTransform": { - "mean": 0.0, - "std": 1.0, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.1 - }, - "ClampTransform": { - "max_clamp_amount": 0.2, - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.00 - }, - "BrightnessTransform": { - "brightness_range": [0.75, 1.25], - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.15 - }, - "GammaTransform": { - "gamma_range": [0.7, 1.5], - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.30 - }, - "InvGammaTransform": { - "gamma_range": [0.7, 1.5], - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.10 - }, - "ContrastTransform": { - "contrast_range": [0.75, 1.25], - "retain_stats": false, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.15 - }, - "FunctionTransform": { - "retain_stats": true, - "in_seg": 0, - "out_seg": 0, - "mix_in_out": false, - "probability": 0.1 - }, - "InverseTransform": { - "retain_stats": true, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "mix_prob": 0.00, - "probability": 0.25 - }, - "HistogramEqualizationTransform": { - "retain_stats": true, - "in_seg": 0, - "out_seg": 0, - "mix_in_out": true, - "mix_prob": 0.60, - "probability": 0.25 - }, - "SimulateLowResTransform": { - "scale": [0.5, 1.0], - "crop": [1.0, 1.0], - "same_on_batch": false, - "probability": 0.25 - }, - "AcqTransform": { - "scale": [0.6, 1.0], - "crop": [1.0, 1.0], - "same_on_batch": false, - "probability": 0.00 - }, - "BiasFieldTransform": { - "retain_stats": true, - "coefficients": 0.2, - "in_seg": 0.0, - "out_seg": 0.0, - "mix_in_out": true, - "probability": 0.15 - }, - "FlipTransform": { - "flip_axis": [0], - "same_on_batch": false, - "keepdim": true, - "probability": 0.5 - }, - "AffineTransform": { - "degrees": 5, - "translate": [0.1, 0.1, 0.1], - "scale": [0.7, 1.4], - "shear": [-5, 5, -5, 5, -5, 5], - "resample": "bilinear", - "probability": 0.0 - }, - "nnUNetSpatialTransform": { - "patch_center_dist_from_border": 80, - "random_crop": false, - "p_elastic_deform": 0.0, - "p_rotation": 0.2, - "p_scaling": 0.2, - "scaling": [0.7, 1.4], - "p_synchronize_scaling_across_axes": 1, - "bg_style_seg_sampling": false - }, - "ZscoreNormalizationTransform": { - "probability": 0.00 + "GPU": { + "RandomFlipTransformGPU": { + "flip_axis": [ + 0 + ], + "same_on_batch": false, + "keepdim": true, + "p": 0.5 + }, + "RandomAffineGPU": { + "degrees": 5, + "translate": [ + 0.1, + 0.1, + 0.1 + ], + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "resample": "bilinear", + "p": 0.0 + }, + "RandomInverseGPU": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.0, + "p": 0.25 + }, + "RandomHistogramEqualizationGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.6, + "p": 0.25 + }, + "RandomRedistributeSegGPU": { + "in_seg": 0.25, + "retain_stats": true, + "p": 0.4 + }, + "RandomScharrGPU": { + "absolute": true, + "retain_stats": true, + "mix_prob": 0.5, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.25 + }, + "RandomUnsharpMaskGPU": { + "sigma": 1.0, + "unsharp_amount": 1.5, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.5, + "p": 0.25 + }, + "RandomRandConvGPU": { + "kernel_sizes": [ + 3, + 5, + 7 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.5, + "p": 0.2 + }, + "RandomClampGPU": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.0 + }, + "RandomGaussianNoiseGPU": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.1 + }, + "RandomGaussianBlurGPU": { + "sigma": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.2 + }, + "RandomBrightnessGPU": { + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.15 + }, + "RandomGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.3 + }, + "RandomInvGammaGPU": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.1 + }, + "RandomContrastGPU": { + "contrast_range": [ + 0.75, + 1.25 + ], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.15 + }, + "RandomLog1pGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomSqrtGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomSinGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomExpGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomSigmoidGPU": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "p": 0.1 + }, + "RandomLowResTransformGPU": { + "scale": [ + 0.5, + 1.0 + ], + "same_on_batch": false, + "p": 0.25 + }, + "RandomAcqTransformGPU": { + "scale": [ + 0.6, + 1.0 + ], + "same_on_batch": false, + "p": 0.0 + }, + "RandomBiasFieldGPU": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "p": 0.15 + }, + "ZscoreNormalizationGPU": { + "p": 0.0 + } + }, + "CPU": { + "SpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [ + 0.7, + 1.4 + ], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false, + "mode_seg": "nearest" + } } } diff --git a/smauglab/registry.py b/smauglab/registry.py new file mode 100644 index 0000000..5fd0b94 --- /dev/null +++ b/smauglab/registry.py @@ -0,0 +1,551 @@ +"""The single source of truth for which augmentations exist. + +Each augmentation class registers itself with `@register(...)`. The registry then +answers: + +* what exists, per backend -- `names()`, `entries()` +* which class a config key maps to -- `get()` +* what parameters that class accepts -- `accepted_params()` +* which backends implement a given concept -- `matrix()`, keyed on `AugId` + +Deliberately stdlib-only, and deliberately importing nothing from +`smauglab.transforms` at module scope: that is what keeps the import graph acyclic +(transform modules import `register` from here) and lets a CLI ask "what exists?" +without paying for torch until it actually has to. +""" + +from __future__ import annotations + +import contextlib +import difflib +import importlib +import inspect +from collections.abc import Callable, Iterator, Mapping +from dataclasses import dataclass, field +from enum import Enum +from types import MappingProxyType +from typing import Any, TypeVar + +__all__ = [ + "AugEntry", + "AugId", + "AugType", + "Backend", + "InvalidConfigError", + "RegistryError", + "UnknownAugmentationError", + "UnknownParameterError", + "accepted_params", + "entries", + "get", + "isolated", + "matrix", + "names", + "register", + "register_entry", +] + + +# `StrEnum` is 3.11+; pyproject promises `requires-python = ">=3.10"`. +class Backend(str, Enum): + """Where an augmentation runs, which is also which config section it lives in.""" + + GPU = "GPU" + CPU = "CPU" + MONAI = "MONAI" + + +class AugType(str, Enum): + """Augmentation strength class. + + Member names are verbatim from segtransferaug's `AUG2GROUP`, which this + replaces, so downstream `AugType.TA` keeps resolving. The GPU random-order + pipelines bucket transforms by this to decide what goes inside each + `RandomChooseXTransformsGPU`. + """ + + GEO = "GEO" # geometric: applied in sequence, never bucketed + GE = "GE" # general enhancement, usually light + TA = "TA" # transfer augmentation, usually heavy + + +class AugId(str, Enum): + """A backend-neutral augmentation *concept*. + + Config keys are class names and therefore backend-specific + (`RandomGaussianNoiseGPU` vs batchgeneratorsv2's `GaussianNoiseTransform`), so + a join key is needed to say "these are the same augmentation on two backends". + That is what makes `matrix()` -- the CPU/GPU/MONAI coverage table -- possible. + + An enum rather than a free string on purpose: a typo'd `"gausian_noise"` would + silently orphan a matrix row, whereas `AugId.GAUSIAN_NOISE` fails at import. + The member list doubles as the authoritative inventory of concepts, which is + what "track which augmentations have a MONAI version" actually means -- the row + exists, and an empty cell is the record that no implementation does. + """ + + # -- geometric + FLIP = "flip" + AFFINE = "affine" + CROP = "crop" + SPATIAL = "spatial" # nnU-Net's own SpatialTransform + # -- general enhancement + GAUSSIAN_NOISE = "gaussian_noise" + GAUSSIAN_BLUR = "gaussian_blur" + BRIGHTNESS = "brightness" + CONTRAST = "contrast" + GAMMA = "gamma" + INV_GAMMA = "inv_gamma" + CLAMP = "clamp" + LOW_RES = "low_res" + ACQ = "acq" + ZSCORE = "zscore" + MIRROR = "mirror" + # -- transfer augmentation + SCHARR = "scharr" + LAPLACE = "laplace" + UNSHARP_MASK = "unsharp_mask" + RAND_CONV = "rand_conv" + BIAS_FIELD = "bias_field" + INVERSE = "inverse" + HISTOGRAM_EQUAL = "histogram_equal" + REDISTRIBUTE_SEG = "redistribute_seg" + PALETTE = "palette" + DOMAIN_TRANSFER = "domain_transfer" + SYNTHSEG = "synthseg" + ARTIFACT = "artifact" + SPATIAL_CUSTOM = "spatial_custom" + SHAPE = "shape" + # -- elementwise functions, one concept each so key <-> class stays 1:1 + FUNC_LOG1P = "func_log1p" + FUNC_SQRT = "func_sqrt" + FUNC_SIN = "func_sin" + FUNC_EXP = "func_exp" + FUNC_SIGMOID = "func_sigmoid" + + +class RegistryError(Exception): + """Base class for every registry and config-resolution failure.""" + + +class UnknownAugmentationError(RegistryError, KeyError): + """A config named an augmentation that is not registered for that backend.""" + + def __str__(self) -> str: # KeyError.__str__ would repr() the message + return self.args[0] if self.args else "" + + +class UnknownParameterError(RegistryError, TypeError): + """A config passed a parameter the augmentation's constructor does not accept.""" + + +class InvalidConfigError(RegistryError, ValueError): + """A config has one or more problems. Every problem is reported at once, so a + broken file is fixed in one pass rather than one pytest run at a time.""" + + def __init__(self, source: str, problems: list[str]) -> None: + self.source = source + self.problems = problems + joined = "\n".join(f" - {p}" for p in problems) + super().__init__(f"{source}: {len(problems)} problem(s)\n{joined}") + + +# Renamed *parameters*, for diagnostics only -- consulted when building an error +# message, NEVER when loading a config. difflib cannot bridge these on its own: +# "probability" vs "p" scores ~0.17, well under any usable cutoff, so without this +# table the single most common migration mistake would get no suggestion at all. +# A test asserts that no key here resolves through `get()`, which is what keeps it +# from becoming a back-compat path. +# +# Renamed augmentations are deliberately absent: stem matching in +# `_unknown_augmentation_message` already bridges "ScharrTransform" -> RandomScharrGPU +# and "SynthSeg" -> RandomSynthSegGPU without a table to maintain. +RENAMED_HINTS: Mapping[str, str] = MappingProxyType( + { + "probability": "p", + "shear": "shears", + "invert_image": "use RandomInvGammaGPU instead", + "kernel_type": "the class name now carries the kernel (e.g. RandomScharrGPU)", + "func": "the class name now carries the function (e.g. RandomLog1pGPU)", + "one_dim": "use RandomAcqTransformGPU (one_dim) or RandomLowResTransformGPU", + } +) + + +@dataclass(frozen=True, slots=True) +class AugEntry: + """One registered augmentation: a class plus everything a config needs to know.""" + + name: str + cls: type + backend: Backend + aug_id: AugId + group: AugType + order: int + # Set when the constructor legitimately forwards **kwargs to another class, so + # `accepted_params` unions both signatures instead of giving up. RandomSynthSegGPU + # forwards ~38 parameters to SynthSegGenerator. + forwards_to: type | None = None + # Supplied by the builder at runtime (nnU-Net hands over patch size, rotation and + # mirror axes), so these are rejected if a config tries to set them. + context_params: tuple[str, ...] = () + # CPU only: batchgeneratorsv2 puts the apply probability on a RandomTransform + # wrapper rather than the transform, so `p` is a builder key, not a ctor kwarg. + wrap_random: bool = True + # Run in pipeline order even in the random-order pipelines, instead of going + # into a shuffled RandomChooseX bucket. GEO transforms are always sequential; + # this is for the ones whose group says otherwise. RandomLowResTransformGPU is + # the only case: segtransferaug's AUG2GROUP calls it GE, but the random-order + # builder has always run it in sequence, and the group is used downstream for + # filtering, so the two meanings are kept apart rather than reconciled. + force_sequential: bool = False + # Name of an env var pointing at a large artefact the transform needs but the + # wheel does not ship. Tests skip rather than fail when it is unset. + external_asset: str | None = None + # Parameters the builder must pass through a callable before handing them over. + # batchgeneratorsv2 ranges are the reason: `BGContrast((0.7, 1.5))` samples 50/50 + # from [lo, 1] and [max(lo, 1), hi], where the bare tuple would be sampled + # uniformly -- a different distribution, not a formatting detail. The config + # stores the plain range; the adapter is applied on the way in. + param_adapters: Mapping[str, Callable[[Any], Any]] = field(default_factory=lambda: MappingProxyType({})) + # Constructor nudges that make the standalone smoke test exercise something. + smoke_kwargs: Mapping[str, Any] = field(default_factory=lambda: MappingProxyType({})) + summary: str = "" + + def __post_init__(self) -> None: + if self.cls.__name__ != self.name: + raise RegistryError( + f"registry name {self.name!r} does not match class name {self.cls.__name__!r}. " + "The config key is the class name, so these cannot diverge." + ) + if _has_var_keyword(self.cls) and self.forwards_to is None: + raise RegistryError( + f"{self.name}.__init__ takes **kwargs but declares no forwards_to. " + "Parameter validation reads the signature, so **kwargs would silently " + "accept anything. Name the parameters explicitly, or set forwards_to " + "to the class the kwargs are passed on to." + ) + + +def _constructor_signature(cls: type) -> inspect.Signature | None: + """The constructor signature, already without `self`. + + `inspect.signature(cls)` rather than `inspect.signature(cls.__init__)`: it drops + `self` for us, and mypy rejects reading `__init__` off a `type` as unsound. + """ + try: + return inspect.signature(cls) + except (TypeError, ValueError): # builtins and C extensions have no signature + return None + + +def _has_var_keyword(cls: type) -> bool: + """True if the constructor ends in **kwargs.""" + signature = _constructor_signature(cls) + if signature is None: + return False + return any(p.kind is inspect.Parameter.VAR_KEYWORD for p in signature.parameters.values()) + + +def _named_params(cls: type) -> dict[str, inspect.Parameter]: + """Named keyword-assignable constructor parameters, minus *args/**kwargs.""" + signature = _constructor_signature(cls) + if signature is None: + return {} + skip = (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) + return {name: p for name, p in signature.parameters.items() if p.kind not in skip} + + +# backend -> name -> entry. Insertion order is irrelevant; `entries()` sorts by `order`. +_REGISTRY: dict[Backend, dict[str, AugEntry]] = {backend: {} for backend in Backend} +# Held in a dict rather than a bare module global so `load_all` can flip it without +# a `global` statement. +_state: dict[str, bool] = {"loaded": False} + +T = TypeVar("T", bound=type) + + +def register_entry(entry: AugEntry) -> AugEntry: + """Add an entry, rejecting anything that would make lookups ambiguous. + + The call form exists for third-party classes that cannot be decorated -- the + batchgeneratorsv2 transforms the CPU pipeline composes directly. + """ + backend_entries = _REGISTRY[entry.backend] + + clash = backend_entries.get(entry.name) + if clash is not None: + raise RegistryError(f"{entry.backend.value} augmentation {entry.name!r} is already registered (as {clash.cls.__module__}).") + + same_order = next((e for e in backend_entries.values() if e.order == entry.order), None) + if same_order is not None: + raise RegistryError( + f"{entry.backend.value} order {entry.order} is taken by {same_order.name!r}, so the " + f"pipeline position of {entry.name!r} would be ambiguous. Orders are 10-spaced -- pick a free slot." + ) + + backend_entries[entry.name] = entry + return entry + + +def register( + *, + aug_id: AugId, + backend: Backend, + group: AugType, + order: int, + forwards_to: type | None = None, + context_params: tuple[str, ...] = (), + wrap_random: bool = True, + force_sequential: bool = False, + external_asset: str | None = None, + smoke_kwargs: Mapping[str, Any] | None = None, + param_adapters: Mapping[str, Callable[[Any], Any]] | None = None, + summary: str = "", +) -> Callable[[T], T]: + """Register the decorated augmentation class. Returns the class unchanged.""" + + def decorate(cls: T) -> T: + register_entry( + AugEntry( + name=cls.__name__, + cls=cls, + backend=backend, + aug_id=aug_id, + group=group, + order=order, + forwards_to=forwards_to, + context_params=context_params, + wrap_random=wrap_random, + force_sequential=force_sequential, + external_asset=external_asset, + smoke_kwargs=MappingProxyType(dict(smoke_kwargs or {})), + param_adapters=MappingProxyType(dict(param_adapters or {})), + summary=summary or _first_docstring_line(cls), + ) + ) + return cls + + return decorate + + +def _first_docstring_line(cls: type) -> str: + doc = inspect.getdoc(cls) or "" + return doc.strip().split("\n", 1)[0] + + +def load_all() -> None: + """Import every transform module so the decorators have run. Idempotent.""" + if _state["loaded"]: + return + # Set before importing, not after: a transform module that queries the registry at + # import time would otherwise re-enter this and recurse. + _state["loaded"] = True + importlib.import_module("smauglab.transforms") + + +def _ensure_loaded() -> None: + if not _state["loaded"]: + load_all() + + +def entries( + backend: Backend | None = None, + group: AugType | None = None, + aug_id: AugId | None = None, +) -> list[AugEntry]: + """Matching entries, in pipeline order. + + Order is a property of the augmentation, not of whoever last edited a config, + so it comes from the registry rather than from config key order. + """ + _ensure_loaded() + backends = [backend] if backend is not None else list(Backend) + found = [e for b in backends for e in _REGISTRY[b].values()] + if group is not None: + found = [e for e in found if e.group is group] + if aug_id is not None: + found = [e for e in found if e.aug_id is aug_id] + return sorted(found, key=lambda e: (e.backend.value, e.order)) + + +def names(backend: Backend | None = None, group: AugType | None = None) -> list[str]: + """Registered config keys, in pipeline order.""" + return [e.name for e in entries(backend=backend, group=group)] + + +def get(name: str, backend: Backend | None = None) -> AugEntry: + """Resolve a config key to its entry, or raise with a suggestion.""" + _ensure_loaded() + backends = [backend] if backend is not None else list(Backend) + for b in backends: + entry = _REGISTRY[b].get(name) + if entry is not None: + return entry + raise UnknownAugmentationError(_unknown_augmentation_message(name, backend)) + + +def _unknown_augmentation_message(name: str, backend: Backend | None) -> str: + candidates = names(backend) + where = f"{backend.value} " if backend is not None else "" + lines = [f"unknown {where}augmentation {name!r}."] + + # Two kinds of near-miss, and they find different things. difflib catches + # typos; matching on the distinctive middle of the name catches a renamed key + # such as "ScharrTransform" -> "RandomScharrGPU", which shares too little with + # its replacement for any usable cutoff. Merged rather than used as a fallback: + # searching across backends, difflib alone would fill the list with CPU + # candidates and crowd out the GPU rename the caller is probably after. + stem = name.removeprefix("Random").removesuffix("Transform").removesuffix("GPU") + close = difflib.get_close_matches(name, candidates, n=3, cutoff=0.6) + close += [c for c in candidates if stem and stem.lower() in c.lower() and c not in close] + if close: + lines.append(f" Did you mean: {', '.join(close[:4])}?") + + lines.append(f" {len(candidates)} registered: run `smauglab list` to see them.") + return "\n".join(lines) + + +def accepted_params(entry: AugEntry) -> dict[str, inspect.Parameter]: + """Every parameter a config may set for this augmentation. + + The constructor signature is the whole truth -- there is no hand-maintained + schema to drift out of sync. `forwards_to` widens it where a class genuinely + passes kwargs on; `context_params` narrows it where the builder supplies the + value; and `p` is added for CPU entries whose probability lives on the + RandomTransform wrapper rather than on the transform itself. + """ + params = _named_params(entry.cls) + if entry.forwards_to is not None: + params = {**_named_params(entry.forwards_to), **params} + for name in entry.context_params: + params.pop(name, None) + if entry.backend is Backend.CPU and entry.wrap_random and "p" not in params: + params["p"] = inspect.Parameter("p", inspect.Parameter.KEYWORD_ONLY, default=1.0, annotation=float) + return params + + +def required_params(entry: AugEntry) -> set[str]: + """Parameters with no default, which a config must therefore supply.""" + return {name for name, p in accepted_params(entry).items() if p.default is inspect.Parameter.empty} + + +def unknown_parameter_message(entry: AugEntry, name: str) -> str: + """Explain an unaccepted parameter, with a suggestion where one exists.""" + allowed = sorted(accepted_params(entry)) + lines = [f"{entry.name}: unknown parameter {name!r}."] + close = difflib.get_close_matches(name, allowed, n=3, cutoff=0.6) + if close: + lines.append(f" Did you mean: {', '.join(close)}?") + elif name in RENAMED_HINTS: + lines.append(f" {name!r} -> {RENAMED_HINTS[name]}") + if name in entry.context_params: + lines[-1:] = [f" {name!r} is supplied by the trainer at runtime and must not appear in the config."] + lines.append(f" Accepted: {', '.join(allowed)}") + return "\n".join(lines) + + +def matrix() -> dict[AugId, dict[Backend, AugEntry | None]]: + """Concept -> backend -> implementing entry, or None where there is none. + + Every `AugId` gets a row even with no implementations anywhere, because an + empty cell is exactly the thing worth seeing. + """ + _ensure_loaded() + table: dict[AugId, dict[Backend, AugEntry | None]] = {aug_id: dict.fromkeys(Backend) for aug_id in AugId} + for entry in entries(): + table[entry.aug_id][entry.backend] = entry + return table + + +def render_matrix(fmt: str = "md") -> str: + """The CPU/GPU/MONAI coverage table.""" + table = matrix() + if fmt not in {"md", "table"}: + raise ValueError(f"unknown format {fmt!r}; expected 'md' or 'table'") + + def cell(entry: AugEntry | None) -> str: + if entry is None: + return "—" if fmt == "md" else "-" + return f"`{entry.name}`" if fmt == "md" else entry.name + + def group_of(row: dict[Backend, AugEntry | None]) -> str: + found = next((e for e in row.values() if e is not None), None) + return found.group.value if found else "" + + header = ["Augmentation", "Group", *(b.value for b in Backend)] + rows = [[aug_id.value, group_of(row), *(cell(row[b]) for b in Backend)] for aug_id, row in table.items()] + + if fmt == "md": + out = ["| " + " | ".join(header) + " |", "| " + " | ".join("---" for _ in header) + " |"] + out += ["| " + " | ".join(r) + " |" for r in rows] + return "\n".join(out) + + widths = [max(len(r[i]) for r in [header, *rows]) for i in range(len(header))] + return "\n".join(" ".join(c.ljust(w) for c, w in zip(row, widths)).rstrip() for row in [header, *rows]) + + +def _json_safe(value: Any) -> Any: + """Best-effort conversion of a default into something JSON can hold.""" + if value is inspect.Parameter.empty: + # Required: no default to show. Null makes the hole visible, and strict + # loading will reject it until someone fills it in. + return None + if isinstance(value, tuple): + return [_json_safe(v) for v in value] + if isinstance(value, list): + return [_json_safe(v) for v in value] + if isinstance(value, (str, int, float, bool)) or value is None: + return value + return str(value) + + +def render_template(backend: Backend) -> dict[str, dict[str, Any]]: + """A config section naming every registered augmentation at its defaults. + + Round-trips through the same `accepted_params`/`inspect.signature` path that + validation uses, so a template that fails to load is a real bug rather than a + documentation slip. Checked into the repo, which is what makes "an augmentation + exists but no config can reach it" a test failure. + """ + section: dict[str, dict[str, Any]] = {} + for entry in entries(backend): + section[entry.name] = {name: _json_safe(param.default) for name, param in sorted(accepted_params(entry).items())} + return section + + +def clear(mark_loaded: bool = True) -> None: + """Drop every entry. For tests only -- never call this from library code. + + `mark_loaded` defaults to True so a test that clears the registry and registers + its own synthetic entries does not have the real augmentations imported back in + underneath it on the next lookup. Pass False to restore normal lazy loading. + + Note this is NOT undoable on its own: `load_all()` re-imports + `smauglab.transforms`, but the module is already in `sys.modules` by then, so the + `@register` decorators do not run a second time and the registry would stay empty + for the rest of the process. Use `isolated()` unless you mean that. + """ + for backend_entries in _REGISTRY.values(): + backend_entries.clear() + _state["loaded"] = mark_loaded + + +@contextlib.contextmanager +def isolated() -> Iterator[None]: + """Empty the registry for the duration of the block, then put it back. + + For tests that register synthetic augmentations and must not see the real ones. + Restores by hand rather than by reloading, because re-importing the transform + modules would not re-run their decorators -- see `clear()`. + """ + saved = {backend: dict(entries_) for backend, entries_ in _REGISTRY.items()} + was_loaded = _state["loaded"] + clear(mark_loaded=True) + try: + yield + finally: + for backend, entries_ in saved.items(): + _REGISTRY[backend].clear() + _REGISTRY[backend].update(entries_) + _state["loaded"] = was_loaded diff --git a/smauglab/trainers/nnUNetTrainerDAExt.py b/smauglab/trainers/nnUNetTrainerDAExt.py index 5926255..30f0a2a 100644 --- a/smauglab/trainers/nnUNetTrainerDAExt.py +++ b/smauglab/trainers/nnUNetTrainerDAExt.py @@ -1,7 +1,25 @@ +"""The SmaugLab nnU-Net trainer. + +One class, because the config already says everything the three previous trainers +encoded between them. `nnUNetTrainerDAExt` built the CPU pipeline, `...GPU` built the +GPU one plus nnU-Net's SpatialTransform, and `...Hybrid` built both -- but a config is +sectioned into "CPU" and "GPU", so which sections are populated decides that on its +own: + + transform_params.json CPU: 19 GPU: 0 -> CPU-only, as ...DAExt did + transform_params_gpu.json CPU: 1 GPU: 26 -> GPU-only, as ...DAExtGPU did + transform_params_hybrid.json CPU: 19 GPU: 24 -> both, as ...DAExtHybrid did + +The class keeps the name `nnUNetTrainerDAExtGPU` whatever the config contains. That is +not cosmetic: nnU-Net writes the trainer class name into every checkpoint +(`checkpoint['trainer_name']`) and resolves the class from it at inference, so +renaming it would make several hundred trained models unloadable. +""" + import importlib -import json import os import shutil +import warnings from typing import Union import numpy as np @@ -11,11 +29,9 @@ from batchgeneratorsv2.transforms.nnunet.random_binary_operator import ApplyRandomBinaryOperatorTransform from batchgeneratorsv2.transforms.nnunet.remove_connected_components import RemoveRandomConnectedComponentFromOneHotEncodingTransform from batchgeneratorsv2.transforms.nnunet.seg_to_onehot import MoveSegAsOneHotToDataTransform -from batchgeneratorsv2.transforms.spatial.spatial import SpatialTransform from batchgeneratorsv2.transforms.utils.compose import ComposeTransforms from batchgeneratorsv2.transforms.utils.deep_supervision_downsampling import DownsampleSegForDSTransform from batchgeneratorsv2.transforms.utils.nnunet_masking import MaskImageTransform -from batchgeneratorsv2.transforms.utils.pseudo2d import Convert2DTo3DTransform, Convert3DTo2DTransform from batchgeneratorsv2.transforms.utils.random import RandomTransform from batchgeneratorsv2.transforms.utils.remove_label import RemoveLabelTansform from batchgeneratorsv2.transforms.utils.seg_to_regions import ConvertSegmentationToRegionsTransform @@ -24,152 +40,79 @@ from torch import autocast from smauglab import configs +from smauglab.config import load_config +from smauglab.registry import Backend from smauglab.trainers.utils import DownsampleSegForDSTransformCustom -from smauglab.transforms.cpu.transforms import AugTransforms -from smauglab.transforms.gpu.transforms import AugTransformsGPU - - -class nnUNetTrainerDAExt(nnUNetTrainer): - def __init__(self, plans: dict, configuration: str, fold: int, dataset_json: dict, device: torch.device = torch.device("cuda")): - super().__init__(plans, configuration, fold, dataset_json, device) - - def configure_rotation_dummyDA_mirroring_and_inital_patch_size(self): - rotation_for_DA, do_dummy_2d_data_aug, initial_patch_size, mirror_axes = ( - super().configure_rotation_dummyDA_mirroring_and_inital_patch_size() +from smauglab.transforms.build import build_cpu_pipeline, build_gpu_pipeline +from smauglab.transforms.gpu.base import AugmentationSequentialCustom + +#: Env var naming the config to train with. +CONFIG_ENV = "SMAUGLAB_PARAMS_JSON" + +#: Previous name, still honoured so existing sweep scripts keep working. The three +#: old variables (_CPU_JSON, _GPU_JSON, _HYBRID_JSON) picked a trainer as much as a +#: file; only this one ever had an external caller. +LEGACY_CONFIG_ENV = "SMAUGLAB_PARAMS_GPU_JSON" + +DEFAULT_CONFIG = "transform_params_gpu.json" + + +def resolve_config_path() -> str: + """Locate the config: new env var, then the deprecated one, then the default.""" + path = os.environ.get(CONFIG_ENV) + if path: + return path + legacy = os.environ.get(LEGACY_CONFIG_ENV) + if legacy: + warnings.warn( + f"{LEGACY_CONFIG_ENV} is deprecated; use {CONFIG_ENV}. The config's CPU and GPU " + "sections now decide which augmentations run, so the name no longer implies a backend.", + DeprecationWarning, + stacklevel=2, ) - # Remove mirroring - mirror_axes = None - self.inference_allowed_mirroring_axes = None - return rotation_for_DA, do_dummy_2d_data_aug, initial_patch_size, mirror_axes + return legacy + return str(importlib.resources.files(configs) / DEFAULT_CONFIG) - @staticmethod - def get_training_transforms( - patch_size: Union[np.ndarray, tuple[int, ...]], - rotation_for_DA: RandomScalar, - deep_supervision_scales: Union[list, tuple, None], - mirror_axes: tuple[int, ...], - do_dummy_2d_data_aug: bool, - use_mask_for_norm: list[bool] | None = None, - is_cascaded: bool = False, - foreground_labels: Union[tuple[int, ...], list[int]] | None = None, - regions: list[Union[list[int], tuple[int, ...], int]] | None = None, - ignore_label: int | None = None, - retain_stats: bool = False, - ) -> BasicTransform: - transforms = [] - - ### Adds transforms - # Load transform parameters from json file - configs_path = importlib.resources.files(configs) - json_path = os.environ.get("SMAUGLAB_PARAMS_CPU_JSON", str(configs_path / "transform_params.json")) - transforms.append( - AugTransforms( - json_path=json_path, - do_dummy_2d_data_aug=do_dummy_2d_data_aug, - patch_size=patch_size, - rotation_for_DA=rotation_for_DA, - mirror_axes=mirror_axes, - ) - ) - - if do_dummy_2d_data_aug: - transforms.append(Convert3DTo2DTransform()) - patch_size_spatial = patch_size[1:] - else: - patch_size_spatial = patch_size - transforms.append( - SpatialTransform( - patch_size_spatial, - patch_center_dist_from_border=0, - random_crop=False, - p_elastic_deform=0, - p_rotation=0, - rotation=rotation_for_DA, - p_scaling=0, - scaling=(0.7, 1.4), - p_synchronize_scaling_across_axes=1, - bg_style_seg_sampling=False, - mode_seg="nearest", - ) - ) - - if do_dummy_2d_data_aug: - transforms.append(Convert2DTo3DTransform()) - - if use_mask_for_norm is not None and any(use_mask_for_norm): - transforms.append( - MaskImageTransform( - apply_to_channels=[i for i in range(len(use_mask_for_norm)) if use_mask_for_norm[i]], - channel_idx_in_seg=0, - set_outside_to=0, - ) - ) - - transforms.append(RemoveLabelTansform(-1, 0)) - - # The following augmentations are related to special nnunet executions - if is_cascaded: - assert foreground_labels is not None, "We need foreground_labels for cascade augmentations" - transforms.append( - MoveSegAsOneHotToDataTransform(source_channel_idx=1, all_labels=foreground_labels, remove_channel_from_source=True) - ) - transforms.append( - RandomTransform( - ApplyRandomBinaryOperatorTransform( - channel_idx=list(range(-len(foreground_labels), 0)), strel_size=(1, 8), p_per_label=1 - ), - apply_probability=0.4, - ) - ) - transforms.append( - RandomTransform( - RemoveRandomConnectedComponentFromOneHotEncodingTransform( - channel_idx=list(range(-len(foreground_labels), 0)), - fill_with_other_class_p=0, - dont_do_if_covers_more_than_x_percent=0.15, - p_per_label=1, - ), - apply_probability=0.2, - ) - ) - if regions is not None: - # the ignore label must also be converted - transforms.append( - ConvertSegmentationToRegionsTransform( - regions=[*list(regions), ignore_label] if ignore_label is not None else regions, channel_in_seg=0 - ) - ) +def _has_gpu_augmentations(config) -> bool: + """Whether this config asks for anything on the GPU side.""" + return bool(config.names(Backend.GPU)) - if deep_supervision_scales is not None: - transforms.append(DownsampleSegForDSTransform(ds_scales=deep_supervision_scales)) - return ComposeTransforms(transforms) +class nnUNetTrainerDAExtGPU(nnUNetTrainer): + """nnU-Net trainer driven entirely by a SmaugLab config. + CPU-section augmentations run in the dataloader worker; GPU-section ones run on + the batch in `train_step`. + """ -class nnUNetTrainerDAExtGPU(nnUNetTrainer): def __init__(self, plans: dict, configuration: str, fold: int, dataset_json: dict, device: torch.device = torch.device("cuda")): super().__init__(plans, configuration, fold, dataset_json, device) - self.num_epochs = 1000 - - # Load transform parameters from json file - configs_path = importlib.resources.files(configs) - json_path = os.environ.get("SMAUGLAB_PARAMS_GPU_JSON", str(configs_path / "transform_params_gpu.json")) - self.transforms = AugTransformsGPU(json_path=json_path).to(self.device) - print(f"Using SmaugLab GPU transforms with parameters from: {json_path}") - - # Copy json transfrom parameters to output folder - shutil.copy(json_path, os.path.join(self.output_folder, "transform_params_gpu_used_for_training.json")) - - def configure_rotation_dummyDA_mirroring_and_inital_patch_size(self): - rotation_for_DA, do_dummy_2d_data_aug, initial_patch_size, mirror_axes = ( - super().configure_rotation_dummyDA_mirroring_and_inital_patch_size() + json_path = resolve_config_path() + config = load_config(json_path) + + # Built only when the config actually asks for GPU augmentations, so a + # CPU-only config costs nothing per step and train_step stays a no-op. + self.transforms: AugmentationSequentialCustom | None = None + if _has_gpu_augmentations(config): + self.transforms = AugmentationSequentialCustom( + *build_gpu_pipeline( + config.section(Backend.GPU), + mode=config.pipeline_mode(), + options=config.pipeline_options("random_choose"), + source=config.source, + ), + data_keys=["input", "mask"], + same_on_batch=True, + ).to(self.device) + + print(f"Using SmaugLab transforms from: {json_path}") + print( + f" CPU: {len(config.names(Backend.CPU))} augmentations, GPU: {len(config.names(Backend.GPU))}, mode: {config.pipeline_mode().value}" ) - # Remove mirroring - mirror_axes = None - self.inference_allowed_mirroring_axes = None - return rotation_for_DA, do_dummy_2d_data_aug, initial_patch_size, mirror_axes + + shutil.copy(json_path, os.path.join(self.output_folder, "transform_params_used_for_training.json")) @staticmethod def get_training_transforms( @@ -183,43 +126,26 @@ def get_training_transforms( foreground_labels: Union[tuple[int, ...], list[int]] | None = None, regions: list[Union[list[int], tuple[int, ...], int]] | None = None, ignore_label: int | None = None, - retain_stats: bool = False, ) -> BasicTransform: - transforms = [] - - configs_path = importlib.resources.files(configs) - json_path = os.environ.get("SMAUGLAB_PARAMS_GPU_JSON", str(configs_path / "transform_params_gpu.json")) - with open(json_path) as f: - config = json.load(f) + """Dataloader-side augmentations: whatever the config's CPU section names. - ### Keep some nnunet transforms - if do_dummy_2d_data_aug: - transforms.append(Convert3DTo2DTransform()) - patch_size_spatial = patch_size[1:] - else: - patch_size_spatial = patch_size - - spatial_params = config.get("nnUNetSpatialTransform", {}) + A staticmethod because that is nnU-Net's contract, so it cannot reach the + instance's parsed config and resolves the path itself. `load_config` is + cached, so the file is still read and validated once. + """ + transforms = [] - transforms.append( - SpatialTransform( - patch_size_spatial, - patch_center_dist_from_border=spatial_params.get("patch_center_dist_from_border", 0), - random_crop=spatial_params.get("random_crop", False), - p_elastic_deform=spatial_params.get("p_elastic_deform", 0), - p_rotation=spatial_params.get("p_rotation", 0), + config = load_config(resolve_config_path()) + transforms.extend( + build_cpu_pipeline( + config.section(Backend.CPU), + do_dummy_2d_data_aug=do_dummy_2d_data_aug, + patch_size=patch_size, rotation=rotation_for_DA, - p_scaling=spatial_params.get("p_scaling", 0), - scaling=spatial_params.get("scaling", (0.7, 1.4)), - p_synchronize_scaling_across_axes=spatial_params.get("p_synchronize_scaling_across_axes", 1), - bg_style_seg_sampling=False, - mode_seg="nearest", + source=config.source, ) ) - if do_dummy_2d_data_aug: - transforms.append(Convert2DTo3DTransform()) - if use_mask_for_norm is not None and any(use_mask_for_norm): transforms.append( MaskImageTransform( @@ -267,9 +193,12 @@ def get_training_transforms( # transforms.append(ZscoreNormalization()) - # NOTE: DownsampleSegForDSTransform is now handled in train_step for GPU augmentations - # if deep_supervision_scales is not None: - # transforms.append(DownsampleSegForDSTransform(ds_scales=deep_supervision_scales)) + # Deep supervision has to come after whatever last deformed the mask. With GPU + # augmentations that is train_step, so the downsampling happens there; without + # them nothing touches the mask after this point and it belongs here, which is + # where nnU-Net puts it. + if deep_supervision_scales is not None and not _has_gpu_augmentations(config): + transforms.append(DownsampleSegForDSTransform(ds_scales=deep_supervision_scales)) return ComposeTransforms(transforms) @@ -303,63 +232,6 @@ def get_validation_transforms( transforms.append(DownsampleSegForDSTransform(ds_scales=deep_supervision_scales)) return ComposeTransforms(transforms) - def train_step(self, batch: dict) -> dict: - data = batch["data"] - target = batch["target"] - - data = data.to(self.device, non_blocking=True) - # Now target should be a single tensor, not a list - target = target.to(self.device, non_blocking=True) - # if isinstance(target, list): - # target = [i.to(self.device, non_blocking=True) for i in target] - # else: - # target = target.to(self.device, non_blocking=True) - - self.optimizer.zero_grad(set_to_none=True) - # Autocast can be annoying - # If the device_type is 'cpu' then it's slow as heck and needs to be disabled. - # If the device_type is 'mps' then it will complain that mps is not implemented, even if enabled=False is set. Whyyyyyyy. (this is why we don't make use of enabled=False) - # So autocast will only be active if we have a cuda device. - with autocast(self.device.type, enabled=True) if self.device.type == "cuda" else dummy_context(): - # Apply GPU augmentations to full-resolution data/target - data, target = self.transforms(data, target) - - # Create multi-scale targets for deep supervision after augmentation - deep_supervision_scales = self._get_deep_supervision_scales() - if deep_supervision_scales is not None: - ds_transform = DownsampleSegForDSTransformCustom(ds_scales=deep_supervision_scales) - target = ds_transform(target) - - output = self.network(data) - # del data - l = self.loss(output, target) - - if self.grad_scaler is not None: - self.grad_scaler.scale(l).backward() - self.grad_scaler.unscale_(self.optimizer) - torch.nn.utils.clip_grad_norm_(self.network.parameters(), 12) - self.grad_scaler.step(self.optimizer) - self.grad_scaler.update() - else: - l.backward() - torch.nn.utils.clip_grad_norm_(self.network.parameters(), 12) - self.optimizer.step() - return {"loss": l.detach().cpu().numpy()} - - -class nnUNetTrainerDAExtHybrid(nnUNetTrainer): - def __init__(self, plans: dict, configuration: str, fold: int, dataset_json: dict, device: torch.device = torch.device("cuda")): - super().__init__(plans, configuration, fold, dataset_json, device) - - # Load transform parameters from json file - configs_path = importlib.resources.files(configs) - json_path = os.environ.get("SMAUGLAB_PARAMS_HYBRID_JSON", str(configs_path / "transform_params_hybrid.json")) - self.transforms = AugTransformsGPU(json_path=json_path).to(self.device) - print(f"Using SmaugLab hybrid transforms with parameters from: {json_path}") - - # Copy json transfrom parameters to output folder - shutil.copy(json_path, os.path.join(self.output_folder, "transform_params_hybrid_used_for_training.json")) - def configure_rotation_dummyDA_mirroring_and_inital_patch_size(self): rotation_for_DA, do_dummy_2d_data_aug, initial_patch_size, mirror_axes = ( super().configure_rotation_dummyDA_mirroring_and_inital_patch_size() @@ -369,87 +241,6 @@ def configure_rotation_dummyDA_mirroring_and_inital_patch_size(self): self.inference_allowed_mirroring_axes = None return rotation_for_DA, do_dummy_2d_data_aug, initial_patch_size, mirror_axes - @staticmethod - def get_training_transforms( - patch_size: Union[np.ndarray, tuple[int, ...]], - rotation_for_DA: RandomScalar, - deep_supervision_scales: Union[list, tuple, None], - mirror_axes: tuple[int, ...], - do_dummy_2d_data_aug: bool, - use_mask_for_norm: list[bool] | None = None, - is_cascaded: bool = False, - foreground_labels: Union[tuple[int, ...], list[int]] | None = None, - regions: list[Union[list[int], tuple[int, ...], int]] | None = None, - ignore_label: int | None = None, - retain_stats: bool = False, - ) -> BasicTransform: - transforms = [] - - ### Adds transforms - # Load transform parameters from json file - configs_path = importlib.resources.files(configs) - json_path = os.environ.get("SMAUGLAB_PARAMS_HYBRID_JSON", str(configs_path / "transform_params_hybrid.json")) - transforms.append( - AugTransforms( - json_path=json_path, - do_dummy_2d_data_aug=do_dummy_2d_data_aug, - patch_size=patch_size, - rotation_for_DA=rotation_for_DA, - mirror_axes=mirror_axes, - ) - ) - - if use_mask_for_norm is not None and any(use_mask_for_norm): - transforms.append( - MaskImageTransform( - apply_to_channels=[i for i in range(len(use_mask_for_norm)) if use_mask_for_norm[i]], - channel_idx_in_seg=0, - set_outside_to=0, - ) - ) - - transforms.append(RemoveLabelTansform(-1, 0)) - - # The following augmentations are related to special nnunet executions - if is_cascaded: - assert foreground_labels is not None, "We need foreground_labels for cascade augmentations" - transforms.append( - MoveSegAsOneHotToDataTransform(source_channel_idx=1, all_labels=foreground_labels, remove_channel_from_source=True) - ) - transforms.append( - RandomTransform( - ApplyRandomBinaryOperatorTransform( - channel_idx=list(range(-len(foreground_labels), 0)), strel_size=(1, 8), p_per_label=1 - ), - apply_probability=0.4, - ) - ) - transforms.append( - RandomTransform( - RemoveRandomConnectedComponentFromOneHotEncodingTransform( - channel_idx=list(range(-len(foreground_labels), 0)), - fill_with_other_class_p=0, - dont_do_if_covers_more_than_x_percent=0.15, - p_per_label=1, - ), - apply_probability=0.2, - ) - ) - - if regions is not None: - # the ignore label must also be converted - transforms.append( - ConvertSegmentationToRegionsTransform( - regions=[*list(regions), ignore_label] if ignore_label is not None else regions, channel_in_seg=0 - ) - ) - - # NOTE: DownsampleSegForDSTransform is now handled in train_step for GPU augmentations - # if deep_supervision_scales is not None: - # transforms.append(DownsampleSegForDSTransform(ds_scales=deep_supervision_scales)) - - return ComposeTransforms(transforms) - def train_step(self, batch: dict) -> dict: data = batch["data"] target = batch["target"] @@ -468,14 +259,17 @@ def train_step(self, batch: dict) -> dict: # If the device_type is 'mps' then it will complain that mps is not implemented, even if enabled=False is set. Whyyyyyyy. (this is why we don't make use of enabled=False) # So autocast will only be active if we have a cuda device. with autocast(self.device.type, enabled=True) if self.device.type == "cuda" else dummy_context(): - # Apply GPU augmentations to full-resolution data/target - data, target = self.transforms(data, target) - - # Create multi-scale targets for deep supervision after augmentation - deep_supervision_scales = self._get_deep_supervision_scales() - if deep_supervision_scales is not None: - ds_transform = DownsampleSegForDSTransformCustom(ds_scales=deep_supervision_scales) - target = ds_transform(target) + # Apply GPU augmentations to full-resolution data/target, then build the + # deep-supervision targets from the *augmented* mask. A CPU-only config + # builds no GPU pipeline; nothing has touched the mask since the + # dataloader, which already produced those targets. + if self.transforms is not None: + data, target = self.transforms(data, target) + + deep_supervision_scales = self._get_deep_supervision_scales() + if deep_supervision_scales is not None: + ds_transform = DownsampleSegForDSTransformCustom(ds_scales=deep_supervision_scales) + target = ds_transform(target) output = self.network(data) # del data diff --git a/smauglab/transforms/__init__.py b/smauglab/transforms/__init__.py new file mode 100644 index 0000000..f85ef14 --- /dev/null +++ b/smauglab/transforms/__init__.py @@ -0,0 +1,23 @@ +"""Augmentation transforms, split by execution backend. + +`cpu` wraps batchgeneratorsv2 transforms for the dataloader worker; `gpu` wraps +kornia ones for the training step; `synthseg` holds the generative label-to-image +augmentation. + +Importing this package is what populates `smauglab.registry`: every augmentation +class carries an `@register(...)` decorator, so the registry is complete once these +modules have been imported and empty before. `registry.load_all()` does exactly +this import, which is why lookups are correct without callers having to know which +module defines what. + +Note this is deliberately NOT done from `smauglab/__init__.py`: it pulls in torch, +kornia and batchgeneratorsv2, and a bare `import smauglab` should not have to pay +for that. +""" + +from smauglab.transforms.cpu import artifact, contrast, external, fromSeg, spatial # noqa: F401 +from smauglab.transforms.gpu import contrast as gpu_contrast # noqa: F401 +from smauglab.transforms.gpu import domain_transfer # noqa: F401 +from smauglab.transforms.gpu import fromSeg as gpu_fromSeg +from smauglab.transforms.gpu import spatial as gpu_spatial # noqa: F401 +from smauglab.transforms.synthseg import transforms as synthseg_transforms # noqa: F401 diff --git a/smauglab/transforms/build.py b/smauglab/transforms/build.py new file mode 100644 index 0000000..6b952b3 --- /dev/null +++ b/smauglab/transforms/build.py @@ -0,0 +1,205 @@ +"""Build an augmentation pipeline from a config section, using the registry. + +This replaces four hand-written `if` ladders -- one in `gpu/transforms.py`, two in +`gpu/transforms_list.py`, one in `cpu/transforms.py` -- that between them repeated +the same ~900 lines of dispatch and had already drifted apart. Each ladder decided +three things implicitly that are now explicit registry data: which class a key maps +to (`entry.cls`), where it sits in the pipeline (`entry.order`), and whether it runs +in sequence or inside a shuffled bucket (`entry.group` / `force_sequential`). + +Validation is strict and cumulative: an unregistered name, an unaccepted parameter +or a missing required one are all collected and raised together, so a bad config is +fixed in one pass. +""" + +from __future__ import annotations + +from enum import Enum +from typing import Any + +from smauglab import registry +from smauglab.registry import AugEntry, AugType, Backend, InvalidConfigError + +#: Keys a config section may hold that are not augmentations. +NON_AUGMENTATION_KEYS = ("_",) + + +class PipelineMode(str, Enum): + """How the GPU pipeline arranges what the registry gives it.""" + + #: Everything in registry order. What `AugTransformsGPU` has always done. + SEQUENTIAL = "sequential" + #: Geometry in order, then TA and GE each shuffled inside a RandomChooseX. + RANDOM_ORDER = "random_order" + #: As above but the TA bucket keeps its order; GE is not bucketed separately. + RANDOM_ORDER_TA = "random_order_ta" + + +def validate_section(section: dict, backend: Backend, *, source: str = "") -> list[str]: + """Return every problem in a config section. Empty means it will build.""" + problems: list[str] = [] + for name, params in section.items(): + if name.startswith(NON_AUGMENTATION_KEYS): + continue + try: + entry = registry.get(name, backend) + except registry.UnknownAugmentationError as exc: + problems.append(str(exc).replace("\n", "\n ")) + continue + if not isinstance(params, dict): + problems.append(f"{name}: expected a block of parameters, got {type(params).__name__}") + continue + + accepted = registry.accepted_params(entry) + problems.extend(registry.unknown_parameter_message(entry, key).replace("\n", "\n ") for key in params if key not in accepted) + missing = registry.required_params(entry) - set(params) - set(entry.context_params) + if missing: + problems.append(f"{name}: missing required parameter(s) {', '.join(sorted(missing))}") + _ = source + return problems + + +def _instantiate(entry: AugEntry, params: dict, context: dict[str, Any]) -> Any: + """Build one transform, applying adapters and injecting runtime context.""" + kwargs = dict(params) + + # batchgeneratorsv2 ranges need wrapping in BGContrast, which samples differently + # from the bare tuple -- see AugEntry.param_adapters. + for name, adapter in entry.param_adapters.items(): + if name in kwargs: + kwargs[name] = adapter(tuple(kwargs[name])) + + for name in entry.context_params: + if name in context: + kwargs[name] = context[name] + + if entry.backend is Backend.CPU and entry.wrap_random: + # The probability lives on the wrapper, not the transform. + from batchgeneratorsv2.transforms.utils.random import RandomTransform + + probability = kwargs.pop("p", 1.0) + return RandomTransform(entry.cls(**kwargs), apply_probability=probability) + + return entry.cls(**kwargs) + + +def build_transforms( + section: dict, + backend: Backend, + *, + context: dict[str, Any] | None = None, + source: str = "", +) -> list[tuple[Any, AugEntry]]: + """Instantiate every augmentation in the section, in registry order. + + Order comes from the registry rather than from the order of keys in the file: + the two have never agreed, and honouring the file would silently reorder every + pipeline the moment someone tidied a config. + """ + problems = validate_section(section, backend, source=source) + if problems: + raise InvalidConfigError(source, problems) + + context = context or {} + built = [] + for name, params in section.items(): + if name.startswith(NON_AUGMENTATION_KEYS): + continue + entry = registry.get(name, backend) + built.append((_instantiate(entry, params, context), entry)) + return sorted(built, key=lambda pair: pair[1].order) + + +def _buckets(built: list[tuple[Any, AugEntry]], *, hoist_sequential: bool) -> tuple[list, list, list]: + """Split into (sequential, transfer, general-enhancement). + + Geometry always leads, in order. `force_sequential` means "never inside the GE + bucket" and so only bites when there *is* one: with both groups bucketed, + `RandomLowResTransformGPU` is hoisted up front alongside the geometry, but when + only the transfer group is bucketed it stays in its ordinary GE position. That + is exactly what the two hand-written pipelines did, and the difference is + invisible unless you line their two ladders up side by side. + """ + sequential, transfer, enhancement = [], [], [] + for transform, entry in built: + if entry.group is AugType.GEO or (hoist_sequential and entry.force_sequential): + sequential.append(transform) + elif entry.group is AugType.TA: + transfer.append(transform) + else: + enhancement.append(transform) + return sequential, transfer, enhancement + + +def build_gpu_pipeline( + section: dict, + *, + mode: PipelineMode = PipelineMode.SEQUENTIAL, + options: dict[str, Any] | None = None, + source: str = "", +) -> list[Any]: + """The GPU transform list for a given pipeline mode.""" + built = build_transforms(section, Backend.GPU, source=source) + if mode is PipelineMode.SEQUENTIAL: + return [transform for transform, _ in built] + + from smauglab.transforms.gpu.transforms_list import RandomChooseXTransformsGPU + + options = options or {} + sequential, transfer, enhancement = _buckets(built, hoist_sequential=mode is PipelineMode.RANDOM_ORDER) + + out = list(sequential) + out.append( + RandomChooseXTransformsGPU( + transforms_list=transfer, + num_transforms=len(transfer), + p=options.get("ta_probability", 1.0), + random_order=options.get("ta_random_order", True) if mode is PipelineMode.RANDOM_ORDER else False, + ) + ) + if mode is PipelineMode.RANDOM_ORDER: + out.append( + RandomChooseXTransformsGPU( + transforms_list=enhancement, + num_transforms=len(enhancement), + p=options.get("ge_probability", 1.0), + random_order=options.get("ge_random_order", True), + ) + ) + else: + # RANDOM_ORDER_TA shuffles only the transfer augmentations; the general + # enhancements stay in pipeline order, as they always have. + out.extend(enhancement) + return out + + +def build_cpu_pipeline( + section: dict, + *, + do_dummy_2d_data_aug: bool, + patch_size: Any, + rotation: Any, + source: str = "", +) -> list[Any]: + """The CPU transform list, with nnU-Net's runtime context injected. + + `SpatialTransform` is bracketed by the 2D/3D converters when dummy 2D + augmentation is on. Those converters are not augmentations and carry no registry + entry -- they are pipeline structure, emitted here around the slot. + """ + patch_size_spatial = patch_size[1:] if do_dummy_2d_data_aug else patch_size + context = {"patch_size": patch_size_spatial, "rotation": rotation} + built = build_transforms(section, Backend.CPU, context=context, source=source) + + if not do_dummy_2d_data_aug: + return [transform for transform, _ in built] + + from batchgeneratorsv2.transforms.utils.pseudo2d import Convert2DTo3DTransform, Convert3DTo2DTransform + + out: list[Any] = [] + for transform, entry in built: + if entry.name == "SpatialTransform": + out.extend([Convert3DTo2DTransform(), transform, Convert2DTo3DTransform()]) + else: + out.append(transform) + return out diff --git a/smauglab/transforms/cpu/__init__.py b/smauglab/transforms/cpu/__init__.py new file mode 100644 index 0000000..0ca92e8 --- /dev/null +++ b/smauglab/transforms/cpu/__init__.py @@ -0,0 +1 @@ +"""CPU augmentations, built on batchgeneratorsv2 transforms.""" diff --git a/smauglab/transforms/cpu/artifact.py b/smauglab/transforms/cpu/artifact.py index 7699680..66019d9 100644 --- a/smauglab/transforms/cpu/artifact.py +++ b/smauglab/transforms/cpu/artifact.py @@ -5,7 +5,15 @@ import torchio as tio from batchgeneratorsv2.transforms.base.basic_transform import BasicTransform +from smauglab.registry import AugId, AugType, Backend, register + +@register( + aug_id=AugId.ARTIFACT, + backend=Backend.CPU, + group=AugType.TA, + order=60, +) class ArtifactTransform(BasicTransform): def __init__(self, motion=False, ghosting=False, spike=False, bias_field=False, blur=False, noise=False, swap=False, random_pick=False): """ diff --git a/smauglab/transforms/cpu/contrast.py b/smauglab/transforms/cpu/contrast.py index 407bc6b..ef19199 100644 --- a/smauglab/transforms/cpu/contrast.py +++ b/smauglab/transforms/cpu/contrast.py @@ -2,13 +2,53 @@ import torch import torch.nn.functional as F +from batchgeneratorsv2.helpers.scalar_type import RandomScalar from batchgeneratorsv2.transforms.base.basic_transform import ImageOnlyTransform +from batchgeneratorsv2.transforms.intensity.contrast import BGContrast +from batchgeneratorsv2.transforms.intensity.gamma import GammaTransform + +from smauglab.registry import AugId, AugType, Backend, register + + +@register( + aug_id=AugId.INV_GAMMA, + backend=Backend.CPU, + group=AugType.GE, + order=140, + param_adapters={"gamma": BGContrast}, +) +class InvertedGammaTransform(GammaTransform): + """Gamma adjustment applied to the inverted image. + + batchgeneratorsv2 expresses this as `GammaTransform(p_invert_image=1)`, which the + old config spelled as a `GammaTransform_invert` key -- a key with no class behind + it. A real class keeps the config key 1:1 with a class on this backend too, and + means the inversion cannot be requested two different ways. + """ - -class ConvTransform(ImageOnlyTransform): + def __init__( + self, + gamma: RandomScalar = (0.7, 1.5), + synchronize_channels: bool = False, + p_per_channel: float = 1, + p_retain_stats: float = 1, + ): + super().__init__( + gamma=gamma, + p_invert_image=1, + synchronize_channels=synchronize_channels, + p_per_channel=p_per_channel, + p_retain_stats=p_retain_stats, + ) + + +class _ConvBaseTransform(ImageOnlyTransform): """ Applies a Laplace/Scharr filter to the image to highlight edges. + Shared implementation. Configs address the per-kernel leaves below, which mirror + the GPU split so both backends of one augmentation share an `aug_id`. + Based on https://github.com/spinalcordtoolbox/disc-labeling-playground/blob/main/src/ply/models/transform.py """ @@ -97,6 +137,42 @@ def _apply_to_image(self, img: torch.Tensor, **params) -> torch.Tensor: return img +# One class per kernel, mirroring the GPU split so a config key names a class on +# either backend and `kernel_type` disappears from the config surface. + + +@register( + aug_id=AugId.LAPLACE, + backend=Backend.CPU, + group=AugType.TA, + order=10, +) +class LaplaceConvTransform(_ConvBaseTransform): + """Laplacian edge enhancement.""" + + def __init__(self, absolute: bool = False, retain_stats: bool = False): + super().__init__(kernel_type="Laplace", absolute=absolute, retain_stats=retain_stats) + + +@register( + aug_id=AugId.SCHARR, + backend=Backend.CPU, + group=AugType.TA, + order=15, +) +class ScharrConvTransform(_ConvBaseTransform): + """Scharr gradient-magnitude edge filter.""" + + def __init__(self, absolute: bool = True, retain_stats: bool = False): + super().__init__(kernel_type="Scharr", absolute=absolute, retain_stats=retain_stats) + + +@register( + aug_id=AugId.HISTOGRAM_EQUAL, + backend=Backend.CPU, + group=AugType.TA, + order=30, +) class HistogramEqualTransform(ImageOnlyTransform): """ Update image intensity using histogram manipulations @@ -144,7 +220,7 @@ def _apply_to_image(self, img: torch.Tensor, **params) -> torch.Tensor: return img -class FunctionTransform(ImageOnlyTransform): +class _FunctionBaseTransform(ImageOnlyTransform): """ Apply different functions to image pixels @@ -180,6 +256,90 @@ def _apply_to_image(self, img: torch.Tensor, **params) -> torch.Tensor: return img +# One class per elementwise function; `function` is not expressible in JSON, so the +# old config had a single key that the builder fanned out over a hardcoded lambda +# list. Spelled out longhand rather than as torch.log1p / torch.sigmoid, which +# differ in the last ulp and would move the seeded determinism hashes. + + +def _log1p(x: torch.Tensor) -> torch.Tensor: + return torch.log(1 + x) + + +def _sigmoid(x: torch.Tensor) -> torch.Tensor: + return 1 / (1 + torch.exp(-x)) + + +class _NamedFunctionTransform(_FunctionBaseTransform): + """Shared constructor for the fixed-function leaves. Not registered itself.""" + + #: Set by each leaf. + function_impl: staticmethod + + def __init__(self, retain_stats: bool = False): + super().__init__(function=type(self).function_impl, retain_stats=retain_stats) + + +@register( + aug_id=AugId.FUNC_LOG1P, + backend=Backend.CPU, + group=AugType.TA, + order=20, +) +class Log1pTransform(_NamedFunctionTransform): + """Apply log(1 + x).""" + + function_impl = staticmethod(_log1p) + + +@register( + aug_id=AugId.FUNC_SQRT, + backend=Backend.CPU, + group=AugType.TA, + order=21, +) +class SqrtTransform(_NamedFunctionTransform): + """Apply sqrt(x).""" + + function_impl = staticmethod(torch.sqrt) + + +@register( + aug_id=AugId.FUNC_SIN, + backend=Backend.CPU, + group=AugType.TA, + order=22, +) +class SinTransform(_NamedFunctionTransform): + """Apply sin(x).""" + + function_impl = staticmethod(torch.sin) + + +@register( + aug_id=AugId.FUNC_EXP, + backend=Backend.CPU, + group=AugType.TA, + order=23, +) +class ExpTransform(_NamedFunctionTransform): + """Apply exp(x).""" + + function_impl = staticmethod(torch.exp) + + +@register( + aug_id=AugId.FUNC_SIGMOID, + backend=Backend.CPU, + group=AugType.TA, + order=24, +) +class SigmoidTransform(_NamedFunctionTransform): + """Apply the logistic sigmoid 1 / (1 + exp(-x)).""" + + function_impl = staticmethod(_sigmoid) + + def apply_filter(x: torch.Tensor, kernel: torch.Tensor, **kwargs) -> torch.Tensor: """ Copied from https://github.com/Project-MONAI/MONAI/blob/dev/monai/networks/layers/simplelayers.py @@ -231,6 +391,12 @@ def apply_filter(x: torch.Tensor, kernel: torch.Tensor, **kwargs) -> torch.Tenso return output.view(batch, chns, *output.shape[2:]) +@register( + aug_id=AugId.ZSCORE, + backend=Backend.CPU, + group=AugType.GE, + order=170, +) class ZscoreNormalization(ImageOnlyTransform): """ Z-score normalization of image diff --git a/smauglab/transforms/cpu/external.py b/smauglab/transforms/cpu/external.py new file mode 100644 index 0000000..a040e37 --- /dev/null +++ b/smauglab/transforms/cpu/external.py @@ -0,0 +1,76 @@ +"""Registry entries for the batchgeneratorsv2 transforms the CPU pipeline composes. + +These are third-party classes, so they cannot carry an `@register` decorator; the +entries are built here instead. Everything else about them is identical to a +decorated augmentation -- same config key rules, same signature-derived parameter +validation. + +Two things differ from the GPU side and are declared per entry: + +* `wrap_random=False` for transforms that are appended directly rather than inside + a `RandomTransform(...)`. Those own no application probability, so `p` is rejected + in a config for them. +* `context_params` for values nnU-Net supplies at runtime (patch size, rotation + range). A config must not set those, and the builder injects them. +""" + +from batchgeneratorsv2.transforms.intensity.brightness import MultiplicativeBrightnessTransform +from batchgeneratorsv2.transforms.intensity.contrast import BGContrast, ContrastTransform +from batchgeneratorsv2.transforms.intensity.gamma import GammaTransform +from batchgeneratorsv2.transforms.intensity.gaussian_noise import GaussianNoiseTransform +from batchgeneratorsv2.transforms.noise.gaussian_blur import GaussianBlurTransform +from batchgeneratorsv2.transforms.spatial.low_resolution import SimulateLowResolutionTransform +from batchgeneratorsv2.transforms.spatial.mirroring import MirrorTransform +from batchgeneratorsv2.transforms.spatial.spatial import SpatialTransform + +from smauglab.registry import AugEntry, AugId, AugType, Backend, register_entry + + +def _entry(cls: type, aug_id: AugId, group: AugType, order: int, **kwargs) -> AugEntry: + return register_entry( + AugEntry( + name=cls.__name__, + cls=cls, + backend=Backend.CPU, + aug_id=aug_id, + group=group, + order=order, + summary=(cls.__doc__ or "").strip().split("\n", 1)[0], + **kwargs, + ) + ) + + +# Orders continue the sequence in AugTransforms._build_transforms, so the registry +# reproduces the pipeline the ladder builds today. +_entry( + SpatialTransform, + AugId.SPATIAL, + AugType.GEO, + 80, + wrap_random=False, + # patch_size is positional and rotation comes from nnU-Net's + # configure_rotation_dummyDA_mirroring_and_inital_patch_size. + context_params=("patch_size", "rotation"), +) +_entry(GaussianNoiseTransform, AugId.GAUSSIAN_NOISE, AugType.GE, 90) +_entry(GaussianBlurTransform, AugId.GAUSSIAN_BLUR, AugType.GE, 100) +_entry( + MultiplicativeBrightnessTransform, + AugId.BRIGHTNESS, + AugType.GE, + 110, + param_adapters={"multiplier_range": BGContrast}, +) +_entry( + ContrastTransform, + AugId.CONTRAST, + AugType.GE, + 120, + param_adapters={"contrast_range": BGContrast}, +) +_entry(SimulateLowResolutionTransform, AugId.LOW_RES, AugType.GE, 130) +_entry(GammaTransform, AugId.GAMMA, AugType.GE, 150, param_adapters={"gamma": BGContrast}) +# Appended bare, and its allowed_axes comes from the config rather than the trainer: +# AugTransforms reads transform_params["mirror_axes"], not its own mirror_axes argument. +_entry(MirrorTransform, AugId.MIRROR, AugType.GEO, 160, wrap_random=False) diff --git a/smauglab/transforms/cpu/fromSeg.py b/smauglab/transforms/cpu/fromSeg.py index c52b42c..b7624e6 100644 --- a/smauglab/transforms/cpu/fromSeg.py +++ b/smauglab/transforms/cpu/fromSeg.py @@ -5,7 +5,15 @@ from batchgeneratorsv2.transforms.base.basic_transform import BasicTransform from scipy.stats import norm +from smauglab.registry import AugId, AugType, Backend, register + +@register( + aug_id=AugId.REDISTRIBUTE_SEG, + backend=Backend.CPU, + group=AugType.TA, + order=40, +) class RedistributeTransform(BasicTransform): """ Redistribute image values using segmentation regions. diff --git a/smauglab/transforms/cpu/spatial.py b/smauglab/transforms/cpu/spatial.py index 15c6e38..c709e26 100644 --- a/smauglab/transforms/cpu/spatial.py +++ b/smauglab/transforms/cpu/spatial.py @@ -5,7 +5,15 @@ import torchio as tio from batchgeneratorsv2.transforms.base.basic_transform import BasicTransform, ImageOnlyTransform +from smauglab.registry import AugId, AugType, Backend, register + +@register( + aug_id=AugId.SPATIAL_CUSTOM, + backend=Backend.CPU, + group=AugType.GEO, + order=70, +) class SpatialCustomTransform(BasicTransform): def __init__(self, flip=False, affine=False, elastic=False, anisotropy=False, random_pick=False): """ @@ -134,6 +142,12 @@ def aug_anisotropy(img, seg, downsampling=7): ### Shape transform +@register( + aug_id=AugId.SHAPE, + backend=Backend.CPU, + group=AugType.GE, + order=50, +) class ShapeTransform(ImageOnlyTransform): def __init__(self, shape_min=1, ignore_axes=()): """ diff --git a/smauglab/transforms/cpu/transforms.py b/smauglab/transforms/cpu/transforms.py index bbfb049..0b406eb 100644 --- a/smauglab/transforms/cpu/transforms.py +++ b/smauglab/transforms/cpu/transforms.py @@ -1,29 +1,30 @@ -import json -import os +"""The CPU augmentation pipeline, built from a config section via the registry. + +The ~250-line `if` ladder this replaces also read three values from outside the +config: `patch_size` and `rotation` come from nnU-Net at runtime (declared as +`context_params` on the registry entries), a top-level `retain_stats` was pushed +into several blocks, and `mode_seg="nearest"` was hardcoded. The first is injected +by the builder; the other two were folded into the configs by `smauglab migrate`. +""" + from typing import Union import numpy as np import torch from batchgeneratorsv2.helpers.scalar_type import RandomScalar -from batchgeneratorsv2.transforms.intensity.brightness import MultiplicativeBrightnessTransform -from batchgeneratorsv2.transforms.intensity.contrast import BGContrast, ContrastTransform -from batchgeneratorsv2.transforms.intensity.gamma import GammaTransform -from batchgeneratorsv2.transforms.intensity.gaussian_noise import GaussianNoiseTransform -from batchgeneratorsv2.transforms.noise.gaussian_blur import GaussianBlurTransform -from batchgeneratorsv2.transforms.spatial.low_resolution import SimulateLowResolutionTransform -from batchgeneratorsv2.transforms.spatial.mirroring import MirrorTransform -from batchgeneratorsv2.transforms.spatial.spatial import SpatialTransform from batchgeneratorsv2.transforms.utils.compose import ComposeTransforms -from batchgeneratorsv2.transforms.utils.pseudo2d import Convert2DTo3DTransform, Convert3DTo2DTransform from batchgeneratorsv2.transforms.utils.random import RandomTransform -from smauglab.transforms.cpu.artifact import ArtifactTransform -from smauglab.transforms.cpu.contrast import ConvTransform, FunctionTransform, HistogramEqualTransform -from smauglab.transforms.cpu.fromSeg import RedistributeTransform -from smauglab.transforms.cpu.spatial import ShapeTransform, SpatialCustomTransform +from smauglab.config import load_config +from smauglab.registry import Backend +from smauglab.transforms.build import build_cpu_pipeline +from smauglab.transforms.cpu.contrast import ScharrConvTransform +from smauglab.transforms.cpu.spatial import SpatialCustomTransform class AugTransforms(ComposeTransforms): + """Dataloader-side augmentations, in registry order.""" + def __init__( self, json_path: str, @@ -32,279 +33,23 @@ def __init__( # tuples, so the one-element form was never what callers pass. patch_size: Union[np.ndarray, tuple[int, ...]], rotation_for_DA: RandomScalar, - # Accepted for signature compatibility with the nnU-Net trainers but - # unused -- the mirror axes are read from the JSON config instead, see - # _build_transforms below. Callers legitimately pass None. - mirror_axes: tuple[int, ...] | None, + # Accepted for signature compatibility with the nnU-Net trainers but unused: + # the mirror axes come from the config's MirrorTransform block, which is + # where the old builder read them from too (transform_params["mirror_axes"], + # never its own argument). + mirror_axes: tuple[int, ...] | None = None, ): - # Load transform parameters from JSON - config_path = os.path.join(json_path) - with open(config_path) as f: - config = json.load(f) - - if "CPU" in config.keys(): - self.transform_params = config["CPU"] - else: - self.transform_params = config - - self.transforms = self._build_transforms( - do_dummy_2d_data_aug=do_dummy_2d_data_aug, patch_size=patch_size, rotation_for_DA=rotation_for_DA, mirror_axes=mirror_axes + config = load_config(str(json_path)) + self.transform_params = config.section(Backend.CPU) + self.transforms = build_cpu_pipeline( + self.transform_params, + do_dummy_2d_data_aug=do_dummy_2d_data_aug, + patch_size=patch_size, + rotation=rotation_for_DA, + source=config.source, ) super().__init__(transforms=self.transforms) - def _build_transforms( - self, - do_dummy_2d_data_aug: bool, - patch_size: Union[np.ndarray, tuple[int, ...]], - rotation_for_DA: RandomScalar, - mirror_axes: tuple[int, ...] | None, - ): - transform_params = self.transform_params - transforms = [] - - # Scharr filter - conv_params = transform_params.get("ConvTransform") - if conv_params is not None: - transforms.append( - RandomTransform( - ConvTransform( - kernel_type=conv_params.get("kernel_type", "Scharr"), - absolute=conv_params.get("absolute", True), - retain_stats=transform_params.get("retain_stats", False), - ), - apply_probability=conv_params.get("probability", 0), - ) - ) - - # Apply functions - func_list = [ - lambda x: torch.log(1 + x), - torch.sqrt, - torch.sin, - torch.exp, - lambda x: 1 / (1 + torch.exp(-x)), - ] - func_params = transform_params.get("FunctionTransform") - if func_params is not None: - transforms.extend( - RandomTransform( - FunctionTransform(function=func, retain_stats=transform_params.get("retain_stats", False)), - apply_probability=func_params.get("probability", 0), - ) - for func in func_list - ) - - # Histogram manipulations - hist_params = transform_params.get("HistogramEqualTransform") - if hist_params is not None: - transforms.append( - RandomTransform( - HistogramEqualTransform(retain_stats=transform_params.get("retain_stats", False)), - apply_probability=hist_params.get("probability", 0), - ) - ) - - # Redistribute segmentation values - redist_params = transform_params.get("RedistributeTransform") - if redist_params is not None: - transforms.append( - RandomTransform( - RedistributeTransform(in_seg=redist_params.get("in_seg", 0), retain_stats=transform_params.get("retain_stats", False)), - apply_probability=redist_params.get("probability", 0), - ) - ) - - # Resolution transforms - shape_params = transform_params.get("ShapeTransform") - if shape_params is not None: - transforms.append( - RandomTransform( - ShapeTransform( - shape_min=shape_params.get("shape_min"), - ignore_axes=tuple(shape_params.get("ignore_axes", None)) - if shape_params.get("ignore_axes", None) is not None - else None, - ), - apply_probability=shape_params.get("probability", 0), - ) - ) - - # Artifacts generation - artifact_params = transform_params.get("ArtifactTransform") - if artifact_params is not None: - transforms.append( - RandomTransform( - ArtifactTransform( - motion=artifact_params.get("motion", False), - ghosting=artifact_params.get("ghosting", False), - spike=artifact_params.get("spike", False), - bias_field=artifact_params.get("bias_field", False), - blur=artifact_params.get("blur", False), - noise=artifact_params.get("noise", False), - swap=artifact_params.get("swap", False), - random_pick=artifact_params.get("random_pick", False), - ), - apply_probability=artifact_params.get("probability", 0), - ) - ) - - # Spatial transforms - spatial_custom_params = transform_params.get("SpatialCustomTransform") - if spatial_custom_params is not None: - transforms.append( - RandomTransform( - SpatialCustomTransform( - flip=spatial_custom_params.get("flip", False), - affine=spatial_custom_params.get("affine", False), - elastic=spatial_custom_params.get("elastic", False), - anisotropy=spatial_custom_params.get("anisotropy", False), - random_pick=spatial_custom_params.get("random_pick", False), - ), - apply_probability=spatial_custom_params.get("probability", 0), - ) - ) - - # Spatial nnunet transform - if do_dummy_2d_data_aug: - transforms.append(Convert3DTo2DTransform()) - patch_size_spatial = patch_size[1:] - else: - patch_size_spatial = patch_size - - spatial_params = transform_params.get("SpatialTransform") - if spatial_params is not None: - transforms.append( - SpatialTransform( - patch_size_spatial, - patch_center_dist_from_border=spatial_params.get("patch_center_dist_from_border", 0), - random_crop=spatial_params.get("random_crop", False), - p_elastic_deform=spatial_params.get("p_elastic_deform", 0), - p_rotation=spatial_params.get("p_rotation", 0), - rotation=rotation_for_DA, - p_scaling=spatial_params.get("p_scaling", 0), - scaling=spatial_params.get("scaling", (0.7, 1.4)), - p_synchronize_scaling_across_axes=spatial_params.get("p_synchronize_scaling_across_axes", 1), - bg_style_seg_sampling=spatial_params.get("bg_style_seg_sampling", False), - mode_seg="nearest", - ) - ) - - if do_dummy_2d_data_aug: - transforms.append(Convert2DTo3DTransform()) - - # Noise transforms - noise_params = transform_params.get("GaussianNoiseTransform") - if noise_params is not None: - transforms.append( - RandomTransform( - GaussianNoiseTransform( - noise_variance=tuple(noise_params.get("noise_variance", (0, 0.1))), - p_per_channel=noise_params.get("p_per_channel", 1), - synchronize_channels=noise_params.get("synchronize_channels", True), - ), - apply_probability=noise_params.get("probability", 0), - ) - ) - - # Gaussian blur - blur_params = transform_params.get("GaussianBlurTransform") - if blur_params is not None: - transforms.append( - RandomTransform( - GaussianBlurTransform( - blur_sigma=tuple(blur_params.get("blur_sigma", (0.5, 1.0))), - synchronize_channels=blur_params.get("synchronize_channels", False), - synchronize_axes=blur_params.get("synchronize_axes", False), - p_per_channel=blur_params.get("p_per_channel", 0.5), - benchmark=blur_params.get("benchmark", True), - ), - apply_probability=blur_params.get("probability", 0), - ) - ) - - # Brightness transforms - bright_params = transform_params.get("MultiplicativeBrightnessTransform") - if bright_params is not None: - transforms.append( - RandomTransform( - MultiplicativeBrightnessTransform( - multiplier_range=BGContrast(tuple(bright_params.get("multiplier_range", (0.75, 1.25)))), - synchronize_channels=bright_params.get("synchronize_channels", False), - p_per_channel=bright_params.get("p_per_channel", 1), - ), - apply_probability=bright_params.get("probability", 0), - ) - ) - - # Contrast transforms - contrast_params = transform_params.get("ContrastTransform") - if contrast_params is not None: - transforms.append( - RandomTransform( - ContrastTransform( - contrast_range=BGContrast(tuple(contrast_params.get("contrast_range", (0.75, 1.25)))), - preserve_range=contrast_params.get("preserve_range", True), - synchronize_channels=contrast_params.get("synchronize_channels", False), - p_per_channel=contrast_params.get("p_per_channel", 1), - ), - apply_probability=contrast_params.get("probability", 0), - ) - ) - - # Simulate low resolution - lowres_params = transform_params.get("SimulateLowResolutionTransform") - if lowres_params is not None: - transforms.append( - RandomTransform( - SimulateLowResolutionTransform( - scale=tuple(lowres_params.get("scale", (0.3, 1))), - synchronize_channels=lowres_params.get("synchronize_channels", True), - synchronize_axes=lowres_params.get("synchronize_axes", False), - ignore_axes=tuple(lowres_params.get("ignore_axes", ())), - allowed_channels=lowres_params.get("allowed_channels", None), - p_per_channel=lowres_params.get("p_per_channel", 0.5), - ), - apply_probability=lowres_params.get("probability", 0), - ) - ) - - # Gamma transforms - gamma_inv_params = transform_params.get("GammaTransform_invert") - if gamma_inv_params is not None: - transforms.append( - RandomTransform( - GammaTransform( - gamma=BGContrast(tuple(gamma_inv_params.get("gamma", (0.7, 1.5)))), - p_invert_image=gamma_inv_params.get("p_invert_image", 1), - synchronize_channels=gamma_inv_params.get("synchronize_channels", False), - p_per_channel=gamma_inv_params.get("p_per_channel", 1), - p_retain_stats=gamma_inv_params.get("p_retain_stats", 1), - ), - apply_probability=gamma_inv_params.get("probability", 0), - ) - ) - - gamma_params = transform_params.get("GammaTransform") - if gamma_params is not None: - transforms.append( - RandomTransform( - GammaTransform( - gamma=BGContrast(tuple(gamma_params.get("gamma", (0.7, 1.5)))), - p_invert_image=gamma_params.get("p_invert_image", 0), - synchronize_channels=gamma_params.get("synchronize_channels", False), - p_per_channel=gamma_params.get("p_per_channel", 1), - p_retain_stats=gamma_params.get("p_retain_stats", 1), - ), - apply_probability=gamma_params.get("probability", 0), - ) - ) - - # Mirroring transforms - if transform_params.get("mirror_axes") is not None and len(transform_params["mirror_axes"]) > 0: - transforms.append(MirrorTransform(allowed_axes=transform_params.get("mirror_axes"))) - - return transforms - class AugTransformsTest(ComposeTransforms): def __init__(self): @@ -317,10 +62,7 @@ def _build_transforms(self): # Scharr filter transforms.append( RandomTransform( - ConvTransform( - kernel_type="Scharr", - absolute=True, - ), + ScharrConvTransform(absolute=True), apply_probability=0.9, ) ) diff --git a/smauglab/transforms/gpu/__init__.py b/smauglab/transforms/gpu/__init__.py new file mode 100644 index 0000000..187f729 --- /dev/null +++ b/smauglab/transforms/gpu/__init__.py @@ -0,0 +1 @@ +"""GPU augmentations, built on kornia's 3D augmentation base classes.""" diff --git a/smauglab/transforms/gpu/contrast.py b/smauglab/transforms/gpu/contrast.py index 832c6fd..c51a8f8 100644 --- a/smauglab/transforms/gpu/contrast.py +++ b/smauglab/transforms/gpu/contrast.py @@ -1,6 +1,6 @@ import math import random -from collections.abc import Callable +from collections.abc import Callable, Sequence from typing import Any, Union import torch @@ -8,6 +8,7 @@ from torch import Tensor from torch.nn import functional as F +from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform @@ -97,16 +98,20 @@ def _apply_region_mode( ## Convolution transform -class RandomConvTransformGPU(ImageOnlyTransform): +class _RandomConvBaseGPU(ImageOnlyTransform): """Apply convolution to image. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. Based on https://docs.pytorch.org/vision/0.9/transforms.html#torchvision.transforms.GaussianBlur Args: - kernel_type (str): Type of convolution kernel, either 'Laplace' or 'Scharr'. Default is 'Laplace'. - spatial_dims (int): Number of spatial dimensions of the input image, either 2 or 3. Default is 2. - absolute (bool): If True, take the absolute value of the convolution result. Default is False. - retain_stats (bool): If True, retain the original mean and standard deviation of the image after convolution. Default is False. + kernel_type (str): One of 'Laplace', 'Scharr', 'GaussianBlur', 'UnsharpMask', 'RandConv'. + apply_to_channel (list of int): Channel indices to convolve. Default is [0]. + absolute (bool): If True, take the absolute value of the result. Scharr only. + sigma (float): Gaussian width. GaussianBlur and UnsharpMask only. + unsharp_amount (float): Strength of the unsharp mask. UnsharpMask only. + kernel_sizes (list of int): Multi-scale kernel sizes to draw from. RandConv only. + mix_prob (float): Probability of blending the result back with the original. + retain_stats (bool): If True, restore the original mean and std afterwards. Returns: Tensor: Convolved version of the input image. @@ -116,35 +121,40 @@ class RandomConvTransformGPU(ImageOnlyTransform): def __init__( self, kernel_type: str = "Laplace", - apply_to_channel: list[int] | None = None, # Apply to first channel by default + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default same_on_batch: bool = False, retain_stats: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, + # Kernel-specific. These used to be read out of **kwargs, which meant they + # were invisible to `inspect.signature` and a typo in a config silently + # selected the default instead. Defaults here are the historical + # kwargs.get() ones, so behaviour is unchanged. + absolute: bool = False, + sigma: float = 1.0, + unsharp_amount: float = 1.0, + kernel_sizes: Sequence[int] = (1, 3, 5, 7), + mix_prob: float = 0.0, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) if kernel_type not in ["Laplace", "Scharr", "GaussianBlur", "UnsharpMask", "RandConv"]: raise NotImplementedError('Currently only "Laplace", "Scharr", "GaussianBlur", "UnsharpMask" and "RandConv" are supported.') else: self.kernel_type = kernel_type self.apply_to_channel = apply_to_channel - self.absolute = kwargs.get("absolute", False) - self.sigma = kwargs.get("sigma", 1.0) + self.absolute = absolute + self.sigma = sigma self.retain_stats = retain_stats self.in_seg = in_seg self.out_seg = out_seg self.mix_in_out = mix_in_out - # Unsharp mask parameters: amount controls strength of the mask - self.unsharp_amount = kwargs.get("unsharp_amount", 1.0) - # RandConv parameters - self.kernel_sizes = kwargs.get("kernel_sizes", [1, 3, 5, 7]) # multi-scale default - self.mix_prob = kwargs.get("mix_prob", 0.0) # probability to mix with original + self.unsharp_amount = unsharp_amount + self.kernel_sizes = kernel_sizes + self.mix_prob = mix_prob def get_kernel(self, device: torch.device) -> Union[Tensor, list[Tensor]]: # Scharr is the odd one out: it returns the three directional kernels as a @@ -279,7 +289,6 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ if seg_mask is not None: region_mode = _choose_region_mode(self.in_seg, self.out_seg, seg_mask) x = _apply_region_mode(orig, x, seg_mask, region_mode, mix_in_out=self.mix_in_out) - # Final safety: check if nan/inf appeared if torch.isnan(x).any() or torch.isinf(x).any(): print(f"Warning nan: {self.__class__.__name__} with kernel={self.kernel_type}", flush=True) @@ -289,6 +298,216 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return input +# One class per convolution kernel. +# +# These used to be a single `kernel_type=` argument on the base, which meant four +# different augmentations shared one config key and every config had to repeat the +# kernel name redundantly. A class each keeps the config key 1:1 with the class, +# lets each expose only the parameters its kernel actually reads, and makes the +# CPU/GPU coverage matrix able to tell them apart. +# +# Defaults below are the values the old `_build_transforms` ladder passed for that +# kernel, NOT the base class defaults -- that is what keeps behaviour identical once +# the ladder is gone. + + +@register( + aug_id=AugId.LAPLACE, + backend=Backend.GPU, + group=AugType.TA, + order=115, +) +class RandomLaplaceGPU(_RandomConvBaseGPU): + """Laplacian edge enhancement.""" + + def __init__( + self, + absolute: bool = False, + mix_prob: float = 0.0, + apply_to_channel: Sequence[int] = (0,), + same_on_batch: bool = False, + retain_stats: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = True, + ) -> None: + super().__init__( + kernel_type="Laplace", + absolute=absolute, + mix_prob=mix_prob, + apply_to_channel=apply_to_channel, + same_on_batch=same_on_batch, + retain_stats=retain_stats, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + +@register( + aug_id=AugId.SCHARR, + backend=Backend.GPU, + group=AugType.TA, + order=90, +) +class RandomScharrGPU(_RandomConvBaseGPU): + """Scharr gradient-magnitude edge filter.""" + + def __init__( + self, + absolute: bool = True, + retain_stats: bool = True, + mix_prob: float = 0.0, + apply_to_channel: Sequence[int] = (0,), + same_on_batch: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = True, + ) -> None: + super().__init__( + kernel_type="Scharr", + absolute=absolute, + retain_stats=retain_stats, + mix_prob=mix_prob, + apply_to_channel=apply_to_channel, + same_on_batch=same_on_batch, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + +@register( + aug_id=AugId.GAUSSIAN_BLUR, + backend=Backend.GPU, + group=AugType.GE, + order=140, +) +class RandomGaussianBlurGPU(_RandomConvBaseGPU): + """Gaussian blur via separable convolution.""" + + def __init__( + self, + sigma: float = 1.0, + apply_to_channel: Sequence[int] = (0,), + same_on_batch: bool = False, + retain_stats: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + mix_prob: float = 0.0, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = True, + ) -> None: + super().__init__( + kernel_type="GaussianBlur", + sigma=sigma, + apply_to_channel=apply_to_channel, + same_on_batch=same_on_batch, + retain_stats=retain_stats, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + mix_prob=mix_prob, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + +@register( + aug_id=AugId.UNSHARP_MASK, + backend=Backend.GPU, + group=AugType.TA, + order=100, +) +class RandomUnsharpMaskGPU(_RandomConvBaseGPU): + """Unsharp masking: sharpen by subtracting a blurred copy.""" + + def __init__( + self, + sigma: float = 1.0, + unsharp_amount: float = 1.5, + mix_prob: float = 0.0, + apply_to_channel: Sequence[int] = (0,), + same_on_batch: bool = False, + retain_stats: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = True, + ) -> None: + super().__init__( + kernel_type="UnsharpMask", + sigma=sigma, + unsharp_amount=unsharp_amount, + mix_prob=mix_prob, + apply_to_channel=apply_to_channel, + same_on_batch=same_on_batch, + retain_stats=retain_stats, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + +@register( + aug_id=AugId.RAND_CONV, + backend=Backend.GPU, + group=AugType.TA, + order=110, +) +class RandomRandConvGPU(_RandomConvBaseGPU): + """RandConv: convolution with a randomly drawn multi-scale kernel.""" + + def __init__( + self, + kernel_sizes: Sequence[int] = (1, 3, 5, 7), + mix_prob: float = 0.0, + apply_to_channel: Sequence[int] = (0,), + same_on_batch: bool = False, + retain_stats: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = True, + ) -> None: + super().__init__( + kernel_type="RandConv", + kernel_sizes=kernel_sizes, + mix_prob=mix_prob, + apply_to_channel=apply_to_channel, + same_on_batch=same_on_batch, + retain_stats=retain_stats, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + def apply_convolution(img: torch.Tensor, kernel: torch.Tensor, dim: int) -> torch.Tensor: """ Based on https://github.com/pytorch/vision/blob/e3b5d3a8bf5e8636462fd8bce9897bccc690b2a0/torchvision/transforms/_functional_tensor.py#L746 @@ -363,6 +582,12 @@ def get_gaussian_kernel3d(kernel_size: int, sigma: Union[float, Tensor], dtype: ## Noise transform +@register( + aug_id=AugId.GAUSSIAN_NOISE, + backend=Backend.GPU, + group=AugType.GE, + order=130, +) class RandomGaussianNoiseGPU(ImageOnlyTransform): """Add random Gaussian noise to image. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -381,19 +606,17 @@ class RandomGaussianNoiseGPU(ImageOnlyTransform): def __init__( self, mean: float = 0.0, - std: float = 0.1, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + std: float = 1.0, + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default same_on_batch: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.apply_to_channel = apply_to_channel self.mean = mean self.std = std @@ -421,7 +644,6 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ if seg_mask is not None: region_mode = _choose_region_mode(self.in_seg, self.out_seg, seg_mask) x = _apply_region_mode(orig, x, seg_mask, region_mode, mix_in_out=self.mix_in_out) - # Final safety: check if nan/inf appeared if torch.isnan(x).any() or torch.isinf(x).any(): print(f"Warning nan: {self.__class__.__name__}", flush=True) @@ -432,6 +654,12 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ ## Multiplicative brightness transform +@register( + aug_id=AugId.BRIGHTNESS, + backend=Backend.GPU, + group=AugType.GE, + order=150, +) class RandomBrightnessGPU(ImageOnlyTransform): """Apply random brightness adjustment to image. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -449,19 +677,17 @@ class RandomBrightnessGPU(ImageOnlyTransform): def __init__( self, - brightness_range: tuple[float, float] = (0.9, 1.1), - apply_to_channel: list[int] | None = None, # Apply to first channel by default + brightness_range: tuple[float, float] = (0.5, 1.5), + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default same_on_batch: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.brightness_range = brightness_range self.apply_to_channel = apply_to_channel self.in_seg = in_seg @@ -504,12 +730,12 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ ## Gamma transform -class RandomGammaGPU(ImageOnlyTransform): +class _RandomGammaBaseGPU(ImageOnlyTransform): """Apply random gamma adjustment to image. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. Args: - gamma_range (tuple of float): Range of gamma multipliers. Default is (0.9, 1.1). + gamma_range (tuple of float): Range of gamma multipliers. Default is (0.7, 1.5). invert_image (bool): If True, invert the image before and after gamma adjustment. Default is False. apply_to_channel (list of int): List of channel indices to apply the gamma adjustment to. Default is [0]. retain_stats (bool): If True, retain the original mean and standard deviation of the image after gamma adjustment. Default is False. @@ -523,21 +749,19 @@ class RandomGammaGPU(ImageOnlyTransform): def __init__( self, - gamma_range: tuple[float, float] = (0.9, 1.1), + gamma_range: tuple[float, float] = (0.7, 1.5), invert_image: bool = False, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default retain_stats: bool = False, same_on_batch: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = False, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.gamma_range = gamma_range self.invert_image = invert_image self.retain_stats = retain_stats @@ -621,7 +845,92 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return input +# Gamma, split so that "gamma" and "inverted gamma" are two config keys rather than +# one key plus an `invert_image` flag. Neither leaf exposes the flag, so a config +# cannot express the same augmentation two ways. + + +@register( + aug_id=AugId.GAMMA, + backend=Backend.GPU, + group=AugType.GE, + order=160, +) +class RandomGammaGPU(_RandomGammaBaseGPU): + """Random gamma adjustment.""" + + def __init__( + self, + gamma_range: tuple[float, float] = (0.7, 1.5), + apply_to_channel: Sequence[int] = (0,), + retain_stats: bool = False, + same_on_batch: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = False, + ) -> None: + super().__init__( + gamma_range=gamma_range, + invert_image=False, + apply_to_channel=apply_to_channel, + retain_stats=retain_stats, + same_on_batch=same_on_batch, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + +@register( + aug_id=AugId.INV_GAMMA, + backend=Backend.GPU, + group=AugType.GE, + order=170, +) +class RandomInvGammaGPU(_RandomGammaBaseGPU): + """Random gamma adjustment applied to the inverted image.""" + + def __init__( + self, + gamma_range: tuple[float, float] = (0.7, 1.5), + apply_to_channel: Sequence[int] = (0,), + retain_stats: bool = False, + same_on_batch: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = False, + ) -> None: + super().__init__( + gamma_range=gamma_range, + invert_image=True, + apply_to_channel=apply_to_channel, + retain_stats=retain_stats, + same_on_batch=same_on_batch, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + ## nnunetv2 contrast transform +@register( + aug_id=AugId.CONTRAST, + backend=Backend.GPU, + group=AugType.GE, + order=180, +) class RandomContrastGPU(ImageOnlyTransform): """Apply random gamma adjustment to image. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -640,20 +949,18 @@ class RandomContrastGPU(ImageOnlyTransform): def __init__( self, - contrast_range: tuple[float, float] = (0.9, 1.1), - apply_to_channel: list[int] | None = None, # Apply to first channel by default + contrast_range: tuple[float, float] = (0.75, 1.25), + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default retain_stats: bool = False, same_on_batch: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.contrast_range = contrast_range self.apply_to_channel = apply_to_channel self.retain_stats = retain_stats @@ -720,7 +1027,7 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ ## Function transform -class RandomFunctionGPU(ImageOnlyTransform): +class _RandomFunctionBaseGPU(ImageOnlyTransform): """Apply function to the image based on probability. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -739,19 +1046,17 @@ class RandomFunctionGPU(ImageOnlyTransform): def __init__( self, func: Callable[[Tensor], Tensor] = lambda x: x**2, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default retain_stats: bool = False, same_on_batch: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.func = func self.retain_stats = retain_stats self.apply_to_channel = apply_to_channel @@ -804,7 +1109,125 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return input +# One class per elementwise function. +# +# `func` was a callable parameter, which no JSON config could ever express -- the old +# ladder worked around that by expanding a single "FunctionTransform" block into five +# transforms from a hardcoded lambda list. A class each makes every one addressable +# from a config, and removes the un-serialisable parameter entirely. +# +# Written out longhand rather than as torch.log1p / torch.sigmoid on purpose: those +# differ from the originals in the last ulp, which is enough to move the seeded +# determinism hashes and invalidate every published experiment. + + +def _log1p(x: Tensor) -> Tensor: + return torch.log(1 + x) + + +def _sigmoid(x: Tensor) -> Tensor: + return 1 / (1 + torch.exp(-x)) + + +class _RandomNamedFunctionGPU(_RandomFunctionBaseGPU): + """Shared constructor for the fixed-function leaves. Not registered itself.""" + + #: Set by each leaf; `func` is therefore absent from the config surface. + function: staticmethod + + def __init__( + self, + apply_to_channel: Sequence[int] = (0,), + retain_stats: bool = False, + same_on_batch: bool = False, + in_seg: float = 0.0, + out_seg: float = 0.0, + mix_in_out: bool = False, + p: float = 1.0, + p_batch: float = 1.0, + keepdim: bool = True, + ) -> None: + super().__init__( + func=type(self).function, + apply_to_channel=apply_to_channel, + retain_stats=retain_stats, + same_on_batch=same_on_batch, + in_seg=in_seg, + out_seg=out_seg, + mix_in_out=mix_in_out, + p=p, + p_batch=p_batch, + keepdim=keepdim, + ) + + +@register( + aug_id=AugId.FUNC_LOG1P, + backend=Backend.GPU, + group=AugType.TA, + order=190, +) +class RandomLog1pGPU(_RandomNamedFunctionGPU): + """Apply log(1 + x).""" + + function = staticmethod(_log1p) + + +@register( + aug_id=AugId.FUNC_SQRT, + backend=Backend.GPU, + group=AugType.TA, + order=191, +) +class RandomSqrtGPU(_RandomNamedFunctionGPU): + """Apply sqrt(x).""" + + function = staticmethod(torch.sqrt) + + +@register( + aug_id=AugId.FUNC_SIN, + backend=Backend.GPU, + group=AugType.TA, + order=192, +) +class RandomSinGPU(_RandomNamedFunctionGPU): + """Apply sin(x).""" + + function = staticmethod(torch.sin) + + +@register( + aug_id=AugId.FUNC_EXP, + backend=Backend.GPU, + group=AugType.TA, + order=193, +) +class RandomExpGPU(_RandomNamedFunctionGPU): + """Apply exp(x).""" + + function = staticmethod(torch.exp) + + +@register( + aug_id=AugId.FUNC_SIGMOID, + backend=Backend.GPU, + group=AugType.TA, + order=194, +) +class RandomSigmoidGPU(_RandomNamedFunctionGPU): + """Apply the logistic sigmoid 1 / (1 + exp(-x)).""" + + function = staticmethod(_sigmoid) + + ## Inverse transform +@register( + aug_id=AugId.INVERSE, + backend=Backend.GPU, + group=AugType.TA, + order=60, +) class RandomInverseGPU(ImageOnlyTransform): """Inverse image based on probability. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -821,7 +1244,7 @@ class RandomInverseGPU(ImageOnlyTransform): def __init__( self, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default retain_stats: bool = False, same_on_batch: bool = False, in_seg: float = 0.0, @@ -829,12 +1252,10 @@ def __init__( mix_in_out: bool = False, mix_prob: float = 0.0, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.apply_to_channel = apply_to_channel self.retain_stats = retain_stats self.in_seg = in_seg @@ -882,6 +1303,12 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ ## Histogram transform +@register( + aug_id=AugId.HISTOGRAM_EQUAL, + backend=Backend.GPU, + group=AugType.TA, + order=70, +) class RandomHistogramEqualizationGPU(ImageOnlyTransform): """Apply histogram equalization transformation to the image based on probability. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -899,7 +1326,7 @@ class RandomHistogramEqualizationGPU(ImageOnlyTransform): def __init__( self, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default retain_stats: bool = False, same_on_batch: bool = False, in_seg: float = 0.0, @@ -907,12 +1334,10 @@ def __init__( mix_in_out: bool = False, mix_prob: float = 0.0, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.retain_stats = retain_stats self.apply_to_channel = apply_to_channel self.in_seg = in_seg @@ -992,6 +1417,12 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return input +@register( + aug_id=AugId.BIAS_FIELD, + backend=Backend.GPU, + group=AugType.TA, + order=230, +) class RandomBiasFieldGPU(ImageOnlyTransform): """Apply a smooth multiplicative bias field to selected channels. @@ -1017,7 +1448,7 @@ def __init__( self, coefficients: Union[float, tuple[float, float]] = 0.5, order: int = 3, - apply_to_channel: list[int] | None = None, + apply_to_channel: Sequence[int] = (0,), invert: bool = False, retain_stats: bool = False, in_seg: float = 0.0, @@ -1025,12 +1456,10 @@ def __init__( mix_in_out: bool = False, same_on_batch: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) if isinstance(coefficients, (int, float)): self.coeff_range = (-float(coefficients), float(coefficients)) elif isinstance(coefficients, (tuple, list)) and len(coefficients) == 2: @@ -1179,6 +1608,12 @@ def apply_transform( # Random clamping transform +@register( + aug_id=AugId.CLAMP, + backend=Backend.GPU, + group=AugType.GE, + order=120, +) class RandomClampGPU(ImageOnlyTransform): """Apply random gamma adjustment to image. If the image is torch Tensor, it is expected to have [N, C, X, Y] or [N, C, X, Y, Z] shape. @@ -1197,20 +1632,18 @@ class RandomClampGPU(ImageOnlyTransform): def __init__( self, - max_clamp_amount: float = 0.2, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + max_clamp_amount: float = 0.0, + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default retain_stats: bool = False, same_on_batch: bool = False, in_seg: float = 0.0, out_seg: float = 0.0, mix_in_out: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.max_clamp_amount = max_clamp_amount self.apply_to_channel = apply_to_channel self.retain_stats = retain_stats @@ -1274,6 +1707,12 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return input +@register( + aug_id=AugId.ZSCORE, + backend=Backend.GPU, + group=AugType.GE, + order=240, +) class ZscoreNormalizationGPU(ImageOnlyTransform): """Apply z-score normalization to selected channels. @@ -1285,16 +1724,14 @@ class ZscoreNormalizationGPU(ImageOnlyTransform): def __init__( self, - apply_to_channel: list[int] | None = None, + apply_to_channel: Sequence[int] = (0,), keepdim: bool = True, in_seg: float = 0.0, out_seg: float = 0.0, p: float = 1.0, - **kwargs, + p_batch: float = 1.0, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=False, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=False, keepdim=keepdim) self.apply_to_channel = apply_to_channel self.in_seg = in_seg self.out_seg = out_seg diff --git a/smauglab/transforms/gpu/domain_transfer.py b/smauglab/transforms/gpu/domain_transfer.py index c1927ff..c1e2155 100644 --- a/smauglab/transforms/gpu/domain_transfer.py +++ b/smauglab/transforms/gpu/domain_transfer.py @@ -35,6 +35,8 @@ """ import math +import os +from collections.abc import Sequence from typing import Any import numpy as np @@ -43,10 +45,33 @@ from torch.distributions import Dirichlet from torch.nn import functional as F +from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform -# Default transfer LUT bank (built by embeddaug/analysis/playground/build_transfer_bank.py). -DEFAULT_BANK_PATH = "/DATA/NAS/ongoing_projects/hendrik/nathan-transferaug/embeddaug/analysis/playground/results/domain_transfer_bank.npz" +# The transfer LUT bank is a multi-hundred-MB artefact built offline by +# embeddaug/analysis/playground/build_transfer_bank.py, so it is not shipped in the +# wheel. Point this env var at it; there is deliberately no baked-in default, because +# the previous one was an absolute path into a single machine's NAS home directory and +# silently made this transform unusable for everyone else. +BANK_PATH_ENV_VAR = "SMAUGLAB_DOMAIN_BANK" + + +def resolve_bank_path(bank_path: str | None = None) -> str: + """Locate the domain-transfer LUT bank, explicit argument first, then the env var. + + Raises with the fix spelled out rather than letting np.load report a bare + FileNotFoundError on a path the caller never chose. + """ + resolved = bank_path or os.environ.get(BANK_PATH_ENV_VAR) + if not resolved: + raise FileNotFoundError( + "RandomDomainTransferGPU needs a transfer LUT bank. Pass bank_path=..., set it in " + f"the config, or export {BANK_PATH_ENV_VAR}=/path/to/domain_transfer_bank.npz " + "(built by embeddaug/analysis/playground/build_transfer_bank.py)." + ) + if not os.path.isfile(resolved): + raise FileNotFoundError(f"Domain transfer bank not found at {resolved!r} (from {BANK_PATH_ENV_VAR} or bank_path).") + return resolved def _gaussian_kernel1d(sigma: float, device, dtype) -> torch.Tensor: @@ -107,6 +132,14 @@ def _random_smooth_field01(shape, scale: float, gain: float, device, dtype) -> t return torch.sigmoid(gain * field + offset) +@register( + aug_id=AugId.DOMAIN_TRANSFER, + backend=Backend.GPU, + group=AugType.TA, + order=50, + external_asset=BANK_PATH_ENV_VAR, + smoke_kwargs={"any_source": True}, +) class RandomDomainTransferGPU(ImageOnlyTransform): """Randomly transfer an image's appearance to another sequence/cluster (see module docstring).""" @@ -115,10 +148,10 @@ def __init__( bank_path: str | None = None, source_label: str | None = None, targets: list[str] | None = None, - include_self: bool = True, + include_self: bool = False, any_source: bool = False, sigma: float = 2.0, - apply_to_channel: list[int] | None = None, + apply_to_channel: Sequence[int] = (0,), zscore_io: str = "auto", pct: float = 1.0, blend_targets: int = 1, @@ -131,14 +164,11 @@ def __init__( spatial_mix_gain: float = 3.0, same_on_batch: bool = False, p: float = 0.2, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) - bank_path = bank_path or DEFAULT_BANK_PATH - data = np.load(bank_path, allow_pickle=True) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) + data = np.load(resolve_bank_path(bank_path), allow_pickle=True) self.labels: list[str] = [str(x) for x in data["labels"].tolist()] self.L = int(data["L"]) self.num_classes = int(data["num_classes"]) diff --git a/smauglab/transforms/gpu/fromSeg.py b/smauglab/transforms/gpu/fromSeg.py index 5afdf9b..37c1e00 100644 --- a/smauglab/transforms/gpu/fromSeg.py +++ b/smauglab/transforms/gpu/fromSeg.py @@ -1,4 +1,5 @@ import random +from collections.abc import Sequence from typing import Any import torch @@ -6,6 +7,7 @@ from torch import Tensor, nn from torch.nn import functional as F +from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform # ── PALETTE AUG helpers ────────────────────────────────────────────────── @@ -45,7 +47,7 @@ def _voronoi_region_ids( fg: torch.Tensor, C: int, device: torch.device, - s_choices: list[int], + s_choices: Sequence[int], skip_sub_parc_prob: float, ) -> tuple[torch.Tensor, int]: """Spatially subdivide each K-means cluster into Voronoi sub-regions. @@ -90,6 +92,12 @@ def _normal_pdf(x: torch.Tensor, mean: torch.Tensor, std: torch.Tensor) -> torch ## Redistribute segmentation values transform (GPU) +@register( + aug_id=AugId.REDISTRIBUTE_SEG, + backend=Backend.GPU, + group=AugType.TA, + order=80, +) class RandomRedistributeSegGPU(ImageOnlyTransform): """Redistribute image values using segmentation regions (GPU version). @@ -100,22 +108,16 @@ class RandomRedistributeSegGPU(ImageOnlyTransform): def __init__( self, in_seg: float = 0.2, - apply_to_channel: list[int] | None = None, + apply_to_channel: Sequence[int] = (0,), retain_stats: bool = False, same_on_batch: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - std_noise_range: list[float] | None = None, - dilation_iterations_range: list[int] | None = None, - **kwargs, + std_noise_range: Sequence[float] = (0.1, 0.3), + dilation_iterations_range: Sequence[int] = (1, 3), ) -> None: - if dilation_iterations_range is None: - dilation_iterations_range = [1, 3] - if std_noise_range is None: - std_noise_range = [0.1, 0.3] - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.in_seg = in_seg self.apply_to_channel = apply_to_channel self.retain_stats = retain_stats @@ -265,7 +267,13 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return input -class RandomPALETTEGPU(ImageOnlyTransform): +@register( + aug_id=AugId.PALETTE, + backend=Backend.GPU, + group=AugType.TA, + order=40, +) +class RandomPaletteGPU(ImageOnlyTransform): """ SmaugLab GPU augmentation implementing PALETTE synthesis. @@ -301,29 +309,27 @@ class RandomPALETTEGPU(ImageOnlyTransform): def __init__( self, - c_choices: list[int] | None = None, - s_choices: list[int] | None = None, - blur_sigmas: list[float] | None = None, + c_choices: Sequence[int] = (2, 3, 4, 5, 6), + s_choices: Sequence[int] = (2, 3, 4, 5, 6, 7, 8, 9, 10), + blur_sigmas: Sequence[float] = (0.0, 0.0, 0.0, 0.3, 0.5, 0.8), dark_threshold: float = 0.01, n_kmeans_subsample: int = 10_000, skip_parcellation_prob: float = 0.10, skip_sub_parc_prob: float = 0.40, - alpha_magnitude_range: list[float] | None = None, + alpha_magnitude_range: Sequence[float] = (0.5, 2.0), label_remap_prob: float = 0.5, min_label_voxels: int = 4, label_classes: list[int] | None = None, p: float = 1.0, - **kwargs: Any, + p_batch: float = 1.0, + same_on_batch: bool = False, + # Note the default is False, not the True its siblings use. This class + # previously forwarded **kwargs straight to super(), so keepdim fell through + # to kornia's own default -- and nothing ever passed it. Spelling that out + # rather than "fixing" it keeps the transform behaving exactly as before. + keepdim: bool = False, ) -> None: - if alpha_magnitude_range is None: - alpha_magnitude_range = [0.5, 2.0] - if blur_sigmas is None: - blur_sigmas = [0.0, 0.0, 0.0, 0.3, 0.5, 0.8] - if s_choices is None: - s_choices = [2, 3, 4, 5, 6, 7, 8, 9, 10] - if c_choices is None: - c_choices = [2, 3, 4, 5, 6] - super().__init__(p=p, **kwargs) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.c_choices = c_choices self.s_choices = s_choices self.blur_sigmas = blur_sigmas diff --git a/smauglab/transforms/gpu/spatial.py b/smauglab/transforms/gpu/spatial.py index 5ac9e5d..7c4df53 100644 --- a/smauglab/transforms/gpu/spatial.py +++ b/smauglab/transforms/gpu/spatial.py @@ -18,11 +18,18 @@ # the kornia-compat matrix in tests.yml exercises, so it stays. from kornia.core.utils import _extract_device_dtype # type: ignore[no-redef] +from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform # Affine transform -class RandomAffine3DCustom(RigidAffineAugmentationBase3D): +@register( + aug_id=AugId.AFFINE, + backend=Backend.GPU, + group=AugType.GEO, + order=20, +) +class RandomAffineGPU(RigidAffineAugmentationBase3D): r"""Apply affine transformation 3D volumes (5D tensor). Based on :class:`kornia.augmentation.RandomAffine3D`. @@ -107,9 +114,9 @@ def __init__( tuple[float, float], tuple[float, float, float], tuple[tuple[float, float], tuple[float, float], tuple[float, float]], - ], - translate: Union[Tensor, tuple[float, float, float]] | None = None, - scale: Union[Tensor, tuple[float, float], tuple[tuple[float, float], tuple[float, float], tuple[float, float]]] | None = None, + ] = 10, + translate: Union[Tensor, tuple[float, float, float]] | None = (0.1, 0.1, 0.1), + scale: Union[Tensor, tuple[float, float], tuple[tuple[float, float], tuple[float, float], tuple[float, float]]] | None = (0.9, 1.1), shears: Union[ Tensor, float, @@ -124,14 +131,15 @@ def __init__( tuple[float, float], ], None, - ] = None, + ] = (-10, 10, -10, 10, -10, 10), resample: Union[str, int, Resample] = Resample.BILINEAR.name, same_on_batch: bool = False, align_corners: bool = True, p: float = 0.5, + p_batch: float = 1.0, keepdim: bool = True, ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.degrees = degrees self.shears = shears self.translate = translate @@ -198,6 +206,13 @@ def apply_transform_mask( # Low resolution transform +@register( + aug_id=AugId.LOW_RES, + backend=Backend.GPU, + group=AugType.GE, + order=200, + force_sequential=True, +) class RandomLowResTransformGPU(RigidAffineAugmentationBase3D): """ Apply low resolution simulation to 3D volumes (5D tensor). @@ -208,10 +223,10 @@ def __init__( scale: tuple[float, float] = (0.3, 1.0), same_on_batch: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self._param_generator = ScaleGenerator3D(scale=scale) def compute_transformation(self, input: Tensor, params: dict[str, Tensor], flags: dict[str, Any]) -> Tensor: @@ -326,6 +341,12 @@ def forward(self, batch_shape: tuple[int, ...], same_on_batch: bool = False) -> # Acquisition transforms +@register( + aug_id=AugId.ACQ, + backend=Backend.GPU, + group=AugType.GE, + order=210, +) class RandomAcqTransformGPU(ImageOnlyTransform): """ Randomly lower acquisition along one axes only. @@ -334,19 +355,19 @@ class RandomAcqTransformGPU(ImageOnlyTransform): def __init__( self, scale: tuple[float, float] = (0.3, 1.0), - one_dim: bool = False, same_on_batch: bool = False, - apply_to_channel: list[int] | None = None, # Apply to first channel by default + apply_to_channel: Sequence[int] = (0,), # Apply to first channel by default p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - if apply_to_channel is None: - apply_to_channel = [0] - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.flags = {"resample": "trilinear"} self.apply_to_channel = apply_to_channel - self._param_generator = ScaleGenerator3D(scale=scale, one_dim=one_dim) + # one_dim is fixed rather than exposed: this class *is* the single-axis case, + # and RandomLowResTransformGPU is the isotropic one. Leaving it configurable + # meant two config keys could each produce either behaviour. + self._param_generator = ScaleGenerator3D(scale=scale, one_dim=True) @torch.no_grad() def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[str, Any], transform: Tensor | None = None) -> Tensor: @@ -412,6 +433,12 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ # Flip transforms +@register( + aug_id=AugId.FLIP, + backend=Backend.GPU, + group=AugType.GEO, + order=10, +) class RandomFlipTransformGPU(RigidAffineAugmentationBase3D): """ Apply low resolution simulation to 3D volumes (5D tensor). @@ -421,13 +448,13 @@ def __init__( self, # Both forms are accepted and normalised below; the annotation said `int` # while the default was a list and every caller passes a list. - flip_axis: Union[int, Sequence[int]] = [0, 1, 2], + flip_axis: Union[int, Sequence[int]] = (0,), same_on_batch: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) # normalize flip_axis into a list of ints if isinstance(flip_axis, int): self.flip_axis = [flip_axis] @@ -534,6 +561,12 @@ def forward(self, batch_shape: tuple[int, ...], same_on_batch: bool = False) -> # Crop transform +@register( + aug_id=AugId.CROP, + backend=Backend.GPU, + group=AugType.GEO, + order=220, +) class RandomCropTransformGPU(RigidAffineAugmentationBase3D): """ Apply low resolution simulation to 3D volumes (5D tensor). @@ -545,13 +578,13 @@ def __init__( # A (low, high) range like `crop`, not a per-axis triple: CropGenerator3D # feeds it to _tuple_range_reader(..., 3, ...), which broadcasts the range # across all three axes. The annotation said triple, the default was a pair. - pos: tuple[float, float] = (0.5, 1.0), # Fraction of the pos + pos: tuple[float, float] = (0.0, 1.0), # Fraction of the pos same_on_batch: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, - **kwargs, ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self._param_generator = CropGenerator3D(crop=crop, pos=pos) def compute_transformation(self, input: Tensor, params: dict[str, Tensor], flags: dict[str, Any]) -> Tensor: diff --git a/smauglab/transforms/gpu/transforms.py b/smauglab/transforms/gpu/transforms.py index 42f158d..b5d46cf 100644 --- a/smauglab/transforms/gpu/transforms.py +++ b/smauglab/transforms/gpu/transforms.py @@ -1,487 +1,35 @@ -import json -import os -from typing import Any - import numpy as np import torch -from torch import Tensor, nn - -from smauglab.transforms.gpu.base import AugmentationSequentialCustom, ImageOnlyTransform, TransformType -from smauglab.transforms.gpu.contrast import ( - RandomBiasFieldGPU, - RandomBrightnessGPU, - RandomClampGPU, - RandomContrastGPU, - RandomConvTransformGPU, - RandomFunctionGPU, - RandomGammaGPU, - RandomGaussianNoiseGPU, - RandomHistogramEqualizationGPU, - RandomInverseGPU, - ZscoreNormalizationGPU, -) -from smauglab.transforms.gpu.domain_transfer import RandomDomainTransferGPU -from smauglab.transforms.gpu.fromSeg import RandomPALETTEGPU, RandomRedistributeSegGPU -from smauglab.transforms.gpu.spatial import ( - RandomAcqTransformGPU, - RandomAffine3DCustom, - RandomCropTransformGPU, - RandomFlipTransformGPU, - RandomLowResTransformGPU, -) -from smauglab.transforms.synthseg.transforms import RandomSynthSegGPU +from smauglab.config import load_config +from smauglab.registry import Backend +from smauglab.transforms.build import PipelineMode, build_gpu_pipeline +from smauglab.transforms.gpu.base import AugmentationSequentialCustom -class AugTransformsGPU(AugmentationSequentialCustom): - """ - Module to perform data augmentation on GPU. - """ - def __init__(self, json_path: str): - # Load transform parameters from JSON - config_path = os.path.join(json_path) - with open(config_path) as f: - config = json.load(f) - - if "GPU" in config.keys(): - self.transform_params = config["GPU"] - else: - self.transform_params = config - - transforms = self._build_transforms() - super().__init__( - *transforms, data_keys=["input", "mask"], same_on_batch=True - ) # Same_on_batch to ensure mask are aligned with images correctly (custom) see AugmentationSequentialOpsCustom in base.py - - def _build_transforms(self) -> list[TransformType]: - # Annotated rather than inferred: an empty list takes its element type from - # the first append, which would pin it to whichever transform the config - # happens to enable first and reject every sibling appended after it. - transforms: list[TransformType] = [] - - # Flipping transforms - flip_params = self.transform_params.get("FlipTransform") - if flip_params is not None: - transforms.append( - RandomFlipTransformGPU( - flip_axis=flip_params.get("flip_axis", [0]), - p=flip_params.get("probability", 0), - same_on_batch=flip_params.get("same_on_batch", False), - keepdim=flip_params.get("keepdim", True), - ) - ) - - # Spatial transforms - affine_params = self.transform_params.get("AffineTransform") - if affine_params is not None: - transforms.append( - RandomAffine3DCustom( - degrees=affine_params.get("degrees", 10), - translate=affine_params.get("translate", [0.1, 0.1, 0.1]), - scale=affine_params.get("scale", [0.9, 1.1]), - shears=affine_params.get("shear", [-10, 10, -10, 10, -10, 10]), - resample=affine_params.get("resample", "bilinear"), - p=affine_params.get("probability", 0), - ) - ) - - # SynthSeg generative augmentation: replace the image with a GMM synthesis - # of the segmentation (intensity-only here, so the mask stays consistent; - # geometric transforms above deform the labels first). All SynthSeg - # generator parameters are read straight from the config block. - synthseg_params = self.transform_params.get("SynthSeg") - if synthseg_params is not None: - synthseg_kwargs = {k: v for k, v in synthseg_params.items() if k != "probability"} - transforms.append( - RandomSynthSegGPU( - p=synthseg_params.get("probability", 1.0), - **synthseg_kwargs, - ) - ) - - ## Transfer augmentations (TA) - ######################### - # Replace image with V26_6_2 contrast (K-means + Voronoi + per-label remap) - palette_params = self.transform_params.get("RandomPALETTETransform") - if palette_params is not None: - transforms.append( - RandomPALETTEGPU( - p=palette_params.get("probability", 1.0), - c_choices=palette_params.get("c_choices", [2, 3, 4, 5, 6]), - s_choices=palette_params.get("s_choices", [2, 3, 4, 5, 6, 7, 8, 9, 10]), - blur_sigmas=palette_params.get("blur_sigmas", [0.0, 0.0, 0.0, 0.3, 0.5, 0.8]), - dark_threshold=palette_params.get("dark_threshold", 0.01), - n_kmeans_subsample=palette_params.get("n_kmeans_subsample", 10000), - skip_parcellation_prob=palette_params.get("skip_parcellation_prob", 0.10), - skip_sub_parc_prob=palette_params.get("skip_sub_parc_prob", 0.40), - alpha_magnitude_range=palette_params.get("alpha_magnitude_range", [0.5, 2.0]), - label_remap_prob=palette_params.get("label_remap_prob", 0.5), - min_label_voxels=palette_params.get("min_label_voxels", 4), - label_classes=palette_params.get("label_classes", None), - ) - ) - - # Domain transfer: randomly re-render the image as another sequence/cluster (TA) - # Accept either the class-name key or the descriptive key. - domain_params = self.transform_params.get("RandomDomainTransferGPU") or self.transform_params.get("DomainTransferTransform") - if domain_params is not None: - transforms.append( - RandomDomainTransferGPU( - bank_path=domain_params.get("bank_path", None), - source_label=domain_params["source_label"], - targets=domain_params.get("targets", None), - include_self=domain_params.get("include_self", False), - any_source=domain_params.get("any_source", False), - sigma=domain_params.get("sigma", 2.0), - apply_to_channel=domain_params.get("apply_to_channel", [0]), - zscore_io=domain_params.get("zscore_io", "auto"), - pct=domain_params.get("pct", 1.0), - blend_targets=domain_params.get("blend_targets", 1), - blend_concentration=domain_params.get("blend_concentration", 1.0), - p_class_mix=domain_params.get("p_class_mix", 0.0), - bias_field_std=domain_params.get("bias_field_std", 0.0), - bias_scale=domain_params.get("bias_scale", 0.03), - p_spatial_mix=domain_params.get("p_spatial_mix", 0.0), - spatial_mix_scale=domain_params.get("spatial_mix_scale", 0.03), - spatial_mix_gain=domain_params.get("spatial_mix_gain", 3.0), - p=domain_params.get("probability", 0.0), - same_on_batch=domain_params.get("same_on_batch", False), - ) - ) - - # Inverse transform (max - pixel_value) - inverse_params = self.transform_params.get("InverseTransform") - if inverse_params is not None: - transforms.append( - RandomInverseGPU( - p=inverse_params.get("probability", 0), - in_seg=inverse_params.get("in_seg", 0.0), - out_seg=inverse_params.get("out_seg", 0.0), - mix_in_out=inverse_params.get("mix_in_out", False), - mix_prob=inverse_params.get("mix_prob", 0.0), - retain_stats=inverse_params.get("retain_stats", False), - ) - ) - - # Histogram manipulations - histo_params = self.transform_params.get("HistogramEqualizationTransform") - if histo_params is not None: - transforms.append( - RandomHistogramEqualizationGPU( - p=histo_params.get("probability", 0), - in_seg=histo_params.get("in_seg", 0.0), - out_seg=histo_params.get("out_seg", 0.0), - mix_in_out=histo_params.get("mix_in_out", False), - mix_prob=histo_params.get("mix_prob", 0.0), - retain_stats=histo_params.get("retain_stats", False), - ) - ) - - # Redistribute segmentation values transform - redistribute_params = self.transform_params.get("RedistributeSegTransform") - if redistribute_params is not None: - transforms.append( - RandomRedistributeSegGPU( - in_seg=redistribute_params.get("in_seg", 0.2), - retain_stats=redistribute_params.get("retain_stats", False), - p=redistribute_params.get("probability", 0), - std_noise_range=redistribute_params.get("std_noise_range", [0.1, 0.3]), - dilation_iterations_range=redistribute_params.get("dilation_iterations_range", [1, 3]), - ) - ) - - # Scharr filter - scharr_params = self.transform_params.get("ScharrTransform") - if scharr_params is not None: - transforms.append( - RandomConvTransformGPU( - kernel_type=scharr_params.get("kernel_type", "Scharr"), - p=scharr_params.get("probability", 0), - in_seg=scharr_params.get("in_seg", 0.0), - out_seg=scharr_params.get("out_seg", 0.0), - mix_in_out=scharr_params.get("mix_in_out", False), - retain_stats=scharr_params.get("retain_stats", True), - absolute=scharr_params.get("absolute", True), - mix_prob=scharr_params.get("mix_prob", 0.0), - ) - ) - - # Unsharp masking - unsharp_params = self.transform_params.get("UnsharpMaskTransform") - if unsharp_params is not None: - transforms.append( - RandomConvTransformGPU( - kernel_type=unsharp_params.get("kernel_type", "UnsharpMask"), - p=unsharp_params.get("probability", 0), - in_seg=unsharp_params.get("in_seg", 0.0), - out_seg=unsharp_params.get("out_seg", 0.0), - mix_in_out=unsharp_params.get("mix_in_out", False), - sigma=unsharp_params.get("sigma", 1.0), - unsharp_amount=unsharp_params.get("unsharp_amount", 1.5), - mix_prob=unsharp_params.get("mix_prob", 0.0), - ) - ) - - # RandomConv transform - randconv_params = self.transform_params.get("RandomConvTransform") - if randconv_params is not None: - transforms.append( - RandomConvTransformGPU( - kernel_type=randconv_params.get("kernel_type", "RandConv"), - p=randconv_params.get("probability", 0), - in_seg=randconv_params.get("in_seg", 0.0), - out_seg=randconv_params.get("out_seg", 0.0), - mix_in_out=randconv_params.get("mix_in_out", False), - retain_stats=randconv_params.get("retain_stats", False), - kernel_sizes=randconv_params.get("kernel_sizes", [1, 3, 5, 7]), - mix_prob=randconv_params.get("mix_prob", 0.0), - ) - ) - - ## General enhancement (GE) - # Clamping transform - clamp_params = self.transform_params.get("ClampTransform") - if clamp_params is not None: - transforms.append( - RandomClampGPU( - max_clamp_amount=clamp_params.get("max_clamp_amount", 0.0), - in_seg=clamp_params.get("in_seg", 0.0), - out_seg=clamp_params.get("out_seg", 0.0), - mix_in_out=clamp_params.get("mix_in_out", False), - retain_stats=clamp_params.get("retain_stats", False), - p=clamp_params.get("probability", 0), - ) - ) - - # Noise transforms - noise_params = self.transform_params.get("GaussianNoiseTransform") - if noise_params is not None: - transforms.append( - RandomGaussianNoiseGPU( - mean=noise_params.get("mean", 0.0), - std=noise_params.get("std", 1.0), - in_seg=noise_params.get("in_seg", 0.0), - out_seg=noise_params.get("out_seg", 0.0), - mix_in_out=noise_params.get("mix_in_out", False), - p=noise_params.get("probability", 0), - ) - ) - - # Gaussian blur - gaussianblur_params = self.transform_params.get("GaussianBlurTransform") - if gaussianblur_params is not None: - transforms.append( - RandomConvTransformGPU( - kernel_type=gaussianblur_params.get("kernel_type", "GaussianBlur"), - in_seg=gaussianblur_params.get("in_seg", 0.0), - out_seg=gaussianblur_params.get("out_seg", 0.0), - mix_in_out=gaussianblur_params.get("mix_in_out", False), - p=gaussianblur_params.get("probability", 0), - sigma=gaussianblur_params.get("sigma", 1.0), - ) - ) - - # Brightness transforms - brightness_params = self.transform_params.get("BrightnessTransform") - if brightness_params is not None: - transforms.append( - RandomBrightnessGPU( - brightness_range=brightness_params.get("brightness_range", [0.5, 1.5]), - in_seg=brightness_params.get("in_seg", 0.0), - out_seg=brightness_params.get("out_seg", 0.0), - mix_in_out=brightness_params.get("mix_in_out", False), - p=brightness_params.get("probability", 0), - ) - ) - - # Gamma transforms - gamma_params = self.transform_params.get("GammaTransform") - if gamma_params is not None: - transforms.append( - RandomGammaGPU( - gamma_range=gamma_params.get("gamma_range", [0.7, 1.5]), - p=gamma_params.get("probability", 0), - invert_image=False, - in_seg=gamma_params.get("in_seg", 0.0), - out_seg=gamma_params.get("out_seg", 0.0), - mix_in_out=gamma_params.get("mix_in_out", False), - retain_stats=gamma_params.get("retain_stats", False), - ) - ) - - inv_gamma_params = self.transform_params.get("InvGammaTransform") - if inv_gamma_params is not None: - transforms.append( - RandomGammaGPU( - gamma_range=inv_gamma_params.get("gamma_range", [0.7, 1.5]), - p=inv_gamma_params.get("probability", 0), - in_seg=inv_gamma_params.get("in_seg", 0.0), - out_seg=inv_gamma_params.get("out_seg", 0.0), - mix_in_out=inv_gamma_params.get("mix_in_out", False), - invert_image=True, - retain_stats=inv_gamma_params.get("retain_stats", False), - ) - ) - - # nnUNetV2 Contrast transforms - contrast_params = self.transform_params.get("ContrastTransform") - if contrast_params is not None: - transforms.append( - RandomContrastGPU( - contrast_range=contrast_params.get("contrast_range", [0.75, 1.25]), - p=contrast_params.get("probability", 0), - in_seg=contrast_params.get("in_seg", 0.0), - out_seg=contrast_params.get("out_seg", 0.0), - mix_in_out=contrast_params.get("mix_in_out", False), - retain_stats=contrast_params.get("retain_stats", False), - ) - ) - - # Apply functions - func_list = [ - lambda x: torch.log(1 + x), - torch.sqrt, - torch.sin, - torch.exp, - lambda x: 1 / (1 + torch.exp(-x)), - ] - function_params = self.transform_params.get("FunctionTransform") - if function_params is not None: - transforms.extend( - RandomFunctionGPU( - func=func, - p=function_params.get("probability", 0), - in_seg=function_params.get("in_seg", 0.0), - out_seg=function_params.get("out_seg", 0.0), - mix_in_out=function_params.get("mix_in_out", False), - retain_stats=function_params.get("retain_stats", False), - ) - for func in func_list - ) - - # Shape transforms (Cropping and Simulating low resolution) - lowres_params = self.transform_params.get("SimulateLowResTransform") - if lowres_params is not None: - transforms.append( - RandomLowResTransformGPU( - p=lowres_params.get("probability", 0), - scale=lowres_params.get("scale", [0.3, 1.0]), - same_on_batch=lowres_params.get("same_on_batch", False), - ) - ) - - acq_params = self.transform_params.get("AcqTransform") - if acq_params is not None: - transforms.append( - RandomAcqTransformGPU( - p=acq_params.get("probability", 0), - scale=acq_params.get("scale", [0.3, 1.0]), - one_dim=True, - same_on_batch=acq_params.get("same_on_batch", False), - ) - ) - - crop_params = self.transform_params.get("CropTransform") - if crop_params is not None: - transforms.append( - RandomCropTransformGPU( - p=crop_params.get("probability", 0), - crop=crop_params.get("crop", [1.0, 1.0]), - pos=crop_params.get("pos", [0.0, 1.0]), - same_on_batch=acq_params.get("same_on_batch", False), - ) - ) - - # Bias field artifact - bias_field_params = self.transform_params.get("BiasFieldTransform") - if bias_field_params is not None: - transforms.append( - RandomBiasFieldGPU( - p=bias_field_params.get("probability", 0), - in_seg=bias_field_params.get("in_seg", 0.0), - out_seg=bias_field_params.get("out_seg", 0.0), - mix_in_out=bias_field_params.get("mix_in_out", False), - retain_stats=bias_field_params.get("retain_stats", False), - coefficients=bias_field_params.get("coefficients", 0.5), - ) - ) - - ## Random Z-score normalization - zscore_params = self.transform_params.get("ZscoreNormalizationTransform") - if zscore_params is not None: - transforms.append(ZscoreNormalizationGPU(p=zscore_params.get("probability", 0))) - - return transforms - - -class RandomChooseXTransformsGPU(ImageOnlyTransform): - """Randomly choose X transforms to apply from a given list of ImageOnlyTransform transforms (GPU version). - - Args: - transforms_list: List of initialized ImageOnlyTransform to choose from. - num_transforms: Number of transforms to randomly select and apply. - same_on_batch: apply the same transformation across the batch. - p: probability for applying the X transforms to a batch. This param controls the augmentation - probabilities batch-wise. - keepdim: whether to keep the output shape the same as input ``True`` or broadcast it to the batch - form ``False``. +class AugTransformsGPU(AugmentationSequentialCustom): + """GPU augmentation pipeline, built from a config section via the registry. + The ~370-line `if` ladder this replaces decided the class, the parameters and + the pipeline position of every augmentation inline; all three now come from the + registry, and `smauglab.transforms.build` does the dispatch once for all three + GPU pipeline modes. """ - def __init__( - self, - transforms_list: list[ImageOnlyTransform], - num_transforms: int = 1, - same_on_batch: bool = False, - p: float = 1.0, - keepdim: bool = True, - **kwargs, - ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) - if not isinstance(num_transforms, int) or num_transforms < 0: - raise ValueError(f"num_transforms must be a non-negative int. Got {num_transforms!r}.") - self.transforms_list = nn.ModuleList(transforms_list) - self.num_transforms = num_transforms - - def _apply_mix(self, x: Tensor, seg: Tensor | None) -> Tensor: - if self.num_transforms == 0 or len(self.transforms_list) == 0: - return x - - k = min(self.num_transforms, len(self.transforms_list)) - # sample without replacement - idx = torch.randperm(len(self.transforms_list), device=x.device)[:k] - - child_params: dict[str, Tensor] = {} - if seg is not None: - child_params["seg"] = seg - - for j in idx.tolist(): - t = self.transforms_list[j] - if torch.rand(1, device=x.device, dtype=x.dtype) > t.p: - continue - if not hasattr(t, "apply_transform"): - raise TypeError(f"All transforms must implement apply_transform like ImageOnlyTransform. Got {type(t)}") - # Most contrast transforms perform their random sampling inside apply_transform. - t_flags = getattr(t, "flags", {}) - x = t.apply_transform(x, child_params, t_flags, transform=None) - return x - - @torch.no_grad() # disable gradients for efficiency - def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[str, Any], transform: Tensor | None = None) -> Tensor: - seg = params.get("seg") - - if self.same_on_batch: - return self._apply_mix(input, seg) - - batch_size = input.shape[0] - out = input - for i in range(batch_size): - xi = out[i : i + 1] - seg_i = None - seg_i = seg[i : i + 1] if seg is not None and isinstance(seg, torch.Tensor) and seg.shape[0] == batch_size else seg - xi = self._apply_mix(xi, seg_i) - out[i : i + 1] = xi - return out + mode: PipelineMode = PipelineMode.SEQUENTIAL + + def __init__(self, json_path: str): + config = load_config(str(json_path)) + self.transform_params = config.section(Backend.GPU) + transforms = build_gpu_pipeline( + self.transform_params, + mode=self.mode, + options=config.pipeline_options("random_choose"), + source=config.source, + ) + # same_on_batch keeps the mask aligned with the image; see + # AugmentationSequentialOpsCustom in base.py. + super().__init__(*transforms, data_keys=["input", "mask"], same_on_batch=True) def normalize(arr: np.ndarray) -> np.ndarray: diff --git a/smauglab/transforms/gpu/transforms_list.py b/smauglab/transforms/gpu/transforms_list.py index 23fad4f..283f260 100644 --- a/smauglab/transforms/gpu/transforms_list.py +++ b/smauglab/transforms/gpu/transforms_list.py @@ -1,674 +1,34 @@ -import json -import os +"""The random-order GPU pipelines, and the combinator they are built from. + +`AugTransformsGPURandomOrder` and `AugTransformsGPURandomOrderTA` used to carry a +near-verbatim copy each of the ~300-line dispatch ladder in `transforms.py`, which +had already drifted from it (they passed a `crop=` argument no transform accepts, +and ordered SimulateLowRes differently). Both are now three lines: the only thing +that distinguishes them from `AugTransformsGPU` is how the registry's TA and GE +groups get bucketed, which `smauglab.transforms.build` handles. +""" + from typing import Any import numpy as np import torch from torch import Tensor, nn -from smauglab.transforms.gpu.base import AugmentationSequentialCustom, ImageOnlyTransform, TransformType -from smauglab.transforms.gpu.contrast import ( - RandomBiasFieldGPU, - RandomBrightnessGPU, - RandomClampGPU, - RandomContrastGPU, - RandomConvTransformGPU, - RandomFunctionGPU, - RandomGammaGPU, - RandomGaussianNoiseGPU, - RandomHistogramEqualizationGPU, - RandomInverseGPU, - ZscoreNormalizationGPU, -) -from smauglab.transforms.gpu.fromSeg import RandomRedistributeSegGPU -from smauglab.transforms.gpu.spatial import RandomAcqTransformGPU, RandomAffine3DCustom, RandomFlipTransformGPU, RandomLowResTransformGPU - - -class AugTransformsGPURandomOrder(AugmentationSequentialCustom): - """ - Module to perform data augmentation on GPU. - """ +from smauglab.transforms.build import PipelineMode +from smauglab.transforms.gpu.base import ImageOnlyTransform +from smauglab.transforms.gpu.transforms import AugTransformsGPU - def __init__(self, json_path: str): - # Load transform parameters from JSON - config_path = os.path.join(json_path) - with open(config_path) as f: - config = json.load(f) - if "GPU" in config.keys(): - self.transform_params = config["GPU"] - else: - self.transform_params = config - - transforms = self._build_transforms() - super().__init__( - *transforms, data_keys=["input", "mask"], same_on_batch=True - ) # Same_on_batch to ensure mask are aligned with images correctly (custom) see AugmentationSequentialOpsCustom in base.py - - def _build_transforms(self) -> list[TransformType]: - # Annotated rather than inferred: an empty list takes its element type from - # the first append, which would pin it to whichever transform the config - # happens to enable first and reject every sibling appended after it. - transforms: list[TransformType] = [] - - # Flipping transforms - flip_params = self.transform_params.get("FlipTransform") - if flip_params is not None: - transforms.append( - RandomFlipTransformGPU( - flip_axis=flip_params.get("flip_axis", [0]), - p=flip_params.get("probability", 0), - same_on_batch=flip_params.get("same_on_batch", False), - keepdim=flip_params.get("keepdim", True), - ) - ) - - # Spatial transforms - affine_params = self.transform_params.get("AffineTransform") - if affine_params is not None: - transforms.append( - RandomAffine3DCustom( - degrees=affine_params.get("degrees", 10), - translate=affine_params.get("translate", [0.1, 0.1, 0.1]), - scale=affine_params.get("scale", [0.9, 1.1]), - shears=affine_params.get("shear", [-10, 10, -10, 10, -10, 10]), - resample=affine_params.get("resample", "bilinear"), - p=affine_params.get("probability", 0), - ) - ) - - ## Transfer augmentations (TA) - ta_transforms: list[ImageOnlyTransform] = [] - # Inverse transform (max - pixel_value) - inverse_params = self.transform_params.get("InverseTransform") - if inverse_params is not None: - ta_transforms.append( - RandomInverseGPU( - p=inverse_params.get("probability", 0), - in_seg=inverse_params.get("in_seg", 0.0), - out_seg=inverse_params.get("out_seg", 0.0), - mix_in_out=inverse_params.get("mix_in_out", False), - mix_prob=inverse_params.get("mix_prob", 0.0), - retain_stats=inverse_params.get("retain_stats", False), - ) - ) - - # Histogram manipulations - histo_params = self.transform_params.get("HistogramEqualizationTransform") - if histo_params is not None: - ta_transforms.append( - RandomHistogramEqualizationGPU( - p=histo_params.get("probability", 0), - in_seg=histo_params.get("in_seg", 0.0), - out_seg=histo_params.get("out_seg", 0.0), - mix_in_out=histo_params.get("mix_in_out", False), - mix_prob=histo_params.get("mix_prob", 0.0), - retain_stats=histo_params.get("retain_stats", False), - ) - ) - - # Redistribute segmentation values transform - redistribute_params = self.transform_params.get("RedistributeSegTransform") - if redistribute_params is not None: - ta_transforms.append( - RandomRedistributeSegGPU( - in_seg=redistribute_params.get("in_seg", 0.2), - retain_stats=redistribute_params.get("retain_stats", False), - p=redistribute_params.get("probability", 0), - ) - ) - - # Scharr filter - scharr_params = self.transform_params.get("ScharrTransform") - if scharr_params is not None: - ta_transforms.append( - RandomConvTransformGPU( - kernel_type=scharr_params.get("kernel_type", "Scharr"), - p=scharr_params.get("probability", 0), - in_seg=scharr_params.get("in_seg", 0.0), - out_seg=scharr_params.get("out_seg", 0.0), - mix_in_out=scharr_params.get("mix_in_out", False), - retain_stats=scharr_params.get("retain_stats", True), - absolute=scharr_params.get("absolute", True), - mix_prob=scharr_params.get("mix_prob", 0.0), - ) - ) - - # Unsharp masking - unsharp_params = self.transform_params.get("UnsharpMaskTransform") - if unsharp_params is not None: - ta_transforms.append( - RandomConvTransformGPU( - kernel_type=unsharp_params.get("kernel_type", "UnsharpMask"), - p=unsharp_params.get("probability", 0), - in_seg=unsharp_params.get("in_seg", 0.0), - out_seg=unsharp_params.get("out_seg", 0.0), - mix_in_out=unsharp_params.get("mix_in_out", False), - sigma=unsharp_params.get("sigma", 1.0), - unsharp_amount=unsharp_params.get("unsharp_amount", 1.5), - mix_prob=unsharp_params.get("mix_prob", 0.0), - ) - ) - - # RandomConv transform - randconv_params = self.transform_params.get("RandomConvTransform") - if randconv_params is not None: - ta_transforms.append( - RandomConvTransformGPU( - kernel_type=randconv_params.get("kernel_type", "RandConv"), - p=randconv_params.get("probability", 0), - in_seg=randconv_params.get("in_seg", 0.0), - out_seg=randconv_params.get("out_seg", 0.0), - mix_in_out=randconv_params.get("mix_in_out", False), - retain_stats=randconv_params.get("retain_stats", False), - kernel_sizes=randconv_params.get("kernel_sizes", [1, 3, 5, 7]), - mix_prob=randconv_params.get("mix_prob", 0.0), - ) - ) - - # Apply functions - func_list = [ - lambda x: torch.log(1 + x), - torch.sqrt, - torch.sin, - torch.exp, - lambda x: 1 / (1 + torch.exp(-x)), - ] - function_params = self.transform_params.get("FunctionTransform") - if function_params is not None: - ta_transforms.extend( - RandomFunctionGPU( - func=func, - p=function_params.get("probability", 0), - in_seg=function_params.get("in_seg", 0.0), - out_seg=function_params.get("out_seg", 0.0), - mix_in_out=function_params.get("mix_in_out", False), - retain_stats=function_params.get("retain_stats", False), - ) - for func in func_list - ) - - # Bias field artifact - bias_field_params = self.transform_params.get("BiasFieldTransform") - if bias_field_params is not None: - ta_transforms.append( - RandomBiasFieldGPU( - p=bias_field_params.get("probability", 0), - in_seg=bias_field_params.get("in_seg", 0.0), - out_seg=bias_field_params.get("out_seg", 0.0), - mix_in_out=bias_field_params.get("mix_in_out", False), - retain_stats=bias_field_params.get("retain_stats", False), - coefficients=bias_field_params.get("coefficients", 0.5), - ) - ) - - ## General enhancement (GE) - ge_transforms: list[ImageOnlyTransform] = [] - # Clamping transform - clamp_params = self.transform_params.get("ClampTransform") - if clamp_params is not None: - ge_transforms.append( - RandomClampGPU( - max_clamp_amount=clamp_params.get("max_clamp_amount", 0.0), - in_seg=clamp_params.get("in_seg", 0.0), - out_seg=clamp_params.get("out_seg", 0.0), - mix_in_out=clamp_params.get("mix_in_out", False), - retain_stats=clamp_params.get("retain_stats", False), - p=clamp_params.get("probability", 0), - ) - ) - - # Noise transforms - noise_params = self.transform_params.get("GaussianNoiseTransform") - if noise_params is not None: - ge_transforms.append( - RandomGaussianNoiseGPU( - mean=noise_params.get("mean", 0.0), - std=noise_params.get("std", 1.0), - in_seg=noise_params.get("in_seg", 0.0), - out_seg=noise_params.get("out_seg", 0.0), - mix_in_out=noise_params.get("mix_in_out", False), - p=noise_params.get("probability", 0), - ) - ) - - # Gaussian blur - gaussianblur_params = self.transform_params.get("GaussianBlurTransform") - if gaussianblur_params is not None: - ge_transforms.append( - RandomConvTransformGPU( - kernel_type=gaussianblur_params.get("kernel_type", "GaussianBlur"), - in_seg=gaussianblur_params.get("in_seg", 0.0), - out_seg=gaussianblur_params.get("out_seg", 0.0), - mix_in_out=gaussianblur_params.get("mix_in_out", False), - p=gaussianblur_params.get("probability", 0), - sigma=gaussianblur_params.get("sigma", 1.0), - ) - ) - - # Brightness transforms - brightness_params = self.transform_params.get("BrightnessTransform") - if brightness_params is not None: - ge_transforms.append( - RandomBrightnessGPU( - brightness_range=brightness_params.get("brightness_range", [0.5, 1.5]), - in_seg=brightness_params.get("in_seg", 0.0), - out_seg=brightness_params.get("out_seg", 0.0), - mix_in_out=brightness_params.get("mix_in_out", False), - p=brightness_params.get("probability", 0), - ) - ) - - # Gamma transforms - gamma_params = self.transform_params.get("GammaTransform") - if gamma_params is not None: - ge_transforms.append( - RandomGammaGPU( - gamma_range=gamma_params.get("gamma_range", [0.7, 1.5]), - p=gamma_params.get("probability", 0), - invert_image=False, - in_seg=gamma_params.get("in_seg", 0.0), - out_seg=gamma_params.get("out_seg", 0.0), - mix_in_out=gamma_params.get("mix_in_out", False), - retain_stats=gamma_params.get("retain_stats", False), - ) - ) - - inv_gamma_params = self.transform_params.get("InvGammaTransform") - if inv_gamma_params is not None: - ge_transforms.append( - RandomGammaGPU( - gamma_range=inv_gamma_params.get("gamma_range", [0.7, 1.5]), - p=inv_gamma_params.get("probability", 0), - in_seg=inv_gamma_params.get("in_seg", 0.0), - out_seg=inv_gamma_params.get("out_seg", 0.0), - mix_in_out=inv_gamma_params.get("mix_in_out", False), - invert_image=True, - retain_stats=inv_gamma_params.get("retain_stats", False), - ) - ) - - # nnUNetV2 Contrast transforms - contrast_params = self.transform_params.get("ContrastTransform") - if contrast_params is not None: - ge_transforms.append( - RandomContrastGPU( - contrast_range=contrast_params.get("contrast_range", [0.75, 1.25]), - p=contrast_params.get("probability", 0), - in_seg=contrast_params.get("in_seg", 0.0), - out_seg=contrast_params.get("out_seg", 0.0), - mix_in_out=contrast_params.get("mix_in_out", False), - retain_stats=contrast_params.get("retain_stats", False), - ) - ) - - # Shape transforms (Cropping and Simulating low resolution) - lowres_params = self.transform_params.get("SimulateLowResTransform") - if lowres_params is not None: - transforms.append( - RandomLowResTransformGPU( - p=lowres_params.get("probability", 0), - scale=lowres_params.get("scale", [0.3, 1.0]), - crop=lowres_params.get("crop", [1.0, 1.0]), - same_on_batch=lowres_params.get("same_on_batch", False), - ) - ) - - acq_params = self.transform_params.get("AcqTransform") - if acq_params is not None: - ge_transforms.append( - RandomAcqTransformGPU( - p=acq_params.get("probability", 0), - scale=acq_params.get("scale", [0.3, 1.0]), - crop=acq_params.get("crop", [1.0, 1.0]), - one_dim=True, - same_on_batch=acq_params.get("same_on_batch", False), - ) - ) - - ## Random Z-score normalization - zscore_params = self.transform_params.get("ZscoreNormalizationTransform") - if zscore_params is not None: - ge_transforms.append(ZscoreNormalizationGPU(p=zscore_params.get("probability", 0))) - - choose_x_params = self.transform_params.get("RandomChooseXTransforms") - transforms.append( - RandomChooseXTransformsGPU( - transforms_list=ta_transforms, - num_transforms=len(ta_transforms), - p=choose_x_params.get("ta_probability", 1.0), - random_order=choose_x_params.get("ta_random_order", True), - ) - ) - transforms.append( - RandomChooseXTransformsGPU( - transforms_list=ge_transforms, - num_transforms=len(ge_transforms), - p=choose_x_params.get("ge_probability", 1.0), - random_order=choose_x_params.get("ge_random_order", True), - ) - ) - - return transforms - - -class AugTransformsGPURandomOrderTA(AugmentationSequentialCustom): - """ - Module to perform data augmentation on GPU. - """ +class AugTransformsGPURandomOrder(AugTransformsGPU): + """Geometry in order, then the TA and GE groups each shuffled in their own bucket.""" - def __init__(self, json_path: str): - # Load transform parameters from JSON - config_path = os.path.join(json_path) - with open(config_path) as f: - config = json.load(f) + mode = PipelineMode.RANDOM_ORDER - if "GPU" in config.keys(): - self.transform_params = config["GPU"] - else: - self.transform_params = config - - transforms = self._build_transforms() - super().__init__( - *transforms, data_keys=["input", "mask"], same_on_batch=True - ) # Same_on_batch to ensure mask are aligned with images correctly (custom) see AugmentationSequentialOpsCustom in base.py - - def _build_transforms(self) -> list[TransformType]: - # Annotated rather than inferred: an empty list takes its element type from - # the first append, which would pin it to whichever transform the config - # happens to enable first and reject every sibling appended after it. - transforms: list[TransformType] = [] - - # Flipping transforms - flip_params = self.transform_params.get("FlipTransform") - if flip_params is not None: - transforms.append( - RandomFlipTransformGPU( - flip_axis=flip_params.get("flip_axis", [0]), - p=flip_params.get("probability", 0), - same_on_batch=flip_params.get("same_on_batch", False), - keepdim=flip_params.get("keepdim", True), - ) - ) - - # Spatial transforms - affine_params = self.transform_params.get("AffineTransform") - if affine_params is not None: - transforms.append( - RandomAffine3DCustom( - degrees=affine_params.get("degrees", 10), - translate=affine_params.get("translate", [0.1, 0.1, 0.1]), - scale=affine_params.get("scale", [0.9, 1.1]), - shears=affine_params.get("shear", [-10, 10, -10, 10, -10, 10]), - resample=affine_params.get("resample", "bilinear"), - p=affine_params.get("probability", 0), - ) - ) - - ## Transfer augmentations (TA) - ta_transforms: list[ImageOnlyTransform] = [] - # Inverse transform (max - pixel_value) - inverse_params = self.transform_params.get("InverseTransform") - if inverse_params is not None: - ta_transforms.append( - RandomInverseGPU( - p=inverse_params.get("probability", 0), - in_seg=inverse_params.get("in_seg", 0.0), - out_seg=inverse_params.get("out_seg", 0.0), - mix_in_out=inverse_params.get("mix_in_out", False), - mix_prob=inverse_params.get("mix_prob", 0.0), - retain_stats=inverse_params.get("retain_stats", False), - ) - ) - - # Histogram manipulations - histo_params = self.transform_params.get("HistogramEqualizationTransform") - if histo_params is not None: - ta_transforms.append( - RandomHistogramEqualizationGPU( - p=histo_params.get("probability", 0), - in_seg=histo_params.get("in_seg", 0.0), - out_seg=histo_params.get("out_seg", 0.0), - mix_in_out=histo_params.get("mix_in_out", False), - mix_prob=histo_params.get("mix_prob", 0.0), - retain_stats=histo_params.get("retain_stats", False), - ) - ) - - # Redistribute segmentation values transform - redistribute_params = self.transform_params.get("RedistributeSegTransform") - if redistribute_params is not None: - ta_transforms.append( - RandomRedistributeSegGPU( - in_seg=redistribute_params.get("in_seg", 0.2), - retain_stats=redistribute_params.get("retain_stats", False), - p=redistribute_params.get("probability", 0), - ) - ) - - # Scharr filter - scharr_params = self.transform_params.get("ScharrTransform") - if scharr_params is not None: - ta_transforms.append( - RandomConvTransformGPU( - kernel_type=scharr_params.get("kernel_type", "Scharr"), - p=scharr_params.get("probability", 0), - in_seg=scharr_params.get("in_seg", 0.0), - out_seg=scharr_params.get("out_seg", 0.0), - mix_in_out=scharr_params.get("mix_in_out", False), - retain_stats=scharr_params.get("retain_stats", True), - absolute=scharr_params.get("absolute", True), - mix_prob=scharr_params.get("mix_prob", 0.0), - ) - ) - - # Unsharp masking - unsharp_params = self.transform_params.get("UnsharpMaskTransform") - if unsharp_params is not None: - ta_transforms.append( - RandomConvTransformGPU( - kernel_type=unsharp_params.get("kernel_type", "UnsharpMask"), - p=unsharp_params.get("probability", 0), - in_seg=unsharp_params.get("in_seg", 0.0), - out_seg=unsharp_params.get("out_seg", 0.0), - mix_in_out=unsharp_params.get("mix_in_out", False), - sigma=unsharp_params.get("sigma", 1.0), - unsharp_amount=unsharp_params.get("unsharp_amount", 1.5), - mix_prob=unsharp_params.get("mix_prob", 0.0), - ) - ) - - # RandomConv transform - randconv_params = self.transform_params.get("RandomConvTransform") - if randconv_params is not None: - ta_transforms.append( - RandomConvTransformGPU( - kernel_type=randconv_params.get("kernel_type", "RandConv"), - p=randconv_params.get("probability", 0), - in_seg=randconv_params.get("in_seg", 0.0), - out_seg=randconv_params.get("out_seg", 0.0), - mix_in_out=randconv_params.get("mix_in_out", False), - retain_stats=randconv_params.get("retain_stats", False), - kernel_sizes=randconv_params.get("kernel_sizes", [1, 3, 5, 7]), - mix_prob=randconv_params.get("mix_prob", 0.0), - ) - ) - - # Apply functions - func_list = [ - lambda x: torch.log(1 + x), - torch.sqrt, - torch.sin, - torch.exp, - lambda x: 1 / (1 + torch.exp(-x)), - ] - function_params = self.transform_params.get("FunctionTransform") - if function_params is not None: - ta_transforms.extend( - RandomFunctionGPU( - func=func, - p=function_params.get("probability", 0), - in_seg=function_params.get("in_seg", 0.0), - out_seg=function_params.get("out_seg", 0.0), - mix_in_out=function_params.get("mix_in_out", False), - retain_stats=function_params.get("retain_stats", False), - ) - for func in func_list - ) - - # Bias field artifact - bias_field_params = self.transform_params.get("BiasFieldTransform") - if bias_field_params is not None: - ta_transforms.append( - RandomBiasFieldGPU( - p=bias_field_params.get("probability", 0), - in_seg=bias_field_params.get("in_seg", 0.0), - out_seg=bias_field_params.get("out_seg", 0.0), - mix_in_out=bias_field_params.get("mix_in_out", False), - retain_stats=bias_field_params.get("retain_stats", False), - coefficients=bias_field_params.get("coefficients", 0.5), - ) - ) - - choose_x_params = self.transform_params.get("RandomChooseXTransforms") - transforms.append( - RandomChooseXTransformsGPU( - transforms_list=ta_transforms, - num_transforms=len(ta_transforms), - p=choose_x_params.get("ta_probability", 1.0), - random_order=False, - ) - ) - - ## General enhancement (GE) - # Clamping transform - clamp_params = self.transform_params.get("ClampTransform") - if clamp_params is not None: - transforms.append( - RandomClampGPU( - max_clamp_amount=clamp_params.get("max_clamp_amount", 0.0), - in_seg=clamp_params.get("in_seg", 0.0), - out_seg=clamp_params.get("out_seg", 0.0), - mix_in_out=clamp_params.get("mix_in_out", False), - retain_stats=clamp_params.get("retain_stats", False), - p=clamp_params.get("probability", 0), - ) - ) - - # Noise transforms - noise_params = self.transform_params.get("GaussianNoiseTransform") - if noise_params is not None: - transforms.append( - RandomGaussianNoiseGPU( - mean=noise_params.get("mean", 0.0), - std=noise_params.get("std", 1.0), - in_seg=noise_params.get("in_seg", 0.0), - out_seg=noise_params.get("out_seg", 0.0), - mix_in_out=noise_params.get("mix_in_out", False), - p=noise_params.get("probability", 0), - ) - ) - - # Gaussian blur - gaussianblur_params = self.transform_params.get("GaussianBlurTransform") - if gaussianblur_params is not None: - transforms.append( - RandomConvTransformGPU( - kernel_type=gaussianblur_params.get("kernel_type", "GaussianBlur"), - in_seg=gaussianblur_params.get("in_seg", 0.0), - out_seg=gaussianblur_params.get("out_seg", 0.0), - mix_in_out=gaussianblur_params.get("mix_in_out", False), - p=gaussianblur_params.get("probability", 0), - sigma=gaussianblur_params.get("sigma", 1.0), - ) - ) - - # Brightness transforms - brightness_params = self.transform_params.get("BrightnessTransform") - if brightness_params is not None: - transforms.append( - RandomBrightnessGPU( - brightness_range=brightness_params.get("brightness_range", [0.5, 1.5]), - in_seg=brightness_params.get("in_seg", 0.0), - out_seg=brightness_params.get("out_seg", 0.0), - mix_in_out=brightness_params.get("mix_in_out", False), - p=brightness_params.get("probability", 0), - ) - ) - - # Gamma transforms - gamma_params = self.transform_params.get("GammaTransform") - if gamma_params is not None: - transforms.append( - RandomGammaGPU( - gamma_range=gamma_params.get("gamma_range", [0.7, 1.5]), - p=gamma_params.get("probability", 0), - invert_image=False, - in_seg=gamma_params.get("in_seg", 0.0), - out_seg=gamma_params.get("out_seg", 0.0), - mix_in_out=gamma_params.get("mix_in_out", False), - retain_stats=gamma_params.get("retain_stats", False), - ) - ) - - inv_gamma_params = self.transform_params.get("InvGammaTransform") - if inv_gamma_params is not None: - transforms.append( - RandomGammaGPU( - gamma_range=inv_gamma_params.get("gamma_range", [0.7, 1.5]), - p=inv_gamma_params.get("probability", 0), - in_seg=inv_gamma_params.get("in_seg", 0.0), - out_seg=inv_gamma_params.get("out_seg", 0.0), - mix_in_out=inv_gamma_params.get("mix_in_out", False), - invert_image=True, - retain_stats=inv_gamma_params.get("retain_stats", False), - ) - ) - - # nnUNetV2 Contrast transforms - contrast_params = self.transform_params.get("ContrastTransform") - if contrast_params is not None: - transforms.append( - RandomContrastGPU( - contrast_range=contrast_params.get("contrast_range", [0.75, 1.25]), - p=contrast_params.get("probability", 0), - in_seg=contrast_params.get("in_seg", 0.0), - out_seg=contrast_params.get("out_seg", 0.0), - mix_in_out=contrast_params.get("mix_in_out", False), - retain_stats=contrast_params.get("retain_stats", False), - ) - ) - - # Shape transforms (Cropping and Simulating low resolution) - lowres_params = self.transform_params.get("SimulateLowResTransform") - if lowres_params is not None: - transforms.append( - RandomLowResTransformGPU( - p=lowres_params.get("probability", 0), - scale=lowres_params.get("scale", [0.3, 1.0]), - crop=lowres_params.get("crop", [1.0, 1.0]), - same_on_batch=lowres_params.get("same_on_batch", False), - ) - ) - - acq_params = self.transform_params.get("AcqTransform") - if acq_params is not None: - transforms.append( - RandomAcqTransformGPU( - p=acq_params.get("probability", 0), - scale=acq_params.get("scale", [0.3, 1.0]), - crop=acq_params.get("crop", [1.0, 1.0]), - one_dim=True, - same_on_batch=acq_params.get("same_on_batch", False), - ) - ) - - ## Random Z-score normalization - zscore_params = self.transform_params.get("ZscoreNormalizationTransform") - if zscore_params is not None: - transforms.append(ZscoreNormalizationGPU(p=zscore_params.get("probability", 0))) - - return transforms + +class AugTransformsGPURandomOrderTA(AugTransformsGPU): + """Only the transfer augmentations are bucketed; everything else keeps its order.""" + + mode = PipelineMode.RANDOM_ORDER_TA class RandomChooseXTransformsGPU(ImageOnlyTransform): @@ -691,11 +51,11 @@ def __init__( num_transforms: int = 1, same_on_batch: bool = False, p: float = 1.0, + p_batch: float = 1.0, keepdim: bool = True, random_order: bool = True, - **kwargs, ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) if not isinstance(num_transforms, int) or num_transforms < 0: raise ValueError(f"num_transforms must be a non-negative int. Got {num_transforms!r}.") self.transforms_list = nn.ModuleList(transforms_list) diff --git a/smauglab/transforms/synthseg/README.md b/smauglab/transforms/synthseg/README.md index 2acabc7..67d225f 100644 --- a/smauglab/transforms/synthseg/README.md +++ b/smauglab/transforms/synthseg/README.md @@ -65,7 +65,7 @@ Defaults match `BrainGenerator.__init__` (which overrides several - **3D only** (5D `(B, C, D, H, W)` tensors), matching SmaugLab's GPU transforms. - Affine transforms are applied **about the volume centre** (like SmaugLab's - `RandomAffine3DCustom`), rather than the corner-origin used by neuron's + `RandomAffineGPU`), rather than the corner-origin used by neuron's `affine_to_shift`. This keeps the anatomy in frame and is the standard choice; the visual augmentation is equivalent. - The SVF is integrated at full resolution after upsampling the coarse velocity @@ -156,11 +156,11 @@ the deformed labels: ```python from smauglab.transforms.gpu.base import AugmentationSequentialCustom -from smauglab.transforms.gpu.spatial import RandomAffine3DCustom +from smauglab.transforms.gpu.spatial import RandomAffineGPU from smauglab.transforms.synthseg import RandomSynthSegGPU aug = AugmentationSequentialCustom( - RandomAffine3DCustom(degrees=15, scale=[0.8, 1.2], p=1.0), + RandomAffineGPU(degrees=15, scale=[0.8, 1.2], p=1.0), RandomSynthSegGPU(generation_labels=None, n_channels=1, bias_field_std=0.7, gamma_std=0.5, randomise_res=True, p=1.0), data_keys=["input", "mask"], same_on_batch=True, diff --git a/smauglab/transforms/synthseg/functional.py b/smauglab/transforms/synthseg/functional.py index 30d6979..9e30f85 100644 --- a/smauglab/transforms/synthseg/functional.py +++ b/smauglab/transforms/synthseg/functional.py @@ -20,7 +20,7 @@ the ``(x, y, z) = (W, H, D)`` order expected by ``F.grid_sample`` at the very end, with ``align_corners=True`` so that integer voxel indices map exactly. * Affine transforms are applied about the volume centre (standard practice and - matching SmaugLab's existing ``RandomAffine3DCustom``), so small rotations / + matching SmaugLab's existing ``RandomAffineGPU``), so small rotations / scalings keep the anatomy in frame. """ diff --git a/smauglab/transforms/synthseg/transforms.py b/smauglab/transforms/synthseg/transforms.py index ac31632..7e9073e 100644 --- a/smauglab/transforms/synthseg/transforms.py +++ b/smauglab/transforms/synthseg/transforms.py @@ -5,7 +5,7 @@ * :class:`RandomSynthSegGPU` -- an :class:`ImageOnlyTransform` that *replaces* the image with a GMM-synthesised one derived from ``params['seg']``. It is intensity-only (no internal spatial deformation), so it composes with SmaugLab's - existing geometric transforms (``RandomAffine3DCustom``, ``RandomFlipTransformGPU``, + existing geometric transforms (``RandomAffineGPU``, ``RandomFlipTransformGPU``, ...) inside an :class:`AugmentationSequentialCustom`: place those *before* it so the mask is deformed first and SynthSeg generates from the deformed labels, keeping image and label aligned. Drop it into a ``transform_params_gpu.json`` @@ -28,60 +28,21 @@ import torch from torch import Tensor, nn +from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform from smauglab.transforms.synthseg.generator import SynthSegGenerator -# Keys understood from the JSON config / kwargs, forwarded to SynthSegGenerator. -_GENERATOR_KEYS = { - "generation_labels", - "output_labels", - "n_neutral_labels", - "generation_classes", - "n_channels", - "prior_distributions", - "prior_means", - "prior_stds", - "flipping", - "flip_axis", - "scaling_bounds", - "rotation_bounds", - "shearing_bounds", - "translation_bounds", - "nonlin_std", - "nonlin_scale", - "svf_integration_steps", - "bias_field_std", - "bias_scale", - "gamma_std", - "clip", - "normalise", - "randomise_res", - "max_res_iso", - "max_res_aniso", - "data_res", - "thickness", - "blur_range", - "atlas_res", - "output_shape", - "em_label_completion", - "em_n_foreground_clusters", - "em_background_clusters_range", - "em_background_label", - "em_n_iters", - "em_max_fit_voxels", - "em_same_on_batch", - "apply_affine", - "apply_nonlinear", - "apply_bias_field", - "apply_intensity_augmentation", - "apply_resolution", -} - - -def _filter_generator_kwargs(params: dict[str, Any]) -> dict[str, Any]: - return {k: v for k, v in params.items() if k in _GENERATOR_KEYS} - +@register( + aug_id=AugId.SYNTHSEG, + backend=Backend.GPU, + group=AugType.TA, + order=30, + forwards_to=SynthSegGenerator, + # Forced below to keep the synthesis intensity-only; a config setting any of + # these would be silently overridden, so they are rejected instead. + context_params=("apply_affine", "apply_nonlinear", "flipping", "output_shape"), +) class RandomSynthSegGPU(ImageOnlyTransform): """Replace the image with a SynthSeg GMM synthesis of ``params['seg']``. @@ -104,12 +65,19 @@ def __init__( apply_to_channel: list[int] | None = None, same_on_batch: bool = False, p: float = 0.5, + p_batch: float = 1.0, keepdim: bool = True, **kwargs: Any, ) -> None: - super().__init__(p=p, same_on_batch=same_on_batch, keepdim=keepdim) + super().__init__(p=p, p_batch=p_batch, same_on_batch=same_on_batch, keepdim=keepdim) self.apply_to_channel = apply_to_channel if apply_to_channel is not None else [0] - gen_kwargs = _filter_generator_kwargs(kwargs) + # Forwarded straight through, so SynthSegGenerator's own signature is the only + # definition of what is accepted. This used to go through a hand-maintained + # _GENERATOR_KEYS set that silently dropped anything not in it -- meaning a + # typo'd generator parameter did nothing at all and said nothing about it. + # The registry declares forwards_to=SynthSegGenerator so config validation + # sees both signatures as one. + gen_kwargs = dict(kwargs) # Intensity-only: never deform/flip internally (geometry comes from the # surrounding sequential, which also transports the mask). gen_kwargs.update(apply_affine=False, apply_nonlinear=False, flipping=False, output_shape=None) @@ -167,7 +135,12 @@ def __init__(self, json_path: str | None = None, params: dict[str, Any] | None = self.probability = float(config.get("probability", 1.0)) self.return_onehot = bool(config.get("return_onehot", False)) - self.generator = SynthSegGenerator(**_filter_generator_kwargs(config)) + # Everything except this driver's own two keys belongs to the generator. + # Removing exactly those, rather than keeping a hand-listed allowlist of + # generator parameters, means an unrecognised key raises here instead of + # being silently discarded. + gen_kwargs = {k: v for k, v in config.items() if k not in {"probability", "return_onehot"}} + self.generator = SynthSegGenerator(**gen_kwargs) @torch.no_grad() def forward(self, data: Tensor, target: Tensor): diff --git a/smauglab/utils/__init__.py b/smauglab/utils/__init__.py new file mode 100644 index 0000000..0c90b86 --- /dev/null +++ b/smauglab/utils/__init__.py @@ -0,0 +1,13 @@ +"""NIfTI image handling. + +Only `image.py` lives here. `utils.py` used to sit alongside it -- MONAI training-loop +helpers, argparse tuple parsers, a Dice function -- but nothing under `smauglab/` +imported any of it once the `__main__` demo blocks moved out to `scripts/`, so it +shipped in every wheel for the benefit of two standalone scripts. It is now +`scripts/_common.py`. + +`image.py` stayed despite having no in-package consumer either: five modules in the +sibling segtransferaug repository import `smauglab.utils.image.Image`, so it is part +of the public API in practice. It is a vendored subset of spinalcordtoolbox's +`image.py` -- see the class docstrings for the upstream links. +""" diff --git a/unit_tests/fixtures/legacy_configs/configs/synthseg_params.json b/unit_tests/fixtures/legacy_configs/configs/synthseg_params.json new file mode 100644 index 0000000..5bdb158 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/synthseg_params.json @@ -0,0 +1,50 @@ +{ + "SynthSeg": { + "probability": 1.0, + "n_channels": 1, + "generation_labels": null, + "output_labels": null, + "n_neutral_labels": null, + "generation_classes": null, + + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + + "flipping": true, + "flip_axis": 2, + "scaling_bounds": 0.2, + "rotation_bounds": 15.0, + "shearing_bounds": 0.012, + "translation_bounds": false, + + "nonlin_std": 4.0, + "nonlin_scale": 0.04, + "svf_integration_steps": 7, + + "bias_field_std": 0.7, + "bias_scale": 0.025, + + "gamma_std": 0.5, + "clip": 300.0, + "normalise": true, + + "randomise_res": true, + "max_res_iso": 4.0, + "max_res_aniso": 8.0, + "data_res": null, + "thickness": null, + "blur_range": 1.03, + "atlas_res": 1.0, + + "output_shape": null, + + "em_label_completion": false, + "em_n_foreground_clusters": 2, + "em_background_clusters_range": [3, 10], + "em_background_label": 0, + "em_n_iters": 20, + "em_max_fit_voxels": 100000, + "em_same_on_batch": false + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params.json b/unit_tests/fixtures/legacy_configs/configs/transform_params.json new file mode 100644 index 0000000..1c3b07b --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params.json @@ -0,0 +1,98 @@ +{ + "retain_stats": true, + "mirror_axes": [0,1,2], + "ArtifactTransform": { + "motion": false, + "ghosting": false, + "spike": false, + "bias_field": false, + "blur": false, + "noise": false, + "swap": false, + "random_pick": false, + "probability": 0.7 + }, + "SpatialCustomTransform": { + "flip": false, + "affine": false, + "elastic": false, + "anisotropy": false, + "random_pick": false, + "probability": 0.6 + }, + "ConvTransform": { + "kernel_type": "Laplace", + "absolute": false, + "retain_stats": false, + "probability": 0.15 + }, + "RedistributeTransform": { + "classes": null, + "in_seg": 0.2, + "retain_stats": false, + "probability": 0.5 + }, + "ShapeTransform": { + "shape_min": 1, + "ignore_axes": [1,2], + "probability": 0.4 + }, + "HistogramEqualTransform": { + "probability": 0.1 + }, + "FunctionTransform": { + "probability": 0.05 + }, + "GaussianNoiseTransform": { + "noise_variance": [0, 0.1], + "p_per_channel": 1, + "synchronize_channels": true, + "probability": 0.1 + }, + "GaussianBlurTransform": { + "blur_sigma": [0.5, 1.0], + "synchronize_channels": false, + "synchronize_axes": false, + "p_per_channel": 0.5, + "benchmark": true, + "probability": 0.2 + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": [0.75, 1.25], + "synchronize_channels": false, + "p_per_channel": 1, + "probability": 0.15 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "preserve_range": true, + "synchronize_channels": false, + "p_per_channel": 1, + "probability": 0.15 + }, + "SimulateLowResolutionTransform": { + "scale": [0.3, 1], + "synchronize_channels": true, + "synchronize_axes": false, + "ignore_axes": [], + "allowed_channels": null, + "p_per_channel": 0.5, + "probability": 0.2 + }, + "GammaTransform_invert": { + "gamma": [0.7, 1.5], + "p_invert_image": 1, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "probability": 0.1 + }, + "GammaTransform": { + "gamma": [0.7, 1.5], + "p_invert_image": 0, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "probability": 0.3 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu.json new file mode 100644 index 0000000..397eb91 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu.json @@ -0,0 +1,209 @@ +{ + "RandomPALETTETransform": { + "probability": 0.25, + "c_choices": [ + 2, + 3, + 4, + 5, + 6 + ], + "s_choices": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "blur_sigmas": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "dark_threshold": 0.01, + "n_kmeans_subsample": 10000, + "skip_parcellation_prob": 0.1, + "skip_sub_parc_prob": 0.4, + "alpha_magnitude_range": [ + 0.5, + 2.0 + ], + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "label_classes": null + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.1, 1.0], + "same_on_batch": false, + "probability": 0.2 + }, + "CropTransform": { + "crop": [0.5, 1.0], + "pos": [0.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noGE.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noGE.json new file mode 100644 index 0000000..32fd2e3 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noGE.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.00 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.0 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.5, + "p_scaling": 0.5, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json new file mode 100644 index 0000000..204c74b --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.05 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.25 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.05 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.5, + "p_scaling": 0.5, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-nobias.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-nobias.json new file mode 100644 index 0000000..b085063 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA-nobias.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.05 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.01 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.05 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.5, + "p_scaling": 0.5, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA.json new file mode 100644 index 0000000..1743064 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0-noTA.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.05 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.01 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.05 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.5, + "p_scaling": 0.5, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0.json new file mode 100644 index 0000000..9eacc73 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_basedon3d250af0.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.05 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.01 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.05 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.5, + "p_scaling": 0.5, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA05.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA05.json new file mode 100644 index 0000000..585a942 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA05.json @@ -0,0 +1,171 @@ +{ + "RandomChooseXTransforms": { + "ge_probability": 1.0, + "ta_probability": 0.5, + "ta_random_order": false, + "ge_random_order": false + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA08.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA08.json new file mode 100644 index 0000000..80c55ee --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List-TA08.json @@ -0,0 +1,169 @@ +{ + "RandomChooseXTransforms": { + "ge_probability": 1.0, + "ta_probability": 0.8 + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List.json new file mode 100644 index 0000000..3ad2f9b --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-List.json @@ -0,0 +1,169 @@ +{ + "RandomChooseXTransforms": { + "ge_probability": 1.0, + "ta_probability": 1.0 + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-Scharr.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-Scharr.json new file mode 100755 index 0000000..89fde45 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-Scharr.json @@ -0,0 +1,108 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.5 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-focus.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-focus.json new file mode 100644 index 0000000..8d5310a --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-focus.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.35 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.5 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noGE.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noGE.json new file mode 100644 index 0000000..705acbf --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noGE.json @@ -0,0 +1,96 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror-RandConv.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror-RandConv.json new file mode 100644 index 0000000..4034358 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror-RandConv.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.00 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.0 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.0, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror.json new file mode 100644 index 0000000..60f81aa --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noMirror.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.0 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noTA.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noTA.json new file mode 100644 index 0000000..1e0407b --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23-noTA.json @@ -0,0 +1,98 @@ +{ + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23.json new file mode 100755 index 0000000..c037ff6 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_CropTransform.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_CropTransform.json new file mode 100644 index 0000000..a58c7b5 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_CropTransform.json @@ -0,0 +1,171 @@ +{ + "CropTransform": { + "crop": [0.5, 1.0], + "pos": [0.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ICGT_plus.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ICGT_plus.json new file mode 100644 index 0000000..7625626 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ICGT_plus.json @@ -0,0 +1,170 @@ +{ + "ImageContrastGPUTransform": { + "num_bins": 32, + "label_classes": [0,1,2,3], + "probability": 0.5 + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ImageContrastGPUTransform.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ImageContrastGPUTransform.json new file mode 100644 index 0000000..9eb176e --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_ImageContrastGPUTransform.json @@ -0,0 +1,170 @@ +{ + "ImageContrastGPUTransform": { + "num_bins": 32, + "label_classes": [0,1,2,3], + "probability": 0.5 + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_RandomDomainTransferGPU.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_RandomDomainTransferGPU.json new file mode 100644 index 0000000..b215275 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_RandomDomainTransferGPU.json @@ -0,0 +1,180 @@ +{ + "RandomDomainTransferGPU": { + "source_label": "spinegan_ct", + "sigma": 2.0, + "probability": 0.5, + "include_self": true, + "any_source": true, + "blend_targets": 1, + "blend_concentration": 0.5, + "p_class_mix": 0.0, + "bias_field_std": 0.0, + "bias_scale": 0.03, + "p_spatial_mix": 0.0, + "spatial_mix_scale": 0.03, + "spatial_mix_gain": 3.0 + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_Synthseg.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_Synthseg.json new file mode 100755 index 0000000..3a20945 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_Synthseg.json @@ -0,0 +1,213 @@ +{ + "SynthSeg": { + "probability": 1.0, + "n_channels": 1, + "generation_labels": null, + "output_labels": null, + "n_neutral_labels": null, + "generation_classes": null, + + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + + "flipping": true, + "flip_axis": 2, + "scaling_bounds": 0.2, + "rotation_bounds": 15.0, + "shearing_bounds": 0.012, + "translation_bounds": false, + + "nonlin_std": 4.0, + "nonlin_scale": 0.04, + "svf_integration_steps": 7, + + "bias_field_std": 0.7, + "bias_scale": 0.025, + + "gamma_std": 0.5, + "clip": 300.0, + "normalise": true, + + "randomise_res": true, + "max_res_iso": 4.0, + "max_res_aniso": 8.0, + "data_res": null, + "thickness": null, + "blur_range": 1.03, + "atlas_res": 1.0, + + "output_shape": null, + + "em_label_completion": false, + "em_n_foreground_clusters": 2, + "em_background_clusters_range": [3, 10], + "em_background_label": 0, + "em_n_iters": 20, + "em_max_fit_voxels": 100000, + "em_same_on_batch": false + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_SynthsegPlus.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_SynthsegPlus.json new file mode 100644 index 0000000..43b7e34 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_default01-23_SynthsegPlus.json @@ -0,0 +1,213 @@ +{ + "SynthSeg": { + "probability": 0.25, + "n_channels": 1, + "generation_labels": null, + "output_labels": null, + "n_neutral_labels": null, + "generation_classes": null, + + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + + "flipping": true, + "flip_axis": 2, + "scaling_bounds": 0.2, + "rotation_bounds": 15.0, + "shearing_bounds": 0.012, + "translation_bounds": false, + + "nonlin_std": 4.0, + "nonlin_scale": 0.04, + "svf_integration_steps": 7, + + "bias_field_std": 0.7, + "bias_scale": 0.025, + + "gamma_std": 0.5, + "clip": 300.0, + "normalise": true, + + "randomise_res": true, + "max_res_iso": 4.0, + "max_res_aniso": 8.0, + "data_res": null, + "thickness": null, + "blur_range": 1.03, + "atlas_res": 1.0, + + "output_shape": null, + + "em_label_completion": true, + "em_n_foreground_clusters": 2, + "em_background_clusters_range": [3, 10], + "em_background_label": 0, + "em_n_iters": 20, + "em_max_fit_voxels": 100000, + "em_same_on_batch": false + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_inoutseg.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_inoutseg.json new file mode 100644 index 0000000..260c4b6 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_inoutseg.json @@ -0,0 +1,155 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.15 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.50, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.15 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.10 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.10 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.1 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.15 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.05 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.5 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.50 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.30 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.5 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.025 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.1 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.80, + "probability": 0.10 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.05 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.5 + }, + "ZscoreNormalizationTransform": { + "probability": 0.0 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_palette-em.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_palette-em.json new file mode 100644 index 0000000..c402486 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_gpu_palette-em.json @@ -0,0 +1,232 @@ +{ + "PaletteSynthesisTransform": { + "probability": 0.25, + "dark_threshold": 0.01, + "alpha_magnitude_range": [ + 0.5, + 2.0 + ], + "blur_sigmas_pre": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "blur_sigmas_post": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "initial_partitioner": { + "type": "em_gmm", + "n_foreground_clusters": 3, + "background_clusters_range": [ + 3, + 8 + ], + "background_label": 0, + "n_iters": 20, + "max_fit_voxels": 100000 + }, + "refinement_partitioners": [ + { + "type": "voronoi", + "s_choices": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "skip_prob": 0.4 + } + ], + "overlay": { + "enabled": true, + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "label_classes": null, + "blend_strength": 1.0, + "alpha_magnitude_range": [ + 0.5, + 2.0 + ] + } + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3, 5, 7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.1, 1.0], + "same_on_batch": false, + "probability": 0.2 + }, + "CropTransform": { + "crop": [0.5, 1.0], + "pos": [0.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid.json new file mode 100644 index 0000000..d27cce8 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid.json @@ -0,0 +1,262 @@ +{ + "CPU": { + "Comment1": "AugLab custom CPU augmentations", + "ConvTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": false, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": false, + "probability": 0.05 + }, + "HistogramEqualTransform": { + "retain_stats": false, + "probability": 0.1 + }, + "RedistributeTransform": { + "in_seg": 0.2, + "retain_stats": false, + "probability": 0.5 + }, + "ShapeTransform": { + "shape_min": 1, + "ignore_axes": [1,2], + "probability": 0.4 + }, + "ArtifactTransform":{ + "motion": true, + "ghosting": true, + "spike": true, + "bias_field": true, + "blur": true, + "noise": true, + "swap": false, + "random_pick": true, + "probability": 0 + }, + "SpatialCustomTransform": { + "flip": true, + "affine": true, + "elastic": true, + "anisotropy": true, + "random_pick": true, + "probability": 0 + }, + + "Comment2": "nnUNet default CPU augmentations", + "SpatialTransform": { + "patch_center_dist_from_border": 0, + "random_crop": false, + "p_elastic_deform": 0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "GaussianNoiseTransform": { + "noise_variance": [0, 0.1], + "p_per_channel": 1, + "synchronize_channels": true, + "probability": 0.1 + }, + "GaussianBlurTransform": { + "blur_sigma": [0.5, 1.0], + "synchronize_channels": false, + "synchronize_axes": false, + "p_per_channel": 0.5, + "benchmark": true, + "probability": 0.2 + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": [0.75, 1.25], + "synchronize_channels": false, + "p_per_channel": 1, + "probability": 0.15 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "preserve_range": true, + "synchronize_channels": false, + "p_per_channel": 1, + "probability": 0.15 + }, + "SimulateLowResolutionTransform": { + "scale": [0.3, 1], + "synchronize_channels": true, + "synchronize_axes": false, + "ignore_axes": [], + "p_per_channel": 0.5, + "probability": 0.2 + }, + "GammaTransform_invert": { + "gamma": [0.7, 1.5], + "p_invert_image": 1, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "probability": 0.1 + }, + "GammaTransform": { + "gamma": [0.7, 1.5], + "p_invert_image": 0, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "probability": 0.3 + } + }, + "GPU": { + "Comment1": "AugLab custom GPU augmentations", + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.15 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": false, + "probability": 0.10 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [1,3,5,7], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "mix_prob": 0.20, + "probability": 0.10 + }, + "RedistributeSegTransform": { + "in_seg": 0.2, + "retain_stats": false, + "probability": 0.3 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 0.1, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.40 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": false, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": false, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.05 + }, + "InverseTransform": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.30 + }, + "HistogramEqualizationTransform": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.20 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.2, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.40 + }, + "BiasFieldTransform": { + "retain_stats": false, + "coefficients": 0.5, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": false, + "probability": 0.20 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0 + }, + "AffineTransform": { + "degrees": 10, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-10, 10, -10, 10, -10, 10], + "resample": "bilinear", + "probability": 0 + }, + "ZscoreNormalizationTransform": { + "probability": 0.3 + } + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid_TAGE.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid_TAGE.json new file mode 100644 index 0000000..9382fc6 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_hybrid_TAGE.json @@ -0,0 +1,264 @@ +{ + "CPU": { + "Comment1": "CPU transfer augmentations (TA)", + "ConvTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": false, + "probability": 0.15 + }, + "HistogramEqualTransform": { + "retain_stats": false, + "probability": 0.1 + }, + "RedistributeTransform": { + "in_seg": 0.2, + "retain_stats": false, + "probability": 0.5 + }, + + "Comment2": "CPU general enhancement (GE)", + "FunctionTransform": { + "retain_stats": false, + "probability": 0.05 + }, + "ShapeTransform": { + "shape_min": 1, + "ignore_axes": [1,2], + "probability": 0.4 + }, + "ArtifactTransform":{ + "motion": true, + "ghosting": true, + "spike": true, + "bias_field": true, + "blur": true, + "noise": true, + "swap": false, + "random_pick": true, + "probability": 0 + }, + "SpatialCustomTransform": { + "flip": true, + "affine": true, + "elastic": true, + "anisotropy": true, + "random_pick": true, + "probability": 0 + }, + "SpatialTransform": { + "patch_center_dist_from_border": 0, + "random_crop": false, + "p_elastic_deform": 0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "GaussianNoiseTransform": { + "noise_variance": [0, 0.1], + "p_per_channel": 1, + "synchronize_channels": true, + "probability": 0.1 + }, + "GaussianBlurTransform": { + "blur_sigma": [0.5, 1.0], + "synchronize_channels": false, + "synchronize_axes": false, + "p_per_channel": 0.5, + "benchmark": true, + "probability": 0.2 + }, + "MultiplicativeBrightnessTransform": { + "multiplier_range": [0.75, 1.25], + "synchronize_channels": false, + "p_per_channel": 1, + "probability": 0.15 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "preserve_range": true, + "synchronize_channels": false, + "p_per_channel": 1, + "probability": 0.15 + }, + "SimulateLowResolutionTransform": { + "scale": [0.3, 1], + "synchronize_channels": true, + "synchronize_axes": false, + "ignore_axes": [], + "p_per_channel": 0.5, + "probability": 0.2 + }, + "GammaTransform_invert": { + "gamma": [0.7, 1.5], + "p_invert_image": 1, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "probability": 0.1 + }, + "GammaTransform": { + "gamma": [0.7, 1.5], + "p_invert_image": 0, + "synchronize_channels": false, + "p_per_channel": 1, + "p_retain_stats": 1, + "probability": 0.3 + } + }, + "GPU": { + "Comment1": "GPU transfer augmentations (TA)", + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.15 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [1,3,5,7], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "mix_prob": 0.20, + "probability": 0.10 + }, + "RedistributeSegTransform": { + "in_seg": 0.2, + "retain_stats": false, + "probability": 0.3 + }, + "InverseTransform": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.30 + }, + "HistogramEqualizationTransform": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.20 + }, + + "Comment2": "GPU general enhancement (GE)", + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": false, + "probability": 0.10 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 0.1, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.40 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": false, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": false, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": false, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": true, + "probability": 0.05 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [0.8, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.2, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.40 + }, + "BiasFieldTransform": { + "retain_stats": false, + "coefficients": 0.5, + "in_seg": 0.5, + "out_seg": 0.5, + "mix_in_out": false, + "probability": 0.20 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0 + }, + "AffineTransform": { + "degrees": 10, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-10, 10, -10, 10, -10, 10], + "resample": "bilinear", + "probability": 0 + }, + "ZscoreNormalizationTransform": { + "probability": 0 + } + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs/transform_params_one-sequence-to-segment-them-all.json b/unit_tests/fixtures/legacy_configs/configs/transform_params_one-sequence-to-segment-them-all.json new file mode 100755 index 0000000..c037ff6 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs/transform_params_one-sequence-to-segment-them-all.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23.json b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23.json new file mode 100755 index 0000000..c037ff6 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23.json @@ -0,0 +1,165 @@ +{ + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.20 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_ImageContrastV26_6_2GPUTransform.json b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_ImageContrastV26_6_2GPUTransform.json new file mode 100755 index 0000000..0afb60a --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_ImageContrastV26_6_2GPUTransform.json @@ -0,0 +1,179 @@ +{ + "ImageContrastV26_6_2GPUTransform": { + "probability": 0.5, + "c_choices": [2, 3, 4, 5, 6], + "s_choices": [2, 3, 4, 5, 6, 7, 8, 9, 10], + "blur_sigmas": [0.0, 0.0, 0.0, 0.3, 0.5, 0.8], + "dark_threshold": 0.01, + "n_kmeans_subsample": 10000, + "skip_parcellation_prob": 0.10, + "skip_sub_parc_prob": 0.40, + "alpha_magnitude_range": [0.5, 2.0], + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "label_classes": null + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3, 5, 7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg.json b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg.json new file mode 100755 index 0000000..3a20945 --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg.json @@ -0,0 +1,213 @@ +{ + "SynthSeg": { + "probability": 1.0, + "n_channels": 1, + "generation_labels": null, + "output_labels": null, + "n_neutral_labels": null, + "generation_classes": null, + + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + + "flipping": true, + "flip_axis": 2, + "scaling_bounds": 0.2, + "rotation_bounds": 15.0, + "shearing_bounds": 0.012, + "translation_bounds": false, + + "nonlin_std": 4.0, + "nonlin_scale": 0.04, + "svf_integration_steps": 7, + + "bias_field_std": 0.7, + "bias_scale": 0.025, + + "gamma_std": 0.5, + "clip": 300.0, + "normalise": true, + + "randomise_res": true, + "max_res_iso": 4.0, + "max_res_aniso": 8.0, + "data_res": null, + "thickness": null, + "blur_range": 1.03, + "atlas_res": 1.0, + + "output_shape": null, + + "em_label_completion": false, + "em_n_foreground_clusters": 2, + "em_background_clusters_range": [3, 10], + "em_background_label": 0, + "em_n_iters": 20, + "em_max_fit_voxels": 100000, + "em_same_on_batch": false + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg_EM.json b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg_EM.json new file mode 100755 index 0000000..ce17cdc --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_Synthseg_EM.json @@ -0,0 +1,213 @@ +{ + "SynthSeg": { + "probability": 1.0, + "n_channels": 1, + "generation_labels": null, + "output_labels": null, + "n_neutral_labels": null, + "generation_classes": null, + + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + + "flipping": true, + "flip_axis": 2, + "scaling_bounds": 0.2, + "rotation_bounds": 15.0, + "shearing_bounds": 0.012, + "translation_bounds": false, + + "nonlin_std": 4.0, + "nonlin_scale": 0.04, + "svf_integration_steps": 7, + + "bias_field_std": 0.7, + "bias_scale": 0.025, + + "gamma_std": 0.5, + "clip": 300.0, + "normalise": true, + + "randomise_res": true, + "max_res_iso": 4.0, + "max_res_aniso": 8.0, + "data_res": null, + "thickness": null, + "blur_range": 1.03, + "atlas_res": 1.0, + + "output_shape": null, + + "em_label_completion": true, + "em_n_foreground_clusters": 2, + "em_background_clusters_range": [3, 10], + "em_background_label": 0, + "em_n_iters": 20, + "em_max_fit_voxels": 100000, + "em_same_on_batch": false + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.50, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.00, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.20 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [3,5,7], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.50, + "probability": 0.0 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.0 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.00 + }, + "BrightnessTransform": { + "brightness_range": [0.75, 1.25], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.30 + }, + "InvGammaTransform": { + "gamma_range": [0.7, 1.5], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.10 + }, + "ContrastTransform": { + "contrast_range": [0.75, 1.25], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.00, + "probability": 0.0 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.60, + "probability": 0.0 + }, + "SimulateLowResTransform": { + "scale": [0.5, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [0.6, 1.0], + "crop": [1.0, 1.0], + "same_on_batch": false, + "probability": 0.00 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [0], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [0.1, 0.1, 0.1], + "scale": [0.7, 1.4], + "shear": [-5, 5, -5, 5, -5, 5], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [0.7, 1.4], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.00 + } +} diff --git a/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_smauglabAug_ImageContrastV26_6_2GPUTransform_train025.json b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_smauglabAug_ImageContrastV26_6_2GPUTransform_train025.json new file mode 100755 index 0000000..9ab2d4a --- /dev/null +++ b/unit_tests/fixtures/legacy_configs/configs_paul/transform_params_gpu_default01-23_smauglabAug_ImageContrastV26_6_2GPUTransform_train025.json @@ -0,0 +1,252 @@ +{ + "ImageContrastV26_6_2GPUTransform": { + "probability": 0.25, + "c_choices": [ + 2, + 3, + 4, + 5, + 6 + ], + "s_choices": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "blur_sigmas": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "dark_threshold": 0.01, + "n_kmeans_subsample": 10000, + "skip_parcellation_prob": 0.1, + "skip_sub_parc_prob": 0.4, + "alpha_magnitude_range": [ + 0.5, + 2.0 + ], + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "label_classes": null + }, + "ScharrTransform": { + "kernel_type": "Scharr", + "absolute": true, + "retain_stats": true, + "mix_prob": 0.5, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.25 + }, + "GaussianBlurTransform": { + "kernel_type": "GaussianBlur", + "sigma": 1.0, + "retain_stats": false, + "mix_prob": 0.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.2 + }, + "UnsharpMaskTransform": { + "kernel_type": "UnsharpMask", + "sigma": 1.0, + "unsharp_amount": 1.5, + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.5, + "probability": 0.25 + }, + "RandomConvTransform": { + "kernel_type": "RandConv", + "kernel_sizes": [ + 3, + 5, + 7 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.5, + "probability": 0.2 + }, + "RedistributeSegTransform": { + "in_seg": 0.25, + "retain_stats": true, + "probability": 0.4 + }, + "GaussianNoiseTransform": { + "mean": 0.0, + "std": 1.0, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ClampTransform": { + "max_clamp_amount": 0.2, + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.0 + }, + "BrightnessTransform": { + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "GammaTransform": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.3 + }, + "InvGammaTransform": { + "gamma_range": [ + 0.7, + 1.5 + ], + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.1 + }, + "ContrastTransform": { + "contrast_range": [ + 0.75, + 1.25 + ], + "retain_stats": false, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FunctionTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": false, + "probability": 0.1 + }, + "InverseTransform": { + "retain_stats": true, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "mix_prob": 0.0, + "probability": 0.25 + }, + "HistogramEqualizationTransform": { + "retain_stats": true, + "in_seg": 0, + "out_seg": 0, + "mix_in_out": true, + "mix_prob": 0.6, + "probability": 0.25 + }, + "SimulateLowResTransform": { + "scale": [ + 0.5, + 1.0 + ], + "crop": [ + 1.0, + 1.0 + ], + "same_on_batch": false, + "probability": 0.25 + }, + "AcqTransform": { + "scale": [ + 0.6, + 1.0 + ], + "crop": [ + 1.0, + 1.0 + ], + "same_on_batch": false, + "probability": 0.0 + }, + "BiasFieldTransform": { + "retain_stats": true, + "coefficients": 0.2, + "in_seg": 0.0, + "out_seg": 0.0, + "mix_in_out": true, + "probability": 0.15 + }, + "FlipTransform": { + "flip_axis": [ + 0 + ], + "same_on_batch": false, + "keepdim": true, + "probability": 0.5 + }, + "AffineTransform": { + "degrees": 5, + "translate": [ + 0.1, + 0.1, + 0.1 + ], + "scale": [ + 0.7, + 1.4 + ], + "shear": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "resample": "bilinear", + "probability": 0.0 + }, + "nnUNetSpatialTransform": { + "patch_center_dist_from_border": 80, + "random_crop": false, + "p_elastic_deform": 0.0, + "p_rotation": 0.2, + "p_scaling": 0.2, + "scaling": [ + 0.7, + 1.4 + ], + "p_synchronize_scaling_across_axes": 1, + "bg_style_seg_sampling": false + }, + "ZscoreNormalizationTransform": { + "probability": 0.0 + } +} diff --git a/unit_tests/fixtures/legacy_effective_kwargs.json b/unit_tests/fixtures/legacy_effective_kwargs.json new file mode 100644 index 0000000..a76d360 --- /dev/null +++ b/unit_tests/fixtures/legacy_effective_kwargs.json @@ -0,0 +1,11951 @@ +{ + "synthseg_params.json::GPU": [ + { + "cls": "RandomSynthSegGPU", + "kwargs": { + "apply_to_channel": null, + "atlas_res": 1.0, + "bias_field_std": 0.7, + "bias_scale": 0.025, + "blur_range": 1.03, + "clip": 300.0, + "data_res": null, + "em_background_clusters_range": [ + 3, + 10 + ], + "em_background_label": 0, + "em_label_completion": false, + "em_max_fit_voxels": 100000, + "em_n_foreground_clusters": 2, + "em_n_iters": 20, + "em_same_on_batch": false, + "flip_axis": 2, + "flipping": true, + "gamma_std": 0.5, + "generation_classes": null, + "generation_labels": null, + "keepdim": true, + "max_res_aniso": 8.0, + "max_res_iso": 4.0, + "n_channels": 1, + "n_neutral_labels": null, + "nonlin_scale": 0.04, + "nonlin_std": 4.0, + "normalise": true, + "output_labels": null, + "output_shape": null, + "p": 1.0, + "p_batch": 1.0, + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + "randomise_res": true, + "rotation_bounds": 15.0, + "same_on_batch": false, + "scaling_bounds": 0.2, + "shearing_bounds": 0.012, + "svf_integration_steps": 7, + "thickness": null, + "translation_bounds": false + } + } + ], + "transform_params.json::CPU": [ + { + "cls": "LaplaceConvTransform", + "kwargs": { + "absolute": false, + "p": 0.15, + "retain_stats": true + } + }, + { + "cls": "Log1pTransform", + "kwargs": { + "p": 0.05, + "retain_stats": true + } + }, + { + "cls": "SqrtTransform", + "kwargs": { + "p": 0.05, + "retain_stats": true + } + }, + { + "cls": "SinTransform", + "kwargs": { + "p": 0.05, + "retain_stats": true + } + }, + { + "cls": "ExpTransform", + "kwargs": { + "p": 0.05, + "retain_stats": true + } + }, + { + "cls": "SigmoidTransform", + "kwargs": { + "p": 0.05, + "retain_stats": true + } + }, + { + "cls": "HistogramEqualTransform", + "kwargs": { + "p": 0.1, + "retain_stats": true + } + }, + { + "cls": "RedistributeTransform", + "kwargs": { + "classes": null, + "in_seg": 0.2, + "p": 0.5, + "retain_stats": true + } + }, + { + "cls": "ShapeTransform", + "kwargs": { + "ignore_axes": [ + 1, + 2 + ], + "p": 0.4, + "shape_min": 1 + } + }, + { + "cls": "ArtifactTransform", + "kwargs": { + "bias_field": false, + "blur": false, + "ghosting": false, + "motion": false, + "noise": false, + "p": 0.7, + "random_pick": false, + "spike": false, + "swap": false + } + }, + { + "cls": "SpatialCustomTransform", + "kwargs": { + "affine": false, + "anisotropy": false, + "elastic": false, + "flip": false, + "p": 0.6, + "random_pick": false + } + }, + { + "cls": "GaussianNoiseTransform", + "kwargs": { + "noise_variance": [ + 0, + 0.1 + ], + "p": 0.1, + "p_per_channel": 1, + "synchronize_channels": true + } + }, + { + "cls": "GaussianBlurTransform", + "kwargs": { + "benchmark": true, + "blur_sigma": [ + 0.5, + 1.0 + ], + "p": 0.2, + "p_per_channel": 0.5, + "synchronize_axes": false, + "synchronize_channels": false + } + }, + { + "cls": "MultiplicativeBrightnessTransform", + "kwargs": { + "multiplier_range": "BGContrast(contrast_range=(0.75, 1.25))", + "p": 0.15, + "p_per_channel": 1, + "synchronize_channels": false + } + }, + { + "cls": "ContrastTransform", + "kwargs": { + "contrast_range": "BGContrast(contrast_range=(0.75, 1.25))", + "p": 0.15, + "p_per_channel": 1, + "preserve_range": true, + "synchronize_channels": false + } + }, + { + "cls": "SimulateLowResolutionTransform", + "kwargs": { + "allowed_channels": null, + "ignore_axes": [], + "p": 0.2, + "p_per_channel": 0.5, + "scale": [ + 0.3, + 1 + ], + "synchronize_axes": false, + "synchronize_channels": true + } + }, + { + "cls": "InvertedGammaTransform", + "kwargs": { + "gamma": "BGContrast(contrast_range=(0.7, 1.5))", + "p": 0.1, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + } + }, + { + "cls": "GammaTransform", + "kwargs": { + "gamma": "BGContrast(contrast_range=(0.7, 1.5))", + "p": 0.3, + "p_invert_image": 0, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + } + }, + { + "cls": "MirrorTransform", + "kwargs": { + "allowed_axes": [ + 0, + 1, + 2 + ] + } + } + ], + "transform_params_gpu.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomPaletteGPU", + "kwargs": { + "alpha_magnitude_range": [ + 0.5, + 2.0 + ], + "blur_sigmas": [ + 0.0, + 0.0, + 0.0, + 0.3, + 0.5, + 0.8 + ], + "c_choices": [ + 2, + 3, + 4, + 5, + 6 + ], + "dark_threshold": 0.01, + "keepdim": false, + "label_classes": null, + "label_remap_prob": 0.5, + "min_label_voxels": 4, + "n_kmeans_subsample": 10000, + "p": 0.25, + "p_batch": 1.0, + "s_choices": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "same_on_batch": false, + "skip_parcellation_prob": 0.1, + "skip_sub_parc_prob": 0.4 + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.2, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.1, + 1.0 + ] + } + }, + { + "cls": "RandomCropTransformGPU", + "kwargs": { + "crop": [ + 0.5, + 1.0 + ], + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "pos": [ + 0.0, + 1.0 + ], + "same_on_batch": false + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_basedon3d250af0-noGE.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_basedon3d250af0-noTA-heavyFunction.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.05, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_basedon3d250af0-noTA-nobias.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.05, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_basedon3d250af0-noTA.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.05, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_basedon3d250af0.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.01, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.05, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-List-TA05.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-List-TA08.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-List.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-Scharr.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-focus.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.35, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-noGE.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-noMirror-RandConv.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-noMirror.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23-noTA.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23_CropTransform.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomCropTransformGPU", + "kwargs": { + "crop": [ + 0.5, + 1.0 + ], + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "pos": [ + 0.0, + 1.0 + ], + "same_on_batch": false + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23_ICGT_plus.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23_ImageContrastGPUTransform.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23_RandomDomainTransferGPU.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomDomainTransferGPU", + "kwargs": { + "any_source": true, + "apply_to_channel": [ + 0 + ], + "bank_path": null, + "bias_field_std": 0.0, + "bias_scale": 0.03, + "blend_concentration": 0.5, + "blend_targets": 1, + "include_self": true, + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "p_class_mix": 0.0, + "p_spatial_mix": 0.0, + "pct": 1.0, + "same_on_batch": false, + "sigma": 2.0, + "source_label": "spinegan_ct", + "spatial_mix_gain": 3.0, + "spatial_mix_scale": 0.03, + "targets": null, + "zscore_io": "auto" + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23_Synthseg.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomSynthSegGPU", + "kwargs": { + "apply_to_channel": null, + "atlas_res": 1.0, + "bias_field_std": 0.7, + "bias_scale": 0.025, + "blur_range": 1.03, + "clip": 300.0, + "data_res": null, + "em_background_clusters_range": [ + 3, + 10 + ], + "em_background_label": 0, + "em_label_completion": false, + "em_max_fit_voxels": 100000, + "em_n_foreground_clusters": 2, + "em_n_iters": 20, + "em_same_on_batch": false, + "flip_axis": 2, + "flipping": true, + "gamma_std": 0.5, + "generation_classes": null, + "generation_labels": null, + "keepdim": true, + "max_res_aniso": 8.0, + "max_res_iso": 4.0, + "n_channels": 1, + "n_neutral_labels": null, + "nonlin_scale": 0.04, + "nonlin_std": 4.0, + "normalise": true, + "output_labels": null, + "output_shape": null, + "p": 1.0, + "p_batch": 1.0, + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + "randomise_res": true, + "rotation_bounds": 15.0, + "same_on_batch": false, + "scaling_bounds": 0.2, + "shearing_bounds": 0.012, + "svf_integration_steps": 7, + "thickness": null, + "translation_bounds": false + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_default01-23_SynthsegPlus.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomSynthSegGPU", + "kwargs": { + "apply_to_channel": null, + "atlas_res": 1.0, + "bias_field_std": 0.7, + "bias_scale": 0.025, + "blur_range": 1.03, + "clip": 300.0, + "data_res": null, + "em_background_clusters_range": [ + 3, + 10 + ], + "em_background_label": 0, + "em_label_completion": true, + "em_max_fit_voxels": 100000, + "em_n_foreground_clusters": 2, + "em_n_iters": 20, + "em_same_on_batch": false, + "flip_axis": 2, + "flipping": true, + "gamma_std": 0.5, + "generation_classes": null, + "generation_labels": null, + "keepdim": true, + "max_res_aniso": 8.0, + "max_res_iso": 4.0, + "n_channels": 1, + "n_neutral_labels": null, + "nonlin_scale": 0.04, + "nonlin_std": 4.0, + "normalise": true, + "output_labels": null, + "output_shape": null, + "p": 0.25, + "p_batch": 1.0, + "prior_distributions": "uniform", + "prior_means": null, + "prior_stds": null, + "randomise_res": true, + "rotation_bounds": 15.0, + "same_on_batch": false, + "scaling_bounds": 0.2, + "shearing_bounds": 0.012, + "svf_integration_steps": 7, + "thickness": null, + "translation_bounds": false + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_inoutseg.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.8, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.5, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.5, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.5, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.5, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.025, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.025, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.025, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.025, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.025, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.05, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.5, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_gpu_palette-em.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.2, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.1, + 1.0 + ] + } + }, + { + "cls": "RandomCropTransformGPU", + "kwargs": { + "crop": [ + 0.5, + 1.0 + ], + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "pos": [ + 0.0, + 1.0 + ], + "same_on_batch": false + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ], + "transform_params_hybrid.json::CPU": [ + { + "cls": "ScharrConvTransform", + "kwargs": { + "absolute": true, + "p": 0.15, + "retain_stats": false + } + }, + { + "cls": "Log1pTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "SqrtTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "SinTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "ExpTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "SigmoidTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "HistogramEqualTransform", + "kwargs": { + "p": 0.1, + "retain_stats": false + } + }, + { + "cls": "RedistributeTransform", + "kwargs": { + "classes": null, + "in_seg": 0.2, + "p": 0.5, + "retain_stats": false + } + }, + { + "cls": "ShapeTransform", + "kwargs": { + "ignore_axes": [ + 1, + 2 + ], + "p": 0.4, + "shape_min": 1 + } + }, + { + "cls": "ArtifactTransform", + "kwargs": { + "bias_field": true, + "blur": true, + "ghosting": true, + "motion": true, + "noise": true, + "p": 0, + "random_pick": true, + "spike": true, + "swap": false + } + }, + { + "cls": "SpatialCustomTransform", + "kwargs": { + "affine": true, + "anisotropy": true, + "elastic": true, + "flip": true, + "p": 0, + "random_pick": true + } + }, + { + "cls": "SpatialTransform", + "kwargs": { + "align_corners": false, + "bg_style_seg_sampling": false, + "border_mode_seg": "zeros", + "center_deformation": true, + "elastic_deform_magnitude": [ + 0, + 0.2 + ], + "elastic_deform_scale": [ + 0, + 0.2 + ], + "mode_image": "bilinear", + "mode_seg": "nearest", + "p_elastic_deform": 0, + "p_rot_per_axis": 1, + "p_rotation": 0.2, + "p_scaling": 0.2, + "p_synchronize_def_scale_across_axes": 0, + "p_synchronize_scaling_across_axes": 1, + "padding_mode_image": "zeros", + "padding_value_image": 0, + "padding_value_seg": 0, + "patch_center_dist_from_border": 0, + "patch_size": [ + 24, + 24, + 24 + ], + "random_crop": false, + "rotation": [ + -10, + 10 + ], + "scaling": [ + 0.7, + 1.4 + ] + } + }, + { + "cls": "GaussianNoiseTransform", + "kwargs": { + "noise_variance": [ + 0, + 0.1 + ], + "p": 0.1, + "p_per_channel": 1, + "synchronize_channels": true + } + }, + { + "cls": "GaussianBlurTransform", + "kwargs": { + "benchmark": true, + "blur_sigma": [ + 0.5, + 1.0 + ], + "p": 0.2, + "p_per_channel": 0.5, + "synchronize_axes": false, + "synchronize_channels": false + } + }, + { + "cls": "MultiplicativeBrightnessTransform", + "kwargs": { + "multiplier_range": "BGContrast(contrast_range=(0.75, 1.25))", + "p": 0.15, + "p_per_channel": 1, + "synchronize_channels": false + } + }, + { + "cls": "ContrastTransform", + "kwargs": { + "contrast_range": "BGContrast(contrast_range=(0.75, 1.25))", + "p": 0.15, + "p_per_channel": 1, + "preserve_range": true, + "synchronize_channels": false + } + }, + { + "cls": "SimulateLowResolutionTransform", + "kwargs": { + "allowed_channels": null, + "ignore_axes": [], + "p": 0.2, + "p_per_channel": 0.5, + "scale": [ + 0.3, + 1 + ], + "synchronize_axes": false, + "synchronize_channels": true + } + }, + { + "cls": "InvertedGammaTransform", + "kwargs": { + "gamma": "BGContrast(contrast_range=(0.7, 1.5))", + "p": 0.1, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + } + }, + { + "cls": "GammaTransform", + "kwargs": { + "gamma": "BGContrast(contrast_range=(0.7, 1.5))", + "p": 0.3, + "p_invert_image": 0, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + } + } + ], + "transform_params_hybrid.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 10, + "keepdim": true, + "p": 0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -10, + 10, + -10, + 10, + -10, + 10 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.2, + "keepdim": true, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "kernel_sizes": [ + 1, + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.2, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 0.1 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.5, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.5, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.2, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.5, + "in_seg": 0.5, + "invert": false, + "keepdim": true, + "mix_in_out": false, + "order": 3, + "out_seg": 0.5, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0 + } + } + ], + "transform_params_hybrid_TAGE.json::CPU": [ + { + "cls": "ScharrConvTransform", + "kwargs": { + "absolute": true, + "p": 0.15, + "retain_stats": false + } + }, + { + "cls": "Log1pTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "SqrtTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "SinTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "ExpTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "SigmoidTransform", + "kwargs": { + "p": 0.05, + "retain_stats": false + } + }, + { + "cls": "HistogramEqualTransform", + "kwargs": { + "p": 0.1, + "retain_stats": false + } + }, + { + "cls": "RedistributeTransform", + "kwargs": { + "classes": null, + "in_seg": 0.2, + "p": 0.5, + "retain_stats": false + } + }, + { + "cls": "ShapeTransform", + "kwargs": { + "ignore_axes": [ + 1, + 2 + ], + "p": 0.4, + "shape_min": 1 + } + }, + { + "cls": "ArtifactTransform", + "kwargs": { + "bias_field": true, + "blur": true, + "ghosting": true, + "motion": true, + "noise": true, + "p": 0, + "random_pick": true, + "spike": true, + "swap": false + } + }, + { + "cls": "SpatialCustomTransform", + "kwargs": { + "affine": true, + "anisotropy": true, + "elastic": true, + "flip": true, + "p": 0, + "random_pick": true + } + }, + { + "cls": "SpatialTransform", + "kwargs": { + "align_corners": false, + "bg_style_seg_sampling": false, + "border_mode_seg": "zeros", + "center_deformation": true, + "elastic_deform_magnitude": [ + 0, + 0.2 + ], + "elastic_deform_scale": [ + 0, + 0.2 + ], + "mode_image": "bilinear", + "mode_seg": "nearest", + "p_elastic_deform": 0, + "p_rot_per_axis": 1, + "p_rotation": 0.2, + "p_scaling": 0.2, + "p_synchronize_def_scale_across_axes": 0, + "p_synchronize_scaling_across_axes": 1, + "padding_mode_image": "zeros", + "padding_value_image": 0, + "padding_value_seg": 0, + "patch_center_dist_from_border": 0, + "patch_size": [ + 24, + 24, + 24 + ], + "random_crop": false, + "rotation": [ + -10, + 10 + ], + "scaling": [ + 0.7, + 1.4 + ] + } + }, + { + "cls": "GaussianNoiseTransform", + "kwargs": { + "noise_variance": [ + 0, + 0.1 + ], + "p": 0.1, + "p_per_channel": 1, + "synchronize_channels": true + } + }, + { + "cls": "GaussianBlurTransform", + "kwargs": { + "benchmark": true, + "blur_sigma": [ + 0.5, + 1.0 + ], + "p": 0.2, + "p_per_channel": 0.5, + "synchronize_axes": false, + "synchronize_channels": false + } + }, + { + "cls": "MultiplicativeBrightnessTransform", + "kwargs": { + "multiplier_range": "BGContrast(contrast_range=(0.75, 1.25))", + "p": 0.15, + "p_per_channel": 1, + "synchronize_channels": false + } + }, + { + "cls": "ContrastTransform", + "kwargs": { + "contrast_range": "BGContrast(contrast_range=(0.75, 1.25))", + "p": 0.15, + "p_per_channel": 1, + "preserve_range": true, + "synchronize_channels": false + } + }, + { + "cls": "SimulateLowResolutionTransform", + "kwargs": { + "allowed_channels": null, + "ignore_axes": [], + "p": 0.2, + "p_per_channel": 0.5, + "scale": [ + 0.3, + 1 + ], + "synchronize_axes": false, + "synchronize_channels": true + } + }, + { + "cls": "InvertedGammaTransform", + "kwargs": { + "gamma": "BGContrast(contrast_range=(0.7, 1.5))", + "p": 0.1, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + } + }, + { + "cls": "GammaTransform", + "kwargs": { + "gamma": "BGContrast(contrast_range=(0.7, 1.5))", + "p": 0.3, + "p_invert_image": 0, + "p_per_channel": 1, + "p_retain_stats": 1, + "synchronize_channels": false + } + } + ], + "transform_params_hybrid_TAGE.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 10, + "keepdim": true, + "p": 0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -10, + 10, + -10, + 10, + -10, + 10 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.2, + "keepdim": true, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "kernel_sizes": [ + 1, + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.2, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 0.1 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": false, + "mix_prob": 0.0, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.5, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.5, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.5, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.5, + "p": 0.05, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.2, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.5, + "in_seg": 0.5, + "invert": false, + "keepdim": true, + "mix_in_out": false, + "order": 3, + "out_seg": 0.5, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0, + "p_batch": 1.0 + } + } + ], + "transform_params_one-sequence-to-segment-them-all.json::GPU": [ + { + "cls": "RandomFlipTransformGPU", + "kwargs": { + "flip_axis": [ + 0 + ], + "keepdim": true, + "p": 0.5, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomAffineGPU", + "kwargs": { + "align_corners": true, + "degrees": 5, + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "resample": "bilinear", + "same_on_batch": false, + "scale": [ + 0.7, + 1.4 + ], + "shears": [ + -5, + 5, + -5, + 5, + -5, + 5 + ], + "translate": [ + 0.1, + 0.1, + 0.1 + ] + } + }, + { + "cls": "RandomInverseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomHistogramEqualizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.6, + "out_seg": 0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomRedistributeSegGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "dilation_iterations_range": [ + 1, + 3 + ], + "in_seg": 0.25, + "keepdim": true, + "p": 0.4, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false, + "std_noise_range": [ + 0.1, + 0.3 + ] + } + }, + { + "cls": "RandomScharrGPU", + "kwargs": { + "absolute": true, + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomUnsharpMaskGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.25, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0, + "unsharp_amount": 1.5 + } + }, + { + "cls": "RandomRandConvGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "kernel_sizes": [ + 3, + 5, + 7 + ], + "mix_in_out": true, + "mix_prob": 0.5, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomClampGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "max_clamp_amount": 0.2, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomGaussianNoiseGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mean": 0.0, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "same_on_batch": false, + "std": 1.0 + } + }, + { + "cls": "RandomGaussianBlurGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "mix_prob": 0.0, + "out_seg": 0.0, + "p": 0.2, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false, + "sigma": 1.0 + } + }, + { + "cls": "RandomBrightnessGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "brightness_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "same_on_batch": false + } + }, + { + "cls": "RandomGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.3, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomInvGammaGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "gamma_range": [ + 0.7, + 1.5 + ], + "in_seg": 0.0, + "keepdim": false, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomContrastGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "contrast_range": [ + 0.75, + 1.25 + ], + "in_seg": 0.0, + "keepdim": true, + "mix_in_out": true, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": false, + "same_on_batch": false + } + }, + { + "cls": "RandomLog1pGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSqrtGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSinGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomExpGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomSigmoidGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0, + "keepdim": true, + "mix_in_out": false, + "out_seg": 0, + "p": 0.1, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "RandomLowResTransformGPU", + "kwargs": { + "keepdim": true, + "p": 0.25, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.5, + 1.0 + ] + } + }, + { + "cls": "RandomAcqTransformGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "keepdim": true, + "p": 0.0, + "p_batch": 1.0, + "same_on_batch": false, + "scale": [ + 0.6, + 1.0 + ] + } + }, + { + "cls": "RandomBiasFieldGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "coefficients": 0.2, + "in_seg": 0.0, + "invert": false, + "keepdim": true, + "mix_in_out": true, + "order": 3, + "out_seg": 0.0, + "p": 0.15, + "p_batch": 1.0, + "retain_stats": true, + "same_on_batch": false + } + }, + { + "cls": "ZscoreNormalizationGPU", + "kwargs": { + "apply_to_channel": [ + 0 + ], + "in_seg": 0.0, + "keepdim": true, + "out_seg": 0.0, + "p": 0.0, + "p_batch": 1.0 + } + } + ] +} diff --git a/unit_tests/helpers.py b/unit_tests/helpers.py index 53aacf2..d2e8416 100644 --- a/unit_tests/helpers.py +++ b/unit_tests/helpers.py @@ -87,20 +87,32 @@ def gpu_config_paths() -> list[Path]: return sorted(paths) -def requires_external_asset(config_path: Path) -> str | None: - """Return a skip reason if a config needs an asset that is not on this machine. +def domain_bank_missing() -> str | None: + """Return a skip reason if the domain-transfer LUT bank is not on this machine. - RandomDomainTransferGPU loads a precomputed histogram bank from an absolute - path baked into the module, which only exists on the authors' machines. - Rather than fail CI, skip those configs and say why. + The bank is a large offline-built artefact that ships with neither the wheel nor + the repo, so it is only present where someone has exported SMAUGLAB_DOMAIN_BANK. + Rather than fail CI, skip and say why. """ - from smauglab.transforms.gpu.domain_transfer import DEFAULT_BANK_PATH + from smauglab.transforms.gpu.domain_transfer import BANK_PATH_ENV_VAR, resolve_bank_path + + try: + resolve_bank_path() + except FileNotFoundError: + return f"domain transfer bank not available (set {BANK_PATH_ENV_VAR})" + return None + +def requires_external_asset(config_path: Path) -> str | None: + """Return a skip reason if a config needs an asset that is not on this machine.""" params = json.loads(config_path.read_text()) params = params.get("GPU", params) if not isinstance(params, dict): return None uses_transfer = params.get("RandomDomainTransferGPU") or params.get("DomainTransferTransform") - if uses_transfer and not Path(DEFAULT_BANK_PATH).is_file(): - return f"domain transfer bank not available at {DEFAULT_BANK_PATH}" - return None + if not uses_transfer: + return None + # A config may name its own bank; only fall back to the env var when it does not. + if isinstance(uses_transfer, dict) and uses_transfer.get("bank_path"): + return None if Path(uses_transfer["bank_path"]).is_file() else f"domain transfer bank not available at {uses_transfer['bank_path']}" + return domain_bank_missing() diff --git a/unit_tests/test_builder.py b/unit_tests/test_builder.py new file mode 100644 index 0000000..49f1032 --- /dev/null +++ b/unit_tests/test_builder.py @@ -0,0 +1,203 @@ +"""The registry-driven builder, and the strict loading in front of it. + +These pin the behaviours the four hand-written ladders used to encode implicitly: +pipeline order, the TA/GE bucketing of the random-order modes, the runtime context +nnU-Net supplies, and the parameter adapters. They also pin the failure modes -- +before this, a config naming a transform that did not exist simply did nothing. +""" + +from __future__ import annotations + +import json +import unittest +from pathlib import Path + +from smauglab import registry +from smauglab.config import SmaugConfig, load_config, validate_file +from smauglab.registry import Backend, InvalidConfigError +from smauglab.transforms.build import ( + PipelineMode, + build_cpu_pipeline, + build_gpu_pipeline, + build_transforms, + validate_section, +) + +REPO = Path(__file__).resolve().parent.parent +DEFAULT_GPU = REPO / "smauglab" / "configs" / "transform_params_gpu_default01-23.json" +LIST_GPU = REPO / "smauglab" / "configs" / "transform_params_gpu_default01-23-List.json" +HYBRID = REPO / "smauglab" / "configs" / "transform_params_hybrid.json" +PATCH = (24, 24, 24) + + +def names(transforms) -> list[str]: + return [type(t).__name__ for t in transforms] + + +class TestPipelineOrder(unittest.TestCase): + def test_order_comes_from_the_registry_not_the_file(self): + """A config's key order has never matched the pipeline order. Honouring the + file would silently reorder every pipeline the moment someone tidied one.""" + section = load_config(str(DEFAULT_GPU)).section(Backend.GPU) + shuffled = dict(reversed(list(section.items()))) + + straight = build_transforms(section, Backend.GPU) + reversed_ = build_transforms(shuffled, Backend.GPU) + self.assertEqual([e.name for _, e in straight], [e.name for _, e in reversed_]) + + def test_built_order_is_ascending_registry_order(self): + built = build_transforms(load_config(str(DEFAULT_GPU)).section(Backend.GPU), Backend.GPU) + orders = [entry.order for _, entry in built] + self.assertEqual(orders, sorted(orders)) + + +class TestPipelineModes(unittest.TestCase): + def setUp(self): + self.section = load_config(str(LIST_GPU)).section(Backend.GPU) + + def test_sequential_returns_every_transform(self): + built = build_gpu_pipeline(self.section, mode=PipelineMode.SEQUENTIAL) + self.assertEqual(len(built), len([k for k in self.section if not k.startswith("_")])) + + def test_random_order_buckets_transfer_and_enhancement(self): + built = build_gpu_pipeline(self.section, mode=PipelineMode.RANDOM_ORDER) + self.assertEqual(names(built).count("RandomChooseXTransformsGPU"), 2) + # Geometry, plus the low-res transform, lead in order. + self.assertEqual(names(built)[:3], ["RandomFlipTransformGPU", "RandomAffineGPU", "RandomLowResTransformGPU"]) + + def test_random_order_ta_buckets_only_the_transfer_group(self): + built = build_gpu_pipeline(self.section, mode=PipelineMode.RANDOM_ORDER_TA) + self.assertEqual(names(built).count("RandomChooseXTransformsGPU"), 1) + + def test_low_res_is_hoisted_only_when_there_is_a_ge_bucket(self): + """force_sequential means "never inside the GE bucket", so it only bites in + RANDOM_ORDER. RANDOM_ORDER_TA leaves it in its ordinary GE position -- which + is what the two hand-written pipelines did, differing from each other.""" + full = names(build_gpu_pipeline(self.section, mode=PipelineMode.RANDOM_ORDER)) + ta_only = names(build_gpu_pipeline(self.section, mode=PipelineMode.RANDOM_ORDER_TA)) + self.assertLess(full.index("RandomLowResTransformGPU"), full.index("RandomChooseXTransformsGPU")) + self.assertGreater(ta_only.index("RandomLowResTransformGPU"), ta_only.index("RandomChooseXTransformsGPU")) + + +class TestCpuBuilder(unittest.TestCase): + def setUp(self): + self.section = load_config(str(HYBRID)).section(Backend.CPU) + + def test_probability_goes_on_the_wrapper_not_the_transform(self): + built = build_cpu_pipeline(self.section, do_dummy_2d_data_aug=False, patch_size=PATCH, rotation=(-10, 10)) + wrapped = [t for t in built if type(t).__name__ == "RandomTransform"] + self.assertTrue(wrapped, "expected batchgeneratorsv2 transforms to be wrapped") + self.assertTrue(all(hasattr(t, "apply_probability") for t in wrapped)) + + def test_context_is_injected_and_not_taken_from_the_config(self): + built = build_cpu_pipeline(self.section, do_dummy_2d_data_aug=False, patch_size=PATCH, rotation=(-7, 7)) + spatial = next(t for t in built if type(t).__name__ == "SpatialTransform") + self.assertEqual(tuple(spatial.patch_size), PATCH) + self.assertEqual(spatial.rotation, (-7, 7)) + + def test_dummy_2d_brackets_the_spatial_transform(self): + """The converters are pipeline structure, not augmentations, so they carry + no registry entry and are emitted around the slot.""" + built = names(build_cpu_pipeline(self.section, do_dummy_2d_data_aug=True, patch_size=PATCH, rotation=(-10, 10))) + spatial = built.index("SpatialTransform") + self.assertEqual(built[spatial - 1], "Convert3DTo2DTransform") + self.assertEqual(built[spatial + 1], "Convert2DTo3DTransform") + + def test_dummy_2d_drops_the_leading_patch_axis(self): + built = build_cpu_pipeline(self.section, do_dummy_2d_data_aug=True, patch_size=PATCH, rotation=(-10, 10)) + spatial = next(t for t in built if type(t).__name__ == "SpatialTransform") + self.assertEqual(tuple(spatial.patch_size), PATCH[1:]) + + def test_ranges_are_wrapped_by_their_adapter(self): + """BGContrast samples 50/50 from [lo, 1] and [max(lo, 1), hi]; the bare tuple + would be sampled uniformly, which is a different augmentation.""" + built = build_cpu_pipeline(self.section, do_dummy_2d_data_aug=False, patch_size=PATCH, rotation=(-10, 10)) + contrast = next(t.transform for t in built if type(getattr(t, "transform", None)).__name__ == "ContrastTransform") + self.assertEqual(type(contrast.contrast_range).__name__, "BGContrast") + + +class TestStrictValidation(unittest.TestCase): + def test_unknown_augmentation_is_rejected_with_a_suggestion(self): + problems = validate_section({"ScharrTransform": {"p": 0.1}}, Backend.GPU) + self.assertEqual(len(problems), 1) + self.assertIn("RandomScharrGPU", problems[0]) + + def test_unknown_parameter_is_rejected(self): + problems = validate_section({"RandomGaussianNoiseGPU": {"p": 0.1, "stdd": 1.0}}, Backend.GPU) + self.assertIn("Did you mean: std?", problems[0]) + + def test_probability_is_rejected_and_points_at_p(self): + problems = validate_section({"RandomGaussianNoiseGPU": {"probability": 0.1}}, Backend.GPU) + self.assertIn("'probability' -> p", problems[0]) + + def test_context_parameter_in_a_config_is_rejected(self): + problems = validate_section( + {"SpatialTransform": {"rotation": [0, 1], "patch_center_dist_from_border": 0, "random_crop": False}}, + Backend.CPU, + ) + self.assertIn("supplied by the trainer", problems[0]) + + def test_missing_required_parameter_is_reported(self): + problems = validate_section({"SpatialTransform": {}}, Backend.CPU) + self.assertIn("missing required parameter", problems[0]) + + def test_every_problem_is_reported_at_once(self): + """One pass to fix a broken file, not one pytest run per mistake.""" + payload = { + "GPU": { + "ScharrTransform": {"p": 0.1}, + "RandomGaussianNoiseGPU": {"probability": 0.2, "stdd": 1.0}, + } + } + with self.assertRaises(InvalidConfigError) as caught: + SmaugConfig(payload, source="broken.json") + self.assertEqual(len(caught.exception.problems), 3) + self.assertIn("broken.json", str(caught.exception)) + + def test_a_flat_config_is_no_longer_accepted(self): + """`GaussianBlurTransform` meant different transforms to the two builders, + so a document that does not say which backend it is cannot be resolved.""" + with self.assertRaises(InvalidConfigError) as caught: + SmaugConfig({"FlipTransform": {"probability": 0.5}}, source="legacy.json") + self.assertIn("migration/", str(caught.exception)) + + def test_unknown_top_level_section_is_rejected(self): + with self.assertRaises(InvalidConfigError): + SmaugConfig({"GPU": {}, "typo": {}}, source="x.json") + + def test_underscore_keys_are_ignored(self): + config = SmaugConfig({"_comment1": "note", "GPU": {"_note": "x", "RandomFlipTransformGPU": {"p": 0.5}}}) + self.assertEqual(config.names(Backend.GPU), ["RandomFlipTransformGPU"]) + + +class TestConfigLoading(unittest.TestCase): + def test_every_shipped_config_validates(self): + for path in sorted((REPO / "smauglab" / "configs").glob("*.json")): + with self.subTest(config=path.name): + self.assertEqual(validate_file(path), []) + + def test_sections_are_copies_so_the_cache_cannot_be_poisoned(self): + config = load_config(str(DEFAULT_GPU)) + section = config.section(Backend.GPU) + section.clear() + self.assertNotEqual(config.section(Backend.GPU), {}) + + def test_the_same_path_is_parsed_once(self): + """The nnU-Net trainer reads the same file from a staticmethod as well as + from the instance; the cache is what stops it being parsed twice.""" + self.assertIs(load_config(str(DEFAULT_GPU)), load_config(str(DEFAULT_GPU))) + + def test_pipeline_options_default_to_empty(self): + """The old builder did `.get("RandomChooseXTransforms")` then `.get()` on the + result, which raised AttributeError on every config without the block.""" + self.assertEqual(load_config(str(DEFAULT_GPU)).pipeline_options("random_choose"), {}) + + def test_the_shipped_template_names_every_registered_augmentation(self): + template = json.loads((REPO / "smauglab" / "configs" / "all_augmentations.json").read_text()) + for backend in (Backend.GPU, Backend.CPU): + with self.subTest(backend=backend.value): + self.assertEqual(set(template[backend.value]), set(registry.names(backend))) + + +if __name__ == "__main__": + unittest.main() diff --git a/unit_tests/test_cli.py b/unit_tests/test_cli.py new file mode 100644 index 0000000..4e91fff --- /dev/null +++ b/unit_tests/test_cli.py @@ -0,0 +1,139 @@ +"""The `smauglab` command line. + +Driven through `cli.main` with captured stdout rather than as a subprocess: the +registry import costs a few seconds, and paying it once per process keeps the suite +fast enough to gate every pull request. +""" + +from __future__ import annotations + +import contextlib +import io +import json +import tempfile +import unittest +from pathlib import Path + +from smauglab import cli + +REPO = Path(__file__).resolve().parent.parent +DEFAULT_GPU = REPO / "smauglab" / "configs" / "transform_params_gpu_default01-23.json" +LEGACY = REPO / "unit_tests" / "fixtures" / "legacy_configs" / "configs" / "transform_params_gpu_default01-23.json" + + +def run(*argv: str) -> tuple[int, str]: + out = io.StringIO() + with contextlib.redirect_stdout(out), contextlib.redirect_stderr(out): + code = cli.main(list(argv)) + return code, out.getvalue() + + +class TestList(unittest.TestCase): + def test_lists_gpu_augmentations_in_pipeline_order(self): + code, out = run("list", "--backend", "gpu") + self.assertEqual(code, 0) + self.assertIn("RandomFlipTransformGPU", out) + # order column ascends + orders = [int(line.split()[0]) for line in out.splitlines() if line.startswith(" ") and line.split()[0].isdigit()] + self.assertEqual(orders, sorted(orders)) + + def test_group_filter(self): + _, out = run("list", "--backend", "gpu", "--group", "geo") + self.assertIn("RandomFlipTransformGPU", out) + self.assertNotIn("RandomScharrGPU", out) + + def test_json_output_is_machine_readable(self): + _, out = run("list", "--backend", "cpu", "--json") + payload = json.loads(out) + self.assertTrue(all({"name", "backend", "aug_id", "group", "order"} <= set(e) for e in payload)) + + +class TestMatrix(unittest.TestCase): + def test_markdown_shows_the_monai_column_as_empty(self): + code, out = run("matrix", "--format", "md") + self.assertEqual(code, 0) + self.assertIn("| Augmentation | Group | GPU | CPU | MONAI |", out) + + def test_json_form_reports_missing_backends_as_null(self): + _, out = run("matrix", "--format", "json") + payload = json.loads(out) + self.assertIsNone(payload["palette"]["CPU"]) + self.assertEqual(payload["palette"]["GPU"], "RandomPaletteGPU") + self.assertTrue(all(row["MONAI"] is None for row in payload.values())) + + def test_check_passes_on_a_clean_tree(self): + code, _ = run("matrix", "--check") + self.assertEqual(code, 0) + + +class TestShow(unittest.TestCase): + def test_reports_parameters_and_defaults(self): + code, out = run("show", "RandomScharrGPU") + self.assertEqual(code, 0) + self.assertIn("group TA", out) + self.assertIn("absolute", out) + + def test_marks_required_parameters(self): + _, out = run("show", "MirrorTransform") + self.assertIn("REQUIRED", out) + + def test_flags_trainer_supplied_parameters(self): + _, out = run("show", "SpatialTransform") + self.assertIn("supplied by the trainer", out) + + def test_unknown_name_exits_nonzero_with_a_suggestion(self): + code, out = run("show", "ScharrTransform") + self.assertEqual(code, 1) + self.assertIn("RandomScharrGPU", out) + + +class TestValidate(unittest.TestCase): + def test_shipped_config_is_valid(self): + code, out = run("validate", str(DEFAULT_GPU)) + self.assertEqual(code, 0) + self.assertIn("ok", out) + + def test_a_broken_config_exits_nonzero_and_reports_every_problem(self): + payload = {"GPU": {"ScharrTransform": {"p": 0.1}, "RandomGaussianNoiseGPU": {"probability": 0.2}}} + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "broken.json" + path.write_text(json.dumps(payload)) + code, out = run("validate", str(path)) + self.assertEqual(code, 1) + self.assertIn("2 problem(s)", out) + self.assertIn("RandomScharrGPU", out) + self.assertIn("'probability' -> p", out) + + def test_a_legacy_config_is_rejected_and_points_at_migrate(self): + code, out = run("validate", str(LEGACY)) + self.assertEqual(code, 1) + self.assertIn("migration/", out) + + +class TestTemplateAndHash(unittest.TestCase): + def test_template_names_every_gpu_augmentation(self): + from smauglab import registry + from smauglab.registry import Backend + + _, out = run("template", "--backend", "gpu") + self.assertEqual(set(json.loads(out)["GPU"]), set(registry.names(Backend.GPU))) + + def test_template_round_trips_through_validation(self): + """A template that cannot be loaded would be worse than no template.""" + from smauglab.config import validate_file + + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "template.json" + run("template", "--backend", "gpu", "-o", str(path)) + self.assertEqual(validate_file(path), []) + + def test_hash_is_stable_and_content_addressed(self): + code, first = run("hash", str(DEFAULT_GPU)) + self.assertEqual(code, 0) + _, second = run("hash", str(DEFAULT_GPU)) + self.assertEqual(first, second) + self.assertEqual(len(first.split()[0]), 8) + + +if __name__ == "__main__": + unittest.main() diff --git a/unit_tests/test_imports.py b/unit_tests/test_imports.py index b5b80e2..17753e0 100644 --- a/unit_tests/test_imports.py +++ b/unit_tests/test_imports.py @@ -9,8 +9,8 @@ import importlib import importlib.util +import pkgutil import unittest -from pathlib import Path import smauglab @@ -19,27 +19,23 @@ def module_names() -> list[str]: - """Every .py file under smauglab/, as a dotted module name. + """Every module and subpackage under smauglab/, as a dotted module name. - Deliberately a filesystem walk rather than pkgutil.walk_packages: several - subdirectories (transforms/, transforms/cpu/, transforms/gpu/, utils/) have - no __init__.py, so smauglab resolves as a PEP 420 namespace package and - walk_packages only reaches 4 of the ~24 modules. Walking the tree keeps - this test honest regardless of how the package is laid out. + smauglab is a regular package (every directory has an __init__.py), so + walk_packages reaches the whole tree. Subpackages are kept, not filtered out: + their __init__.py files hold real code now (smauglab/__init__.py resolves the + version) and are worth importing too. + + `onerror` matters: without it, walk_packages swallows an ImportError raised + while probing a subpackage and silently returns a short list, turning a real + breakage into a quietly passing test. Re-raising surfaces it here instead. """ - roots = [Path(p) for p in smauglab.__path__] - names = set() - for root in roots: - for path in root.rglob("*.py"): - if "__pycache__" in path.parts: - continue - relative = path.relative_to(root).with_suffix("") - parts = list(relative.parts) - if parts[-1] == "__init__": - parts.pop() - if not parts: - continue - names.add(".".join(["smauglab", *parts])) + + def onerror(_name: str) -> None: + raise # noqa: PLE0704 -- re-raises whatever walk_packages was handling + + names = {module.name for module in pkgutil.walk_packages(smauglab.__path__, prefix="smauglab.", onerror=onerror)} + names.add("smauglab") return sorted(names) diff --git a/unit_tests/test_registered_augmentations.py b/unit_tests/test_registered_augmentations.py new file mode 100644 index 0000000..06dc2ec --- /dev/null +++ b/unit_tests/test_registered_augmentations.py @@ -0,0 +1,176 @@ +"""The real registry, and the artifacts generated from it. + +test_registry.py covers the mechanism against synthetic classes. This file covers +the actual augmentations: that every one is registered coherently, that the +generated matrix and template config are not stale, and that every registered +augmentation is genuinely constructible. + +Together these are the answer to "which augmentations exist and which backends +have them" -- previously recoverable only by reading four `if` ladders side by side. +""" + +from __future__ import annotations + +import inspect +import json +import subprocess +import sys +import unittest +from pathlib import Path + +from smauglab import registry +from smauglab.registry import AugId, Backend + +REPO = Path(__file__).resolve().parent.parent +TEMPLATE = REPO / "smauglab" / "configs" / "all_augmentations.json" + + +class TestRegistryIsPopulated(unittest.TestCase): + def test_both_backends_have_augmentations(self): + self.assertGreaterEqual(len(registry.names(Backend.GPU)), 25) + self.assertGreaterEqual(len(registry.names(Backend.CPU)), 20) + + def test_no_monai_implementations_yet(self): + """Tracked, not built. If this starts failing, the matrix gained a real cell.""" + self.assertEqual(registry.names(Backend.MONAI), []) + + def test_every_aug_id_is_used(self): + """An unused AugId is a concept nothing implements -- almost always a typo.""" + used = {entry.aug_id for entry in registry.entries()} + self.assertEqual(set(AugId) - used, set(), "AugId members with no implementation on any backend") + + def test_class_name_is_the_config_key(self): + for entry in registry.entries(): + with self.subTest(entry=entry.name): + self.assertEqual(entry.cls.__name__, entry.name) + + def test_orders_are_unique_per_backend(self): + for backend in Backend: + orders = [entry.order for entry in registry.entries(backend)] + with self.subTest(backend=backend.value): + self.assertEqual(len(orders), len(set(orders))) + + def test_no_registered_class_hides_parameters_behind_kwargs(self): + """**kwargs would make signature-derived validation accept anything.""" + for entry in registry.entries(): + with self.subTest(entry=entry.name): + if registry._has_var_keyword(entry.cls): + self.assertIsNotNone( + entry.forwards_to, + f"{entry.name} takes **kwargs without declaring forwards_to", + ) + + def test_gpu_transforms_expose_the_kornia_probability_parameters(self): + for entry in registry.entries(Backend.GPU): + accepted = registry.accepted_params(entry) + with self.subTest(entry=entry.name): + self.assertIn("p", accepted) + self.assertIn("p_batch", accepted) + + def test_probability_is_never_a_parameter_name(self): + """It was renamed to `p`; a survivor would mean a half-done migration.""" + for entry in registry.entries(): + with self.subTest(entry=entry.name): + self.assertNotIn("probability", registry.accepted_params(entry)) + + def test_legacy_config_keys_do_not_resolve(self): + """The hard break, on the names that actually appear in the old configs.""" + for legacy in ( + "ScharrTransform", + "UnsharpMaskTransform", + "RandomConvTransform", + "SynthSeg", + "AffineTransform", + "FlipTransform", + "RandomPALETTETransform", + "GammaTransform_invert", + "ImageContrastGPUTransform", + "PaletteSynthesisTransform", + ): + with self.subTest(legacy=legacy), self.assertRaises(registry.UnknownAugmentationError): + registry.get(legacy, Backend.GPU) + + +class TestEveryEntryIsConstructible(unittest.TestCase): + """The registry may not advertise an augmentation that cannot be built.""" + + def test_constructible_with_declared_defaults(self): + for entry in registry.entries(): + with self.subTest(entry=f"{entry.backend.value}.{entry.name}"): + if entry.external_asset: + from unit_tests.helpers import domain_bank_missing + + reason = domain_bank_missing() + if reason: + self.skipTest(reason) + required = registry.required_params(entry) + if required: + # Legitimate: a few third-party CPU transforms take mandatory + # arguments that only a config or the trainer can supply. + self.assertTrue( + entry.backend is Backend.CPU or entry.context_params, + f"{entry.name} requires {sorted(required)} but nothing supplies them", + ) + continue + entry.cls(**dict(entry.smoke_kwargs)) + + +class TestGeneratedArtifactsAreCurrent(unittest.TestCase): + """`smauglab matrix --check` is the anti-staleness guarantee. + + Run as a subprocess rather than re-deriving the comparison here, so the test + exercises the same code path a developer and CI run. + """ + + def test_readme_matrix_and_template_are_up_to_date(self): + result = subprocess.run( + [sys.executable, "-m", "smauglab.cli", "matrix", "--check"], + capture_output=True, + text=True, + cwd=REPO, + check=False, # a non-zero exit is the assertion below, not an error here + ) + self.assertEqual( + result.returncode, + 0, + f"generated artifacts are stale:\n{result.stdout}{result.stderr}", + ) + + def test_template_covers_exactly_the_registered_augmentations(self): + payload = json.loads(TEMPLATE.read_text()) + for backend in (Backend.GPU, Backend.CPU): + with self.subTest(backend=backend.value): + self.assertEqual(set(payload[backend.value]), set(registry.names(backend))) + + def test_template_parameters_are_all_accepted(self): + """Every key in the template must survive the validation stage 6 will apply.""" + payload = json.loads(TEMPLATE.read_text()) + for backend in (Backend.GPU, Backend.CPU): + for name, params in payload[backend.value].items(): + entry = registry.get(name, backend) + accepted = set(registry.accepted_params(entry)) + with self.subTest(entry=f"{backend.value}.{name}"): + self.assertEqual(set(params) - accepted, set()) + + def test_forced_parameters_are_not_offered(self): + """RandomSynthSegGPU overrides these internally; offering them would lie.""" + payload = json.loads(TEMPLATE.read_text()) + synthseg = payload["GPU"]["RandomSynthSegGPU"] + for forced in ("apply_affine", "apply_nonlinear", "flipping", "output_shape"): + with self.subTest(param=forced): + self.assertNotIn(forced, synthseg) + + +class TestForwardedSignatures(unittest.TestCase): + def test_synthseg_accepts_its_generator_parameters(self): + """forwards_to unions the two signatures, replacing a hand-listed allowlist.""" + from smauglab.transforms.synthseg.generator import SynthSegGenerator + + entry = registry.get("RandomSynthSegGPU", Backend.GPU) + accepted = set(registry.accepted_params(entry)) + generator = set(inspect.signature(SynthSegGenerator).parameters) - set(entry.context_params) + self.assertEqual(generator - accepted, set(), "generator parameters missing from the accepted set") + + +if __name__ == "__main__": + unittest.main() diff --git a/unit_tests/test_registry.py b/unit_tests/test_registry.py new file mode 100644 index 0000000..f2fec04 --- /dev/null +++ b/unit_tests/test_registry.py @@ -0,0 +1,257 @@ +"""The registry machinery, exercised against synthetic transform classes. + +Deliberately not against the real augmentations: those get registered in a later +stage, and these tests are about the mechanism -- registration invariants, +signature-derived parameter validation, and the coverage matrix. Keeping them +synthetic means they stay fast and cannot break for reasons unrelated to the +registry itself. + +`registry.clear()` empties the global table, so every test here builds the world +it needs and tears it down again. +""" + +from __future__ import annotations + +import inspect +import unittest + +from smauglab import registry +from smauglab.registry import ( + AugEntry, + AugId, + AugType, + Backend, + RegistryError, + UnknownAugmentationError, +) + + +class RegistryTestCase(unittest.TestCase): + """Isolates each test from the global registry.""" + + def setUp(self) -> None: + # Empties the registry so these tests see only what they register, and puts + # the real augmentations back afterwards. A bare clear() cannot be undone: + # load_all() re-imports an already-imported module, so the decorators never + # run again and every later test in the process would see an empty registry. + context = registry.isolated() + context.__enter__() + self.addCleanup(context.__exit__, None, None, None) + + +def make_transform(name: str, **params): + """A throwaway class whose __init__ has exactly the given parameters.""" + defaults = ", ".join(f"{k}={v!r}" for k, v in params.items()) + namespace: dict = {} + exec( # noqa: S102 -- building a signature is the point of this helper + f"def __init__(self, {defaults}): pass" if defaults else "def __init__(self): pass", + namespace, + ) + return type(name, (), {"__init__": namespace["__init__"], "__doc__": f"{name} summary line.\n\nMore."}) + + +class TestRegistration(RegistryTestCase): + def test_decorator_registers_and_returns_the_class(self): + cls = make_transform("RandomThingGPU", p=1.0) + decorated = registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(cls) + + self.assertIs(decorated, cls, "the decorator must not replace the class") + self.assertEqual(registry.get("RandomThingGPU", Backend.GPU).cls, cls) + + def test_summary_defaults_to_the_first_docstring_line(self): + cls = make_transform("RandomThingGPU", p=1.0) + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(cls) + self.assertEqual(registry.get("RandomThingGPU").summary, "RandomThingGPU summary line.") + + def test_name_must_match_the_class_name(self): + """The config key IS the class name, so a mismatch has to be impossible.""" + with self.assertRaises(RegistryError) as caught: + AugEntry( + name="SomethingElse", + cls=make_transform("RandomThingGPU"), + backend=Backend.GPU, + aug_id=AugId.SCHARR, + group=AugType.TA, + order=10, + ) + self.assertIn("does not match class name", str(caught.exception)) + + def test_duplicate_name_is_rejected(self): + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(make_transform("RandomThingGPU", p=1.0)) + with self.assertRaises(RegistryError) as caught: + registry.register(aug_id=AugId.LAPLACE, backend=Backend.GPU, group=AugType.TA, order=20)( + make_transform("RandomThingGPU", p=1.0) + ) + self.assertIn("already registered", str(caught.exception)) + + def test_duplicate_order_within_a_backend_is_rejected(self): + """Two transforms at the same order would have an ambiguous pipeline position.""" + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(make_transform("RandomOneGPU", p=1.0)) + with self.assertRaises(RegistryError) as caught: + registry.register(aug_id=AugId.LAPLACE, backend=Backend.GPU, group=AugType.TA, order=10)(make_transform("RandomTwoGPU", p=1.0)) + self.assertIn("order 10 is taken", str(caught.exception)) + + def test_same_order_on_different_backends_is_fine(self): + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(make_transform("RandomScharrGPU", p=1.0)) + registry.register(aug_id=AugId.SCHARR, backend=Backend.CPU, group=AugType.TA, order=10)(make_transform("ScharrConvTransform")) + self.assertEqual(len(registry.entries()), 2) + + def test_var_keyword_without_forwards_to_is_rejected(self): + """**kwargs would make signature-based validation accept anything.""" + + class RandomSloppyGPU: + def __init__(self, p: float = 1.0, **kwargs): + pass + + with self.assertRaises(RegistryError) as caught: + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(RandomSloppyGPU) + self.assertIn("**kwargs", str(caught.exception)) + + def test_var_keyword_is_allowed_when_forwards_to_is_declared(self): + target = make_transform("Generator", n_labels=3, blur=0.5) + + class RandomForwardingGPU: + def __init__(self, p: float = 1.0, **kwargs): + pass + + registry.register(aug_id=AugId.SYNTHSEG, backend=Backend.GPU, group=AugType.TA, order=10, forwards_to=target)(RandomForwardingGPU) + self.assertIn("n_labels", registry.accepted_params(registry.get("RandomForwardingGPU"))) + + +class TestLookup(RegistryTestCase): + def setUp(self) -> None: + super().setUp() + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=20)( + make_transform("RandomScharrGPU", p=1.0, absolute=True) + ) + registry.register(aug_id=AugId.FLIP, backend=Backend.GPU, group=AugType.GEO, order=10)( + make_transform("RandomFlipTransformGPU", p=1.0) + ) + + def test_entries_come_back_in_pipeline_order_not_registration_order(self): + self.assertEqual(registry.names(Backend.GPU), ["RandomFlipTransformGPU", "RandomScharrGPU"]) + + def test_filtering_by_group(self): + self.assertEqual(registry.names(Backend.GPU, group=AugType.GEO), ["RandomFlipTransformGPU"]) + + def test_unknown_name_suggests_a_close_match(self): + with self.assertRaises(UnknownAugmentationError) as caught: + registry.get("RandomScharGPU", Backend.GPU) + self.assertIn("Did you mean: RandomScharrGPU?", str(caught.exception)) + + def test_legacy_key_fails_but_points_at_its_replacement(self): + """The hard break: old spellings must raise, not quietly work. + + difflib alone cannot bridge 'ScharrTransform' -> 'RandomScharrGPU' + (too little shared prefix), so the stem fallback has to carry it. + """ + with self.assertRaises(UnknownAugmentationError) as caught: + registry.get("ScharrTransform", Backend.GPU) + self.assertIn("Did you mean: RandomScharrGPU?", str(caught.exception)) + + def test_renamed_hints_are_diagnostics_only(self): + """Nothing in RENAMED_HINTS may be a working config key.""" + for stale in registry.RENAMED_HINTS: + with self.subTest(key=stale), self.assertRaises(UnknownAugmentationError): + registry.get(stale) + + +class TestAcceptedParams(RegistryTestCase): + def test_params_come_from_the_constructor_signature(self): + registry.register(aug_id=AugId.GAUSSIAN_NOISE, backend=Backend.GPU, group=AugType.GE, order=10)( + make_transform("RandomGaussianNoiseGPU", p=1.0, mean=0.0, std=0.1) + ) + entry = registry.get("RandomGaussianNoiseGPU") + self.assertEqual(set(registry.accepted_params(entry)), {"p", "mean", "std"}) + + def test_context_params_are_excluded(self): + registry.register( + aug_id=AugId.SPATIAL, + backend=Backend.CPU, + group=AugType.GEO, + order=10, + wrap_random=False, + context_params=("rotation",), + )(make_transform("SpatialTransform", rotation=None, p_rotation=0.2)) + entry = registry.get("SpatialTransform") + self.assertNotIn("rotation", registry.accepted_params(entry)) + self.assertIn("p_rotation", registry.accepted_params(entry)) + + def test_cpu_wrapped_transforms_accept_p_even_though_the_class_does_not(self): + """batchgeneratorsv2 puts the probability on the RandomTransform wrapper.""" + registry.register(aug_id=AugId.SCHARR, backend=Backend.CPU, group=AugType.TA, order=10)( + make_transform("ScharrConvTransform", absolute=True) + ) + entry = registry.get("ScharrConvTransform") + self.assertIn("p", registry.accepted_params(entry)) + + def test_unwrapped_cpu_transforms_do_not_get_a_synthetic_p(self): + registry.register(aug_id=AugId.MIRROR, backend=Backend.CPU, group=AugType.GEO, order=10, wrap_random=False)( + make_transform("MirrorTransform", allowed_axes=None) + ) + self.assertNotIn("p", registry.accepted_params(registry.get("MirrorTransform"))) + + def test_required_params_are_those_without_a_default(self): + """RandomAffineGPU really does take a required `degrees` today.""" + + class RandomAffineGPU: + def __init__(self, degrees, p: float = 1.0): + pass + + registry.register(aug_id=AugId.AFFINE, backend=Backend.GPU, group=AugType.GEO, order=10)(RandomAffineGPU) + self.assertEqual(registry.required_params(registry.get("RandomAffineGPU")), {"degrees"}) + + def test_unknown_parameter_message_suggests_p_for_probability(self): + registry.register(aug_id=AugId.GAUSSIAN_NOISE, backend=Backend.GPU, group=AugType.GE, order=10)( + make_transform("RandomGaussianNoiseGPU", p=1.0, std=0.1) + ) + message = registry.unknown_parameter_message(registry.get("RandomGaussianNoiseGPU"), "probability") + self.assertIn("'probability' -> p", message) + self.assertIn("Accepted: p, std", message) + + def test_unknown_parameter_message_flags_context_params_specifically(self): + registry.register( + aug_id=AugId.SPATIAL, + backend=Backend.CPU, + group=AugType.GEO, + order=10, + wrap_random=False, + context_params=("rotation",), + )(make_transform("SpatialTransform", rotation=None)) + message = registry.unknown_parameter_message(registry.get("SpatialTransform"), "rotation") + self.assertIn("supplied by the trainer", message) + + +class TestMatrix(RegistryTestCase): + def test_every_aug_id_gets_a_row_even_with_no_implementations(self): + self.assertEqual(set(registry.matrix()), set(AugId)) + + def test_a_concept_joins_its_backends_into_one_row(self): + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(make_transform("RandomScharrGPU", p=1.0)) + registry.register(aug_id=AugId.SCHARR, backend=Backend.CPU, group=AugType.TA, order=10)(make_transform("ScharrConvTransform")) + row = registry.matrix()[AugId.SCHARR] + self.assertEqual(row[Backend.GPU].name, "RandomScharrGPU") + self.assertEqual(row[Backend.CPU].name, "ScharrConvTransform") + self.assertIsNone(row[Backend.MONAI], "no MONAI implementations exist yet") + + def test_markdown_render_marks_the_gap(self): + registry.register(aug_id=AugId.SCHARR, backend=Backend.GPU, group=AugType.TA, order=10)(make_transform("RandomScharrGPU", p=1.0)) + rendered = registry.render_matrix("md") + self.assertIn("| scharr | TA | `RandomScharrGPU` | — | — |", rendered) + + def test_unknown_format_is_rejected(self): + with self.assertRaises(ValueError): + registry.render_matrix("yaml") + + +class TestModuleHygiene(unittest.TestCase): + def test_registry_does_not_import_torch(self): + """`smauglab list` must be able to answer without paying for torch.""" + source = inspect.getsource(registry) + for banned in ("import torch", "import kornia", "from smauglab.transforms"): + with self.subTest(banned=banned): + self.assertNotIn(f"\n{banned}", source, f"registry.py must not import {banned!r} at module scope") + + +if __name__ == "__main__": + unittest.main() diff --git a/unit_tests/test_trainers.py b/unit_tests/test_trainers.py new file mode 100644 index 0000000..7f67e5d --- /dev/null +++ b/unit_tests/test_trainers.py @@ -0,0 +1,198 @@ +"""The nnU-Net trainer, which is now one class driven entirely by the config. + +Three trainers used to encode the CPU/GPU split in their class names. The config +already carries it -- which sections are populated says what runs -- so there is one +class, and these tests pin that it reproduces what each of the three used to build. + +`get_training_transforms` is a staticmethod (nnU-Net's contract), so it can be driven +directly: no plans.json, no dataset, no GPU. +""" + +from __future__ import annotations + +import importlib.util +import os +import unittest +import warnings +from pathlib import Path + +from batchgeneratorsv2.transforms.utils.compose import ComposeTransforms + +from smauglab.config import load_config +from smauglab.registry import Backend +from smauglab.transforms.build import PipelineMode, build_cpu_pipeline + +REPO = Path(__file__).resolve().parent.parent +CONFIGS = REPO / "smauglab" / "configs" +PATCH = (24, 24, 24) +ROTATION = (-10, 10) +DS_SCALES = [[1, 1, 1], [0.5, 0.5, 0.5]] + +# Raised at import so it works under pytest and `python -m unittest` alike; a +# `pytestmark` would only be understood by one of them. +if importlib.util.find_spec("nnunetv2") is None: + raise unittest.SkipTest("the trainer needs the nnunetv2 extra") + + +def flatten(transform) -> list[str]: + """Class names, descending into Compose and unwrapping RandomTransform.""" + if isinstance(transform, ComposeTransforms): + return [name for child in transform.transforms for name in flatten(child)] + wrapped = getattr(transform, "transform", None) + if type(transform).__name__ == "RandomTransform" and wrapped is not None: + return [type(wrapped).__name__] + return [type(transform).__name__] + + +def training_transforms(config_name: str, **overrides) -> list[str]: + from smauglab.trainers.nnUNetTrainerDAExt import nnUNetTrainerDAExtGPU + + os.environ["SMAUGLAB_PARAMS_JSON"] = str(CONFIGS / config_name) + kwargs = { + "patch_size": PATCH, + "rotation_for_DA": ROTATION, + "deep_supervision_scales": DS_SCALES, + "mirror_axes": (0, 1, 2), + "do_dummy_2d_data_aug": False, + "use_mask_for_norm": None, + "is_cascaded": False, + "foreground_labels": None, + "regions": None, + "ignore_label": None, + } + kwargs.update(overrides) + return flatten(nnUNetTrainerDAExtGPU.get_training_transforms(**kwargs)) + + +def cpu_block(config_name: str) -> list[str]: + """The CPU pipeline a config asks for, independent of the trainer.""" + config = load_config(str(CONFIGS / config_name)) + built = build_cpu_pipeline(config.section(Backend.CPU), do_dummy_2d_data_aug=False, patch_size=PATCH, rotation=ROTATION) + return [name for transform in built for name in flatten(transform)] + + +class TestOnlyOneTrainerRemains(unittest.TestCase): + def test_the_load_bearing_name_survives(self): + """nnU-Net writes the class name into every checkpoint and resolves the class + from it at inference, and several hundred trained runs record this one.""" + from smauglab.trainers import nnUNetTrainerDAExt + + self.assertTrue(hasattr(nnUNetTrainerDAExt, "nnUNetTrainerDAExtGPU")) + + def test_the_backend_specific_trainers_are_gone(self): + """They differed only in which config they defaulted to, which the config + itself now says. Neither had a single run on disk.""" + from smauglab.trainers import nnUNetTrainerDAExt + + for gone in ("nnUNetTrainerDAExtHybrid", "nnUNetTrainerDAExt"): + with self.subTest(trainer=gone): + self.assertFalse(hasattr(nnUNetTrainerDAExt, gone)) + + +class TestCompositionMatchesTheOldTrainers(unittest.TestCase): + """Each config must build what its dedicated trainer used to build.""" + + def test_gpu_config_matches_the_old_gpu_trainer(self): + self.assertEqual(training_transforms("transform_params_gpu.json"), ["SpatialTransform", "RemoveLabelTransform"]) + + def test_hybrid_config_matches_the_old_hybrid_trainer(self): + expected = [*cpu_block("transform_params_hybrid.json"), "RemoveLabelTransform"] + self.assertEqual(training_transforms("transform_params_hybrid.json"), expected) + + def test_cpu_config_builds_the_whole_cpu_pipeline(self): + got = training_transforms("transform_params.json") + self.assertEqual(got[: len(cpu_block("transform_params.json"))], cpu_block("transform_params.json")) + self.assertIn("SpatialTransform", got) + + def test_the_cpu_config_carries_the_spatial_transform_the_trainer_used_to_hardcode(self): + """The old CPU trainer appended a SpatialTransform with every probability at + 0 -- a no-op that only enforces the patch size. The merged trainer builds only + what the config names, so the config has to say it.""" + section = load_config(str(CONFIGS / "transform_params.json")).section(Backend.CPU) + self.assertIn("SpatialTransform", section) + spatial = section["SpatialTransform"] + self.assertEqual(spatial["p_rotation"], 0) + self.assertEqual(spatial["p_scaling"], 0) + self.assertEqual(spatial["p_elastic_deform"], 0) + self.assertEqual(spatial["mode_seg"], "nearest") + + +class TestDeepSupervisionPlacement(unittest.TestCase): + """Downsampling must follow whatever last deformed the mask. + + With GPU augmentations that is `train_step`, so it happens there; without them + nothing touches the mask after the dataloader and it belongs there. Getting this + backwards would train against targets that no longer match the image. + """ + + def test_a_gpu_config_leaves_downsampling_to_train_step(self): + for config in ("transform_params_gpu.json", "transform_params_hybrid.json"): + with self.subTest(config=config): + self.assertNotIn("DownsampleSegForDSTransform", training_transforms(config)) + + def test_a_cpu_only_config_downsamples_in_the_dataloader(self): + self.assertIn("DownsampleSegForDSTransform", training_transforms("transform_params.json")) + + def test_no_downsampling_when_no_scales_are_requested(self): + got = training_transforms("transform_params.json", deep_supervision_scales=None) + self.assertNotIn("DownsampleSegForDSTransform", got) + + +class TestDummy2D(unittest.TestCase): + def test_the_converters_bracket_the_spatial_transform(self): + got = training_transforms("transform_params_gpu.json", do_dummy_2d_data_aug=True) + spatial = got.index("SpatialTransform") + self.assertEqual(got[spatial - 1], "Convert3DTo2DTransform") + self.assertEqual(got[spatial + 1], "Convert2DTo3DTransform") + + +class TestConfigResolution(unittest.TestCase): + def setUp(self): + self._saved = {k: os.environ.get(k) for k in ("SMAUGLAB_PARAMS_JSON", "SMAUGLAB_PARAMS_GPU_JSON")} + for key in self._saved: + os.environ.pop(key, None) + + def tearDown(self): + for key, value in self._saved.items(): + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + def test_the_packaged_default_is_used_when_nothing_is_set(self): + from smauglab.trainers.nnUNetTrainerDAExt import resolve_config_path + + self.assertTrue(resolve_config_path().endswith("transform_params_gpu.json")) + + def test_the_new_variable_wins(self): + from smauglab.trainers.nnUNetTrainerDAExt import resolve_config_path + + os.environ["SMAUGLAB_PARAMS_JSON"] = "/new.json" + os.environ["SMAUGLAB_PARAMS_GPU_JSON"] = "/old.json" + self.assertEqual(resolve_config_path(), "/new.json") + + def test_the_old_variable_still_works_but_warns(self): + """segtransferaug/run_trainings.py sets it, and it drives every historical run.""" + from smauglab.trainers.nnUNetTrainerDAExt import resolve_config_path + + os.environ["SMAUGLAB_PARAMS_GPU_JSON"] = "/old.json" + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + self.assertEqual(resolve_config_path(), "/old.json") + self.assertTrue(any(issubclass(w.category, DeprecationWarning) for w in caught)) + + +class TestPipelineMode(unittest.TestCase): + def test_configs_default_to_the_sequential_pipeline(self): + self.assertIs(load_config(str(CONFIGS / "transform_params_gpu.json")).pipeline_mode(), PipelineMode.SEQUENTIAL) + + def test_the_list_configs_ask_for_the_random_order_pipeline(self): + """They carry a random_choose block, so they were written for the ChooseX + trainers; the config says that now instead of the class name.""" + for config in sorted(CONFIGS.glob("*-List*.json")): + with self.subTest(config=config.name): + self.assertIs(load_config(str(config)).pipeline_mode(), PipelineMode.RANDOM_ORDER) + + +if __name__ == "__main__": + unittest.main() diff --git a/unit_tests/test_transforms_gpu.py b/unit_tests/test_transforms_gpu.py index f391ba7..543c65d 100644 --- a/unit_tests/test_transforms_gpu.py +++ b/unit_tests/test_transforms_gpu.py @@ -4,112 +4,66 @@ use still work. These prove each transform works in isolation, so a failure points at one class instead of a whole config. -Transforms are discovered by introspection rather than listed by hand, so a -newly added transform is covered the moment it lands. +The list comes from the registry rather than from introspection, so it covers +exactly what a config can reach -- no denylist of helper classes to maintain, no +silent gap when a transform has a required argument. This replaced a hand-rolled +walk over four hardcoded module names that missed RandomAffineGPU (required +`degrees`) and RandomSynthSegGPU (its module was not in the list). """ from __future__ import annotations -import importlib -import inspect import unittest -from pathlib import Path import torch +from smauglab import registry +from smauglab.registry import Backend from smauglab.transforms.gpu.base import AugmentationSequentialCustom -from unit_tests.helpers import SmaugLabTestCase, first_output - -TRANSFORM_MODULES = [ - "smauglab.transforms.gpu.contrast", - "smauglab.transforms.gpu.spatial", - "smauglab.transforms.gpu.fromSeg", - "smauglab.transforms.gpu.domain_transfer", -] - -# Not augmentations: helper modules that happen to be nn.Module subclasses. -NOT_A_TRANSFORM = {"DifferentiableHistogram3D"} - - -def discover_transforms(): - """Collect transform classes that can be constructed without arguments.""" - found = [] - for module_name in TRANSFORM_MODULES: - module = importlib.import_module(module_name) - for name, obj in vars(module).items(): - if not inspect.isclass(obj) or obj.__module__ != module_name: - continue - if not issubclass(obj, torch.nn.Module) or name in NOT_A_TRANSFORM: - continue - signature = inspect.signature(obj.__init__) - required = [ - param - for param in list(signature.parameters.values())[1:] - if param.default is inspect.Parameter.empty and param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD) - ] - if required: - # Needs caller-supplied configuration; covered via test_configs.py. - continue - found.append((f"{module_name.rsplit('.', 1)[-1]}.{name}", obj, signature)) - return sorted(found, key=lambda item: item[0]) - - -DISCOVERED = discover_transforms() - - -def build_kwargs(cls, signature) -> dict: - """Construction arguments that make a transform actually do something. - - `p` is forced to 1.0 because most transforms default to a low probability - and would otherwise pass through untouched most of the time. - """ - kwargs = {"p": 1.0} if "p" in signature.parameters else {} - if cls.__name__ == "RandomDomainTransferGPU": - # Every parameter has a default, but the constructor still rejects a - # missing source_label unless it is told to draw from every domain pair. - kwargs["any_source"] = True - return kwargs - - -def skip_reason(cls) -> str | None: - """Some transforms depend on assets that do not exist on a fresh checkout.""" - if cls.__name__ == "RandomDomainTransferGPU": - from smauglab.transforms.gpu.domain_transfer import DEFAULT_BANK_PATH - - if not Path(DEFAULT_BANK_PATH).is_file(): - return f"domain transfer bank not available at {DEFAULT_BANK_PATH}" +from unit_tests.helpers import SmaugLabTestCase, domain_bank_missing, first_output + +# `p` is forced to 1.0 so a transform actually fires; most default lower and would +# otherwise pass the volume through untouched. +GPU_ENTRIES = [entry for entry in registry.entries(Backend.GPU) if not registry.required_params(entry)] + + +def skip_reason(entry) -> str | None: + """Some transforms need assets that do not exist on a fresh checkout.""" + if entry.external_asset: + return domain_bank_missing() return None class TestTransformDiscovery(unittest.TestCase): - def test_discovery_found_transforms(self): + def test_registry_offers_the_bulk_of_the_gpu_transforms(self): self.assertGreaterEqual( - len(DISCOVERED), - 15, - f"expected the bulk of the GPU transforms, found {len(DISCOVERED)}", + len(GPU_ENTRIES), + 25, + f"expected the bulk of the GPU transforms, found {len(GPU_ENTRIES)}", ) class TestTransformsRunStandalone(SmaugLabTestCase): - def _pipeline(self, cls, signature): + def _pipeline(self, entry): """Drive a single transform the way AugTransformsGPU does.""" + kwargs = {"p": 1.0, **dict(entry.smoke_kwargs)} return AugmentationSequentialCustom( - cls(**build_kwargs(cls, signature)), + entry.cls(**kwargs), data_keys=["input", "mask"], same_on_batch=True, ) def test_transform_runs_on_a_tiny_volume(self): - for label, cls, signature in DISCOVERED: - with self.subTest(transform=label): - reason = skip_reason(cls) + for entry in GPU_ENTRIES: + with self.subTest(transform=entry.name): + reason = skip_reason(entry) if reason: self.skipTest(reason) volume, seg = self.tiny_volume(), self.tiny_seg() - image = first_output(self._pipeline(cls, signature)(volume, seg)) + image = first_output(self._pipeline(entry)(volume, seg)) - self.assertIsImageLike(image, volume, cls.__name__) + self.assertIsImageLike(image, volume, entry.name) def test_transform_leaves_the_mask_intact(self): """Image-only transforms must not silently alter the segmentation labels. @@ -118,23 +72,23 @@ def test_transform_leaves_the_mask_intact(self): is checked -- values must stay in {0, 1}, never interpolated into something in between. """ - for label, cls, signature in DISCOVERED: - with self.subTest(transform=label): - reason = skip_reason(cls) + for entry in GPU_ENTRIES: + with self.subTest(transform=entry.name): + reason = skip_reason(entry) if reason: self.skipTest(reason) - result = self._pipeline(cls, signature)(self.tiny_volume(), self.tiny_seg()) + result = self._pipeline(entry)(self.tiny_volume(), self.tiny_seg()) if not isinstance(result, (list, tuple)) or len(result) < 2: - self.skipTest(f"{cls.__name__} does not return a mask") + self.skipTest(f"{entry.name} does not return a mask") mask = result[1] - self.assertTrue(bool(torch.isfinite(mask).all()), f"{cls.__name__} produced a non-finite mask") + self.assertTrue(bool(torch.isfinite(mask).all()), f"{entry.name} produced a non-finite mask") unique = torch.unique(mask) self.assertLessEqual( unique.numel(), 2, - f"{cls.__name__} interpolated the mask into {unique.numel()} values", + f"{entry.name} interpolated the mask into {unique.numel()} values", ) diff --git a/unit_tests/test_variant_leaves.py b/unit_tests/test_variant_leaves.py new file mode 100644 index 0000000..6fa8407 --- /dev/null +++ b/unit_tests/test_variant_leaves.py @@ -0,0 +1,148 @@ +"""Variant leaves fix their variant and hide it from the config surface. + +Four convolution kernels used to share one class behind a `kernel_type=` argument, +gamma and inverted-gamma shared one behind `invert_image=`, and five elementwise +functions shared one behind an un-serialisable `func=` callable. That made the +config key ambiguous -- the same augmentation could be spelled several ways, and +`FunctionTransform` could not name a single function at all. + +Each variant is now its own class. These tests pin both halves of that: the leaf +really does apply its own variant, and the variant argument is gone, so a config +cannot contradict the class it just named. +""" + +from __future__ import annotations + +import inspect +import unittest + +import torch + +from smauglab.transforms.cpu.contrast import ( + ExpTransform, + InvertedGammaTransform, + LaplaceConvTransform, + Log1pTransform, + ScharrConvTransform, + SigmoidTransform, + SinTransform, + SqrtTransform, +) +from smauglab.transforms.gpu.contrast import ( + RandomExpGPU, + RandomGammaGPU, + RandomGaussianBlurGPU, + RandomInvGammaGPU, + RandomLaplaceGPU, + RandomLog1pGPU, + RandomRandConvGPU, + RandomScharrGPU, + RandomSigmoidGPU, + RandomSinGPU, + RandomSqrtGPU, + RandomUnsharpMaskGPU, +) + +GPU_KERNELS = { + RandomLaplaceGPU: "Laplace", + RandomScharrGPU: "Scharr", + RandomGaussianBlurGPU: "GaussianBlur", + RandomUnsharpMaskGPU: "UnsharpMask", + RandomRandConvGPU: "RandConv", +} +GPU_FUNCTIONS = [RandomLog1pGPU, RandomSqrtGPU, RandomSinGPU, RandomExpGPU, RandomSigmoidGPU] +CPU_FUNCTIONS = [Log1pTransform, SqrtTransform, SinTransform, ExpTransform, SigmoidTransform] + + +def params(cls) -> set[str]: + return set(inspect.signature(cls).parameters) + + +class TestConvLeaves(unittest.TestCase): + def test_each_leaf_fixes_its_kernel(self): + for cls, kernel in GPU_KERNELS.items(): + with self.subTest(cls=cls.__name__): + self.assertEqual(cls().kernel_type, kernel) + + def test_kernel_type_is_not_configurable(self): + for cls in GPU_KERNELS: + with self.subTest(cls=cls.__name__): + self.assertNotIn("kernel_type", params(cls)) + + def test_scharr_keeps_the_defaults_the_old_ladder_passed(self): + """The ladder passed absolute=True and retain_stats=True only for Scharr.""" + scharr = RandomScharrGPU() + self.assertTrue(scharr.absolute) + self.assertTrue(scharr.retain_stats) + self.assertFalse(RandomLaplaceGPU().absolute) + + def test_unsharp_keeps_its_ladder_amount(self): + self.assertEqual(RandomUnsharpMaskGPU().unsharp_amount, 1.5) + + def test_cpu_leaves_mirror_the_gpu_split(self): + self.assertEqual(LaplaceConvTransform().kernel_type, "Laplace") + self.assertEqual(ScharrConvTransform().kernel_type, "Scharr") + for cls in (LaplaceConvTransform, ScharrConvTransform): + with self.subTest(cls=cls.__name__): + self.assertNotIn("kernel_type", params(cls)) + + +class TestGammaLeaves(unittest.TestCase): + def test_leaves_fix_opposite_inversions(self): + self.assertFalse(RandomGammaGPU().invert_image) + self.assertTrue(RandomInvGammaGPU().invert_image) + + def test_invert_image_is_not_configurable(self): + for cls in (RandomGammaGPU, RandomInvGammaGPU): + with self.subTest(cls=cls.__name__): + self.assertNotIn("invert_image", params(cls)) + + def test_cpu_inverted_gamma_fixes_the_flag(self): + self.assertNotIn("p_invert_image", params(InvertedGammaTransform)) + self.assertEqual(InvertedGammaTransform().p_invert_image, 1) + + +class TestFunctionLeaves(unittest.TestCase): + def test_func_is_not_configurable(self): + for cls in GPU_FUNCTIONS + CPU_FUNCTIONS: + with self.subTest(cls=cls.__name__): + self.assertNotIn("func", params(cls)) + self.assertNotIn("function", params(cls)) + + def test_each_leaf_applies_a_distinct_function(self): + x = torch.linspace(0.1, 2.0, 8) + results = {cls.__name__: cls().func(x) for cls in GPU_FUNCTIONS} + for name, value in results.items(): + with self.subTest(cls=name): + self.assertTrue(torch.isfinite(value).all()) + flat = list(results.values()) + for i in range(len(flat)): + for j in range(i + 1, len(flat)): + self.assertFalse(torch.allclose(flat[i], flat[j]), "two function leaves compute the same thing") + + def test_arithmetic_matches_the_original_lambdas_bit_for_bit(self): + """torch.log1p / torch.sigmoid differ in the last ulp and would move the + seeded determinism hashes that published experiments depend on.""" + x = torch.linspace(0.1, 2.0, 64) + self.assertTrue(torch.equal(RandomLog1pGPU().func(x), torch.log(1 + x))) + self.assertTrue(torch.equal(RandomSigmoidGPU().func(x), 1 / (1 + torch.exp(-x)))) + + def test_gpu_and_cpu_leaves_compute_the_same_functions(self): + x = torch.linspace(0.1, 2.0, 16) + for gpu_cls, cpu_cls in zip(GPU_FUNCTIONS, CPU_FUNCTIONS): + with self.subTest(pair=f"{gpu_cls.__name__}/{cpu_cls.__name__}"): + self.assertTrue(torch.equal(gpu_cls().func(x), cpu_cls().function(x))) + + +class TestAcqIsSingleAxis(unittest.TestCase): + def test_one_dim_is_not_configurable(self): + """RandomAcqTransformGPU *is* the single-axis case; the isotropic one is + RandomLowResTransformGPU. Exposing the flag let either key do either job.""" + from smauglab.transforms.gpu.spatial import RandomAcqTransformGPU + + self.assertNotIn("one_dim", params(RandomAcqTransformGPU)) + self.assertTrue(RandomAcqTransformGPU()._param_generator.one_dim) + + +if __name__ == "__main__": + unittest.main() From b71ba37de3115e62d351cd90950823f7efb1e556 Mon Sep 17 00:00:00 2001 From: iback Date: Wed, 19 Aug 2026 06:29:59 +0000 Subject: [PATCH 2/2] fix: correct nine augmentations that were not doing what they claimed Each of these is silent -- nothing crashes, nothing fails a test, the pipeline just produces something other than what the config asked for. Every fix below has a regression test that fails against the previous implementation. * RandomFlipTransformGPU never read the flip flags its own generator sampled. It recomputed the same `flip_axis`-derived list for every batch element, so it flipped all configured axes, identically, on every call -- three seeded calls gave byte-identical output, and FlipGenerator3D (including its "at least one axis" guarantee) was dead code. * The 1D Gaussian was sampled at `arange(k)` rather than a centred range, putting its peak at index 0. The 3D kernel's maximum sat at corner [0,0,0], so RandomGaussianBlurGPU and RandomUnsharpMaskGPU blurred *and* translated the image about a voxel -- relative to a segmentation mask that is not convolved. * in_seg/out_seg reduced the mask's class axis with `argmax(...) > 0`. For an ordinary single-channel mask argmax over a length-1 axis is always 0, so the result was all-False: in_seg applied the transform nowhere, out_seg applied it everywhere. For a one-hot mask it dropped the first foreground class, since this repository encodes channel c as label c+1. * The single-axis generators drew their "random" axis in make_samplers, which kornia calls once and caches -- the same axis was degraded for a whole training run. CropGenerator3D additionally drew separate axes for the crop and for its position, and neutralised the position to 1.0 (the far edge) using the crop's neutral value instead of 0.5 (centred). * The 2D CPU Scharr x-kernel had [-10, 0, -10] as its middle row, summing to -20 rather than 0. It was not a gradient operator. Predates the registry work. * The elementwise function transforms normalised with a batch-wide min/max, so a volume's augmentation depended on which other volumes shared its batch. * RandomHistogramEqualizationGPU wrote through an `input[:, c]` view, so its non-finite guard `continue`d over values already in the batch. * RandomChooseXTransformsGPU mutated the caller's batch in place, and raised "params must contain 'scale'" for any transform with a kornia parameter generator, because it calls apply_transform directly and skips forward_parameters. RandomLowResTransformGPU also read flags["data_keys"] unguarded, which only the mask path injects. * Blur sigmas and kernel sizes were drawn with Python's `random`, which torch.manual_seed does not reach and which diverges across DDP ranks. They now use smauglab.transforms.rng, built from the unused _shared_rand apparatus that was already sitting in gpu/fromSeg.py. Also: scipy's structuring element rank is taken from the data rather than hardcoded to 3, and `resample_method` is read with .get() so restoring it cannot raise UnboundLocalError. Models trained before this change saw the old behaviour and will not reproduce against it. Configs are unaffected -- no key, parameter or default changed. The README gains a table of what moved and why. `smauglab migrate` was advertised in cli.py, config.py and the README but never existed as a subcommand; those now point at migration/, matching MIGRATE_HINT. Co-Authored-By: Claude Opus 5 --- README.md | 30 +++- smauglab/transforms/cpu/contrast.py | 5 +- smauglab/transforms/cpu/fromSeg.py | 5 +- smauglab/transforms/cpu/transforms.py | 2 +- smauglab/transforms/gpu/contrast.py | 57 +++++- smauglab/transforms/gpu/fromSeg.py | 34 +--- smauglab/transforms/gpu/spatial.py | 96 ++++++---- smauglab/transforms/gpu/transforms_list.py | 18 +- smauglab/transforms/rng.py | 70 ++++++++ unit_tests/test_region_mode.py | 144 +++++++++++++++ unit_tests/test_transform_randomness.py | 197 +++++++++++++++++++++ 11 files changed, 577 insertions(+), 81 deletions(-) create mode 100644 smauglab/transforms/rng.py create mode 100644 unit_tests/test_region_mode.py create mode 100644 unit_tests/test_transform_randomness.py diff --git a/README.md b/README.md index 646931f..49757d9 100644 --- a/README.md +++ b/README.md @@ -184,10 +184,12 @@ smauglab list --group TA # just the transfer augmentations smauglab show RandomScharrGPU # one augmentation's parameters and defaults smauglab validate my_config.json # strict check, every problem reported at once smauglab template --backend gpu # a config naming everything, at defaults -smauglab migrate old_run/params.json # bring a pre-registry config forward smauglab hash my_config.json # content-addressed config identity ``` +Bringing a pre-registry config forward is a one-time job, so it is not a subcommand: +the migrator lives in `migration/` in this repository rather than in the wheel. + Config keys are class names, exactly, and parameters are constructor arguments, exactly. Anything else is an error rather than a silent no-op: @@ -199,6 +201,32 @@ my_config.json: 2 problem(s) 'probability' -> p ``` +## Augmentation behaviour changed in the registry release + +Several augmentations were silently producing something other than what they claimed. +Fixing them changes what the pipeline emits, so **models trained before this release +saw different augmentations and are not bit-reproducible against it**. Configs are +unaffected -- no key, parameter or default changed. The hash discontinuity is already +recorded in `migration/hash_migration.json`. + +What changed, and why each was wrong: + +| Augmentation | Was | Now | +|---|---|---| +| `RandomFlipTransformGPU` | Ignored the flip flags its generator sampled, so it flipped every configured axis, identically, on every call and for every batch element. Seeded runs gave byte-identical output. | Reads `params["flip"]`: each batch element flips an independently sampled subset. | +| `RandomGaussianBlurGPU`, `RandomUnsharpMaskGPU` | The Gaussian kernel was sampled at `arange(k)` rather than a centred range, so its peak sat at index 0 and the "blur" also translated the image about a voxel — relative to a mask that was not translated. | Centred kernel; blurring an impulse leaves its centre of mass in place. | +| Every GPU contrast transform with `in_seg` / `out_seg` | Reduced the mask's class axis with `argmax(...) > 0`. For an ordinary single-channel mask that is always false, so `in_seg` applied the transform *nowhere* and `out_seg` applied it *everywhere*; for a one-hot mask it dropped the first foreground class. | `amax(...) > 0`, matching `collapse_onehot_to_index`. Both knobs now do what they say. | +| `RandomAcqTransformGPU` (and any `one_dim` generator) | Drew its "random" axis in `make_samplers`, which kornia calls once and caches — the same axis was degraded for the entire training run. `CropGenerator3D` also drew *separate* axes for the crop and for its position. | Drawn per call and per batch element, with one axis shared by crop and position. | +| `ScharrConvTransform` (CPU, 2-D only) | The x-kernel's middle row was `[-10, 0, -10]`, so it summed to −20 and was not a gradient operator. The 3-D CPU and both GPU kernels were correct. | Middle row is `[-10, 0, 10]`. | +| `RandomLog1pGPU`, `RandomSqrtGPU`, `RandomSinGPU`, `RandomExpGPU`, `RandomSigmoidGPU` | Normalised with a batch-wide `x.min()` / `x.max()`, so a volume's augmentation depended on which other volumes shared its batch. | Per-sample min/max, as every other transform in the file. | +| `RandomHistogramEqualizationGPU` | Wrote through a view of the input, so its non-finite guard skipped over values already in the batch. | Works on a clone; the guard is effective. | +| `RandomChooseXTransformsGPU` | Wrote into the caller's batch in place. Transforms with a kornia parameter generator raised `params must contain 'scale'` inside a bucket. | Clones; samples parameters for generator-based transforms. | +| Transforms drawing blur sigmas / kernel sizes | Used Python's `random`, which `torch.manual_seed` does not reach and which diverges across DDP ranks. | `smauglab.transforms.rng.shared_choice`, driven by torch's RNG. | + +The regression tests are in `unit_tests/test_region_mode.py` and +`unit_tests/test_transform_randomness.py`; each one fails against the previous +implementation. + ## Available augmentations Which augmentations exist, and which backends implement each one. An empty cell diff --git a/smauglab/transforms/cpu/contrast.py b/smauglab/transforms/cpu/contrast.py index ef19199..186fe19 100644 --- a/smauglab/transforms/cpu/contrast.py +++ b/smauglab/transforms/cpu/contrast.py @@ -70,7 +70,10 @@ def get_parameters(self, **data_dict) -> dict: if self.kernel_type == "Laplace": kernel = torch.tensor([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], dtype=torch.float32) elif self.kernel_type == "Scharr": - kernel_x = torch.tensor([[-3, 0, 3], [-10, 0, -10], [-3, 0, 3]], dtype=torch.float32) + # Middle row is [-10, 0, 10]. It read [-10, 0, -10], which sums to -20 + # instead of 0, so this was not a gradient operator at all. The 3-D + # kernel below and both GPU kernels were always right. + kernel_x = torch.tensor([[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]], dtype=torch.float32) kernel_y = torch.tensor([[-3, -10, -3], [0, 0, 0], [3, 10, 3]], dtype=torch.float32) kernel = [kernel_x, kernel_y] elif spatial_dims == 3: diff --git a/smauglab/transforms/cpu/fromSeg.py b/smauglab/transforms/cpu/fromSeg.py index b7624e6..9a78daa 100644 --- a/smauglab/transforms/cpu/fromSeg.py +++ b/smauglab/transforms/cpu/fromSeg.py @@ -81,7 +81,10 @@ def aug_redistribute_seg(img, seg, classes=None, in_seg=0.2, retain_stats=False) # Convert to NumPy for dilation operations (not supported in PyTorch) l_mask_np = l_mask.cpu().numpy() - struct = ndi.iterate_structure(ndi.generate_binary_structure(3, 1), 3) + # Rank from the data, not hardcoded 3: scipy requires the structuring element + # to match the input's rank, so a 2-D image raised + # "structure rank must match input rank" here. + struct = ndi.iterate_structure(ndi.generate_binary_structure(l_mask_np.ndim, 1), 3) l_mask_dilate_np = ndi.binary_dilation(l_mask_np, structure=struct) # Convert back to PyTorch diff --git a/smauglab/transforms/cpu/transforms.py b/smauglab/transforms/cpu/transforms.py index 0b406eb..5726504 100644 --- a/smauglab/transforms/cpu/transforms.py +++ b/smauglab/transforms/cpu/transforms.py @@ -4,7 +4,7 @@ config: `patch_size` and `rotation` come from nnU-Net at runtime (declared as `context_params` on the registry entries), a top-level `retain_stats` was pushed into several blocks, and `mode_seg="nearest"` was hardcoded. The first is injected -by the builder; the other two were folded into the configs by `smauglab migrate`. +by the builder; the other two were folded into the configs by the `migration/` script. """ from typing import Union diff --git a/smauglab/transforms/gpu/contrast.py b/smauglab/transforms/gpu/contrast.py index c51a8f8..6d59236 100644 --- a/smauglab/transforms/gpu/contrast.py +++ b/smauglab/transforms/gpu/contrast.py @@ -1,5 +1,4 @@ import math -import random from collections.abc import Callable, Sequence from typing import Any, Union @@ -10,6 +9,7 @@ from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform +from smauglab.transforms.rng import shared_choice def _choose_region_mode(p_in: float, p_out: float, seg_mask: torch.Tensor | None) -> str: # noqa: ARG001 -- seg_mask kept for signature symmetry with _apply_region_mode @@ -31,6 +31,26 @@ def _choose_region_mode(p_in: float, p_out: float, seg_mask: torch.Tensor | None return "all" +def _foreground(mask: torch.Tensor, dim: int) -> torch.Tensor: + """Which voxels the segmentation covers, reducing over the class axis. + + This used to be `torch.argmax(mask, dim) > 0`, which is only "is anything labelled + here" if class 0 is background -- and it is not: + + * For a single-channel [B, 1, D, H, W] mask (an ordinary nnU-Net target, and what + the tests build) `argmax` over a length-1 axis is always 0, so the result was + **all False**. `in_seg` then applied the transform nowhere and `out_seg` applied + it everywhere: the two knobs did nothing and the opposite of nothing. + * For a one-hot mask, this repository's convention (`collapse_onehot_to_index` in + gpu/fromSeg.py) is that channel `c` encodes label `c + 1` with background + implicit, so `argmax == 0` is a real foreground class and was being dropped. + + `amax > 0` asks the question that was meant, and matches what + `collapse_onehot_to_index` already does with `seg_raw.any(dim=1)`. + """ + return mask.amax(dim=dim) > 0 + + def _apply_region_mode( orig: torch.Tensor, transformed: torch.Tensor, @@ -68,7 +88,7 @@ def _apply_region_mode( o = torch.randint(0, 2, (seg_mask.shape[1],), device=seg_mask.device, dtype=seg_mask.dtype) m[i] = m[i] * o.view(-1, 1, 1, 1) # Broadcasting o to match the dimensions of m - m = torch.argmax(m, dim=1) > 0 + m = _foreground(m, dim=1) m = m.to(transformed.dtype) if mode == "out": m = 1.0 - m @@ -86,7 +106,7 @@ def _apply_region_mode( # Create a tensor with random one and zero o = torch.randint(0, 2, (seg_mask.shape[0],), device=seg_mask.device, dtype=seg_mask.dtype) m = m * o.view(-1, 1, 1, 1) # Broadcasting o to match the dimensions of m - m = torch.argmax(m, dim=0) > 0 + m = _foreground(m, dim=0) m = m.to(transformed.dtype) if mode == "out": m = 1.0 - m @@ -206,7 +226,7 @@ def get_kernel(self, device: torch.device) -> Union[Tensor, list[Tensor]]: kernel = get_gaussian_kernel3d(kernel_size, sigma, torch.float32, device) elif self.kernel_type == "RandConv": # choose random odd kernel size e.g. [1,3,5,7] - k = int(random.choice(self.kernel_sizes)) # define kernel_sizes in __init__ + k = int(shared_choice(self.kernel_sizes)) # define kernel_sizes in __init__ std = 1.0 / math.sqrt(k * k) kernel = torch.randn((k, k, k), device=device) * std # for 3D @@ -545,9 +565,15 @@ def apply_convolution(img: torch.Tensor, kernel: torch.Tensor, dim: int) -> torc def get_gaussian_kernel1d(kernel_size: int, sigma: Union[float, Tensor], dtype: torch.dtype, device: torch.device) -> Tensor: - """Create a 1D Gaussian kernel.""" + """Create a 1D Gaussian kernel, centred on the middle tap. - x = torch.arange(kernel_size, dtype=dtype, device=device) + The sample points were `arange(kernel_size)` -- 0, 1, 2 -- which puts the peak at + index 0 instead of the centre. The resulting 3D kernel had its maximum at corner + [0,0,0], so RandomGaussianBlurGPU and RandomUnsharpMaskGPU blurred *and* translated + the image by about a voxel, relative to a segmentation mask that is not convolved. + """ + half = (kernel_size - 1) / 2.0 + x = torch.linspace(-half, half, kernel_size, dtype=dtype, device=device) pdf = torch.exp(-0.5 * (x / sigma).pow(2)) kernel1d = pdf / pdf.sum() @@ -1078,8 +1104,17 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ orig_means = x.mean(dim=reduce_dims) orig_stds = x.std(dim=reduce_dims) - # Normalize to make values >=0 - x = (x - x.min()) / (x.max() - x.min() + 0.00001) + # Normalize to make values >=0, per sample. + # + # This used to be a bare `x.min()` / `x.max()`, which reduces over the whole + # [N, ...] slab: an image's augmentation then depended on which other images + # happened to share its batch, so the same volume augmented twice in + # different batches came out differently. Every other transform in this file + # reduces over `dim=reduce_dims` per sample. + keep_dims = tuple(range(1, x.dim())) + x_min = x.amin(dim=keep_dims, keepdim=True) + x_max = x.amax(dim=keep_dims, keepdim=True) + x = (x - x_min) / (x_max - x_min + 0.00001) # Apply function x = self.func(x) @@ -1351,7 +1386,11 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ # Apply histogram equalization transform seg_mask = params.get("seg") for c in self.apply_to_channel: - channel_data = input[:, c] # shape [N, ...spatial...] + # `.clone()`, not the bare `input[:, c]` view this used to take: the loop + # below assigns into `channel_data[b]`, which through a view writes straight + # into `input`. The NaN guard at the bottom would then `continue` over + # values that were already in the batch -- the guard skipped nothing. + channel_data = input[:, c].clone() # shape [N, ...spatial...] orig = channel_data.clone() if self.retain_stats: diff --git a/smauglab/transforms/gpu/fromSeg.py b/smauglab/transforms/gpu/fromSeg.py index 37c1e00..1f003e0 100644 --- a/smauglab/transforms/gpu/fromSeg.py +++ b/smauglab/transforms/gpu/fromSeg.py @@ -1,14 +1,13 @@ -import random from collections.abc import Sequence from typing import Any import torch -import torch.distributed as dist from torch import Tensor, nn from torch.nn import functional as F from smauglab.registry import AugId, AugType, Backend, register from smauglab.transforms.gpu.base import ImageOnlyTransform +from smauglab.transforms.rng import shared_choice # ── PALETTE AUG helpers ────────────────────────────────────────────────── @@ -438,7 +437,7 @@ def apply_transform( synth = torch.stack(synth_list) # (B, N) synth_01 = synth.reshape(B, 1, D, H, W) - sigma = random.choice(self.blur_sigmas) + sigma = shared_choice(self.blur_sigmas) if sigma > 0.0: synth_01 = _gaussian_blur_3d(synth_01, sigma) synth = synth_01.reshape(B, N) @@ -478,7 +477,7 @@ def apply_transform( # ── Step 3: optional second blur, then foreground z-score ───────────── synth_01 = synth.reshape(B, 1, D, H, W) - sigma2 = random.choice(self.blur_sigmas) + sigma2 = shared_choice(self.blur_sigmas) if sigma2 > 0.0: synth_01 = _gaussian_blur_3d(synth_01, sigma2) synth = synth_01.reshape(B, N) @@ -495,20 +494,6 @@ def apply_transform( return out -_SHARED_RNG_COUNTER = 0 - - -def _next_shared_seed() -> int: - global _SHARED_RNG_COUNTER # noqa: PLW0603 -- module-level counter is the point: it makes successive seeds distinct - _SHARED_RNG_COUNTER += 1 - seed = (int(torch.initial_seed()) + _SHARED_RNG_COUNTER) % (2**63 - 1) - if dist.is_available() and dist.is_initialized(): - seed_tensor = torch.tensor([seed], dtype=torch.long) - dist.broadcast(seed_tensor, src=0) - seed = int(seed_tensor.item()) - return seed - - def _minmax_norm(x: torch.Tensor, eps: float = 1e-8) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: """Per-sample min-max normalise to [0, 1]. Returns (normed, min, max).""" B = x.shape[0] @@ -538,19 +523,6 @@ def _zscore_renorm(x: torch.Tensor, bg_threshold: float = 1e-6) -> torch.Tensor: return torch.where(fg, (x - mean) / std, torch.zeros_like(x)) -def _shared_cpu_generator() -> torch.Generator: - generator = torch.Generator(device="cpu") - generator.manual_seed(_next_shared_seed()) - return generator - - -def _shared_rand(shape: tuple[int, ...], device: torch.device, dtype: torch.dtype) -> torch.Tensor: - if not (dist.is_available() and dist.is_initialized()): - return torch.rand(shape, device=device, dtype=dtype) - rand_cpu = torch.rand(shape, generator=_shared_cpu_generator(), device="cpu", dtype=dtype) - return rand_cpu.to(device=device, dtype=dtype) - - def collapse_onehot_to_index(seg_raw: torch.Tensor) -> torch.Tensor: """ Convert a one-hot segmentation mask to a single-channel integer index mask. diff --git a/smauglab/transforms/gpu/spatial.py b/smauglab/transforms/gpu/spatial.py index 7c4df53..e10444a 100644 --- a/smauglab/transforms/gpu/spatial.py +++ b/smauglab/transforms/gpu/spatial.py @@ -195,9 +195,10 @@ def apply_transform_mask( Convert "resample" arguments to "nearest" by default. """ - resample_method: Resample | None - if "resample" in flags: - resample_method = flags["resample"] + # `resample_method` was declared but only *assigned* inside the `if`, so the + # restore below raised UnboundLocalError whenever flags carried no "resample". + resample_method: Resample | None = flags.get("resample") + if resample_method is not None: flags["resample"] = Resample.get("nearest") output = self.apply_transform(input, params, flags, transform) if resample_method is not None: @@ -246,12 +247,17 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ scales = params["scale"] # shape [B, 3] - if flags["data_keys"][0] is DataKey.IMAGE: + # Only MaskSequentialOpsCustom injects "data_keys" (see gpu/base.py), so a bare + # `flags["data_keys"]` raised KeyError for every other caller -- calling this + # transform standalone, or from inside RandomChooseXTransformsGPU, which passes + # the transform's own `flags`. Defaulting to IMAGE is what those callers mean. + data_keys = flags.get("data_keys") or [DataKey.INPUT] + if data_keys[0] in (DataKey.INPUT, DataKey.IMAGE): resample = "trilinear" - elif flags["data_keys"][0] is DataKey.MASK: + elif data_keys[0] is DataKey.MASK: resample = "nearest" else: - raise ValueError(f"Unsupported data key {flags['data_keys'][0]} for RandomLowResTransformGPU. Expected IMAGE or MASK.") + raise ValueError(f"Unsupported data key {data_keys[0]} for RandomLowResTransformGPU. Expected IMAGE or MASK.") # Define interpolation modes interp_down = resample @@ -308,6 +314,24 @@ def apply_transform_mask( return output +def _choose_axis(batch_size: int, device: torch.device, same_on_batch: bool) -> torch.Tensor: + """Pick the single axis to act on, per batch element. Returns `[B]` indices. + + Drawn here, from `forward`, rather than in `make_samplers`. kornia calls + `make_samplers` once and caches the samplers it builds, so an axis picked there was + fixed for the transform's lifetime -- "degrade a random axis" degraded the *same* + axis for a whole training run. + """ + keep = torch.randint(0, 3, (1 if same_on_batch else batch_size,), device=device) + return keep.expand(batch_size) if same_on_batch else keep + + +def _keep_one_axis(values: torch.Tensor, keep: torch.Tensor, neutral: float) -> torch.Tensor: + """Keep column `keep[b]` of a `[B, 3]` draw and set the other two to `neutral`.""" + selected = torch.arange(3, device=values.device).unsqueeze(0) == keep.unsqueeze(1) + return torch.where(selected, values, torch.full_like(values, neutral)) + + class ScaleGenerator3D(RandomGeneratorBase): def __init__(self, scale: tuple[float, float], one_dim: bool = False) -> None: super().__init__() @@ -316,13 +340,6 @@ def __init__(self, scale: tuple[float, float], one_dim: bool = False) -> None: def make_samplers(self, device: torch.device, dtype: torch.dtype) -> None: scale = _tuple_range_reader(self.scale, 3, device, dtype) - if self.one_dim: - # Pick a random dimension to apply scaling - dim = torch.randint(0, 3, (1,)).item() - for i in range(3): - if i != dim: - scale[i, 0] = 1.0 - scale[i, 1] = 1.0 self.scalex_sampler = UniformDistribution(scale[0, 0], scale[0, 1], validate_args=False) self.scaley_sampler = UniformDistribution(scale[1, 0], scale[1, 1], validate_args=False) self.scalez_sampler = UniformDistribution(scale[2, 0], scale[2, 1], validate_args=False) @@ -337,6 +354,10 @@ def forward(self, batch_shape: tuple[int, ...], same_on_batch: bool = False) -> scalez = _adapted_rsampling((batch_size,), self.scalez_sampler, same_on_batch) scale = torch.stack([scalex, scaley, scalez], dim=1) + if self.one_dim: + # A scale of 1.0 leaves an axis at full resolution. + scale = _keep_one_axis(scale, _choose_axis(batch_size, scale.device, same_on_batch), 1.0) + return {"scale": torch.as_tensor(scale, device=_device, dtype=_dtype)} @@ -476,16 +497,24 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ batch_size, C, D, H, W = input.shape - # Expect params to contain 'flip' tensor of shape [B, 3] with 0/1 values - # flips = None - # if params is not None and 'flip' in params: - # flips = params['flip'] + # params["flip"] is [B, 3] of 0/1 flags over (z, y, x), produced by + # FlipGenerator3D. Reading it is what makes this transform random: the loop + # below used to recompute the same `flip_axis`-derived list for every b and + # ignore the sampled flags entirely, so every call flipped all configured axes + # identically -- three seeded calls gave byte-identical output, and the + # generator (including its "at least one axis" guarantee) was dead code. + flips = params.get("flip") out = input.clone() - # For each batch element, build list of spatial dims to flip (D,H,W -> dims 2,3,4) + # For each batch element, build list of spatial dims to flip. `input[b]` is + # [C, D, H, W], so spatial axis i sits at dim 1 + i. for b in range(batch_size): - # fb expected as length-3 tensor for (z,y,x) - flip_dims = [1 + axis for axis in range(3) if axis in self.flip_axis] + if flips is None: + # No sampled flags (a caller invoking apply_transform directly): fall + # back to flipping every configured axis. + flip_dims = [1 + axis for axis in range(3) if axis in self.flip_axis] + else: + flip_dims = [1 + axis for axis in range(3) if axis in self.flip_axis and bool(flips[b, axis])] if len(flip_dims) > 0: out[b] = torch.flip(input[b], dims=tuple(flip_dims)) @@ -682,25 +711,11 @@ def __init__(self, crop: tuple[float, float], pos: tuple[float, float], one_dim: def make_samplers(self, device: torch.device, dtype: torch.dtype) -> None: crop = _tuple_range_reader(self.crop, 3, device, dtype) - if self.one_dim: - # Pick a random dimension to apply cropping - dim = torch.randint(0, 3, (1,)).item() - for i in range(3): - if i != dim: - crop[i, 0] = 1.0 - crop[i, 1] = 1.0 self.cropx_sampler = UniformDistribution(crop[0, 0], crop[0, 1], validate_args=False) self.cropy_sampler = UniformDistribution(crop[1, 0], crop[1, 1], validate_args=False) self.cropz_sampler = UniformDistribution(crop[2, 0], crop[2, 1], validate_args=False) pos = _tuple_range_reader(self.pos, 3, device, dtype) - if self.one_dim: - # Pick a random dimension to apply cropping - dim = torch.randint(0, 3, (1,)).item() - for i in range(3): - if i != dim: - pos[i, 0] = 1.0 - pos[i, 1] = 1.0 self.posx_sampler = UniformDistribution(pos[0, 0], pos[0, 1], validate_args=False) self.posy_sampler = UniformDistribution(pos[1, 0], pos[1, 1], validate_args=False) self.posz_sampler = UniformDistribution(pos[2, 0], pos[2, 1], validate_args=False) @@ -722,4 +737,17 @@ def forward(self, batch_shape: tuple[int, ...], same_on_batch: bool = False) -> posz = _adapted_rsampling((batch_size,), self.posz_sampler, same_on_batch) pos = torch.stack([posx, posy, posz], dim=1) + if self.one_dim: + # One axis for both: `make_samplers` drew a separate `dim` for crop and for + # pos, so the crop could be taken along one axis while the position that + # placed it was randomised along another. + keep = _choose_axis(batch_size, crop.device, same_on_batch) + # A crop fraction of 1.0 keeps the whole axis. The *position*, though, is + # the crop centre as a fraction of the axis, so its neutral value is 0.5 + # (centred) -- the previous code copied the crop's 1.0 onto it, which put + # the box centre on the far edge and left the crop flush against it after + # clamping. + crop = _keep_one_axis(crop, keep, 1.0) + pos = _keep_one_axis(pos, keep, 0.5) + return {"crop": torch.as_tensor(crop, device=_device, dtype=_dtype), "pos": torch.as_tensor(pos, device=_device, dtype=_dtype)} diff --git a/smauglab/transforms/gpu/transforms_list.py b/smauglab/transforms/gpu/transforms_list.py index 283f260..08f76b9 100644 --- a/smauglab/transforms/gpu/transforms_list.py +++ b/smauglab/transforms/gpu/transforms_list.py @@ -83,9 +83,18 @@ def _apply_mix(self, x: Tensor, seg: Tensor | None) -> Tensor: continue if not hasattr(t, "apply_transform"): raise TypeError(f"All transforms must implement apply_transform like ImageOnlyTransform. Got {type(t)}") - # Most contrast transforms perform their random sampling inside apply_transform. + # Most contrast transforms perform their random sampling inside + # apply_transform, so an empty params dict is all they need. The ones with a + # kornia `_param_generator` (the spatial transforms) read their draw out of + # `params` instead, and calling apply_transform directly skips the + # forward_parameters step that fills it -- they used to raise + # "params must contain 'scale'" from inside a bucket. Sampling here keeps + # the bucket usable for both kinds. + t_params = child_params + if getattr(t, "_param_generator", None) is not None: + t_params = {**child_params, **t.forward_parameters(x.shape)} t_flags = getattr(t, "flags", {}) - x = t.apply_transform(x, child_params, t_flags, transform=None) + x = t.apply_transform(x, t_params, t_flags, transform=None) return x @torch.no_grad() # disable gradients for efficiency @@ -96,7 +105,10 @@ def apply_transform(self, input: Tensor, params: dict[str, Tensor], flags: dict[ return self._apply_mix(input, seg) batch_size = input.shape[0] - out = input + # A clone, not `out = input`: the loop writes back through `out[i:i+1]`, so + # without it the caller's batch is modified in place. Every sibling transform + # in gpu/spatial.py clones. + out = input.clone() for i in range(batch_size): xi = out[i : i + 1] seg_i = None diff --git a/smauglab/transforms/rng.py b/smauglab/transforms/rng.py new file mode 100644 index 0000000..ad2a086 --- /dev/null +++ b/smauglab/transforms/rng.py @@ -0,0 +1,70 @@ +"""Random draws that `torch.manual_seed` actually reaches, and that DDP ranks agree on. + +Several GPU transforms reached for Python's `random.choice` to pick a blur sigma or a +kernel size, inside an `apply_transform` that was otherwise entirely `torch.rand` +driven. Two consequences: + +* `torch.manual_seed(...)` does not seed Python's `random`, so a "seeded" run was not + reproducible. The test suite hid this -- `unit_tests/helpers.py::seed_everything` + seeds torch, numpy *and* random -- but training does not call that. +* Under DistributedDataParallel each rank has its own `random` state, so ranks picked + different sigmas for the same batch. + +`gpu/fromSeg.py` already contained `_next_shared_seed` / `_shared_rand` written for +exactly this, and never called them. That machinery lives here now, with the `choice` +helper the call sites actually needed. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from typing import TypeVar + +import torch +import torch.distributed as dist + +T = TypeVar("T") + +_SHARED_RNG_COUNTER = 0 + + +def next_shared_seed() -> int: + """A seed every rank agrees on, different on each call.""" + global _SHARED_RNG_COUNTER # noqa: PLW0603 -- module-level counter is the point: it makes successive seeds distinct + _SHARED_RNG_COUNTER += 1 + seed = (int(torch.initial_seed()) + _SHARED_RNG_COUNTER) % (2**63 - 1) + if dist.is_available() and dist.is_initialized(): + seed_tensor = torch.tensor([seed], dtype=torch.long) + dist.broadcast(seed_tensor, src=0) + seed = int(seed_tensor.item()) + return seed + + +def shared_cpu_generator() -> torch.Generator: + generator = torch.Generator(device="cpu") + generator.manual_seed(next_shared_seed()) + return generator + + +def shared_rand(shape: tuple[int, ...], device: torch.device, dtype: torch.dtype = torch.float32) -> torch.Tensor: + """Uniform [0, 1) draws; identical across ranks when running under DDP. + + Outside DDP this is just `torch.rand`, so it stays on whatever device and generator + the caller has already seeded. + """ + if not (dist.is_available() and dist.is_initialized()): + return torch.rand(shape, device=device, dtype=dtype) + rand_cpu = torch.rand(shape, generator=shared_cpu_generator(), device="cpu", dtype=dtype) + return rand_cpu.to(device=device, dtype=dtype) + + +def shared_choice(options: Sequence[T]) -> T: + """Pick one element of `options`, using torch's RNG rather than Python's. + + The drop-in replacement for `random.choice` in a transform. + """ + if len(options) == 0: + raise ValueError("cannot choose from an empty sequence") + draw = float(shared_rand((1,), torch.device("cpu")).item()) + # torch.rand is [0, 1), so the index is already in range; the clamp is belt-and-braces. + return options[min(int(draw * len(options)), len(options) - 1)] diff --git a/unit_tests/test_region_mode.py b/unit_tests/test_region_mode.py new file mode 100644 index 0000000..6bd1603 --- /dev/null +++ b/unit_tests/test_region_mode.py @@ -0,0 +1,144 @@ +"""`in_seg` / `out_seg`: applying a GPU contrast transform only inside or outside the mask. + +The bug these pin down: `_apply_region_mode` reduced the class axis with +`torch.argmax(mask, dim) > 0`. For the ordinary single-channel mask that is always +False, so `in_seg` applied the transform nowhere and `out_seg` applied it everywhere -- +both knobs silently inoperative. For a one-hot mask it dropped the first foreground +class, because this repository encodes channel `c` as label `c + 1`. +""" + +import torch + +from smauglab.transforms.gpu.contrast import _apply_region_mode, _foreground +from unit_tests.helpers import SmaugLabTestCase + + +class TestForegroundReduction(SmaugLabTestCase): + def test_a_single_channel_mask_is_not_collapsed_to_nothing(self): + """argmax over a length-1 axis is always 0, so `> 0` was always False.""" + mask = torch.zeros(1, 1, 4, 4, 4) + mask[0, 0, 1:3, 1:3, 1:3] = 1.0 + + found = _foreground(mask, dim=1) + + self.assertEqual(int(found.sum()), 8, "the labelled block was not recognised as foreground") + self.assertTrue(bool(found[0, 1, 1, 1])) + self.assertFalse(bool(found[0, 0, 0, 0])) + + def test_the_old_argmax_reduction_really_did_collapse_it(self): + """Control: what the previous expression produced on the same input.""" + mask = torch.zeros(1, 1, 4, 4, 4) + mask[0, 0, 1:3, 1:3, 1:3] = 1.0 + self.assertEqual(int((torch.argmax(mask, dim=1) > 0).sum()), 0) + + def test_the_first_one_hot_channel_counts_as_foreground(self): + """Channel 0 encodes label 1 -- see collapse_onehot_to_index in gpu/fromSeg.py.""" + mask = torch.zeros(1, 3, 4, 4, 4) + mask[0, 0, 0, 0, 0] = 1.0 # only the first class is present here + mask[0, 2, 3, 3, 3] = 1.0 + + found = _foreground(mask, dim=1) + + self.assertTrue(bool(found[0, 0, 0, 0]), "the first one-hot class was dropped") + self.assertTrue(bool(found[0, 3, 3, 3])) + self.assertEqual(int(found.sum()), 2) + + def test_background_stays_background(self): + self.assertEqual(int(_foreground(torch.zeros(1, 4, 5, 5, 5), dim=1).sum()), 0) + + +class TestApplyRegionMode(SmaugLabTestCase): + """The behaviour a config actually asks for when it sets in_seg or out_seg.""" + + def setUp(self): + super().setUp() + # orig/transformed are one channel of the batch: [N, D, H, W]. + self.orig = torch.zeros(1, 4, 4, 4) + self.transformed = torch.ones(1, 4, 4, 4) + # A single-channel mask covering one corner: [N, 1, D, H, W]. + self.mask = torch.zeros(1, 1, 4, 4, 4) + self.mask[0, 0, :2, :2, :2] = 1.0 + self.inside = (slice(None), slice(0, 2), slice(0, 2), slice(0, 2)) + + def test_mode_in_changes_only_the_masked_voxels(self): + out = _apply_region_mode(self.orig, self.transformed, self.mask, "in") + + self.assertTrue(bool((out[self.inside] == 1.0).all()), "'in' did not apply the transform inside the mask") + self.assertEqual(int(out.sum()), 8, "'in' leaked outside the mask") + + def test_mode_out_changes_only_the_unmasked_voxels(self): + out = _apply_region_mode(self.orig, self.transformed, self.mask, "out") + + self.assertTrue(bool((out[self.inside] == 0.0).all()), "'out' applied the transform inside the mask") + self.assertEqual(int(out.sum()), 64 - 8) + + def test_in_and_out_partition_the_volume(self): + inside = _apply_region_mode(self.orig, self.transformed, self.mask, "in") + outside = _apply_region_mode(self.orig, self.transformed, self.mask, "out") + self.assertTrue(torch.equal(inside + outside, self.transformed)) + + def test_mode_all_ignores_the_mask(self): + out = _apply_region_mode(self.orig, self.transformed, self.mask, "all") + self.assertTrue(torch.equal(out, self.transformed)) + + def test_a_missing_mask_means_apply_everywhere(self): + out = _apply_region_mode(self.orig, self.transformed, None, "in") + self.assertTrue(torch.equal(out, self.transformed)) + + def test_the_unbatched_3d_path_behaves_the_same(self): + orig = torch.zeros(4, 4, 4) + transformed = torch.ones(4, 4, 4) + mask = torch.zeros(2, 4, 4, 4) + mask[0, :2, :2, :2] = 1.0 + + out = _apply_region_mode(orig, transformed, mask, "in") + self.assertEqual(int(out.sum()), 8) + + def test_an_unsupported_rank_is_rejected(self): + with self.assertRaises(ValueError): + _apply_region_mode(torch.rand(2, 2), torch.rand(2, 2), torch.rand(1, 2, 2), "in") + + +class TestNaNGuard(SmaugLabTestCase): + def test_histogram_equalisation_does_not_write_through_a_view(self): + """It used to take `input[:, c]` as a view and assign into it per batch element, + so the NaN guard's `continue` skipped over values already written to the batch.""" + from smauglab.transforms.gpu.contrast import RandomHistogramEqualizationGPU + + transform = RandomHistogramEqualizationGPU(p=1.0, mix_prob=0.0) + # A constant channel makes img_max == img_min, the degenerate histogram case. + volume = torch.ones(1, 1, 8, 8, 8) + out = transform.apply_transform(volume.clone(), {}, {}, transform=None) + + self.assertTrue(bool(torch.isfinite(out).all()), "a non-finite result reached the batch despite the guard") + + +class TestRegionModeReachesTheRealTransforms(SmaugLabTestCase): + """End to end: a registered GPU transform with in_seg=1.0 must respect the mask.""" + + def test_in_seg_confines_a_scharr_transform_to_the_mask(self): + from smauglab.transforms.gpu.base import AugmentationSequentialCustom + from smauglab.transforms.gpu.contrast import RandomScharrGPU + + image = self.tiny_volume() + seg = self.tiny_seg() # single channel, a centred cube + + # Driven through the container, as AugTransformsGPU does: that is what routes + # the mask into params["seg"], which is where _apply_region_mode reads it. + pipeline = AugmentationSequentialCustom( + RandomScharrGPU(p=1.0, in_seg=1.0, out_seg=0.0, mix_prob=0.0), + data_keys=["input", "mask"], + same_on_batch=True, + ) + out = pipeline(image.clone(), seg.clone()) + out = out[0] if isinstance(out, (list, tuple)) else out + + outside = ~(seg[0, 0] > 0) + self.assertTrue( + torch.allclose(out[0, 0][outside], image[0, 0][outside], atol=1e-5), + "in_seg=1.0 changed voxels outside the segmentation", + ) + self.assertFalse( + torch.allclose(out[0, 0], image[0, 0], atol=1e-5), + "in_seg=1.0 changed nothing at all -- this is what the argmax bug did", + ) diff --git a/unit_tests/test_transform_randomness.py b/unit_tests/test_transform_randomness.py new file mode 100644 index 0000000..6a4fbaf --- /dev/null +++ b/unit_tests/test_transform_randomness.py @@ -0,0 +1,197 @@ +"""Transforms that were advertised as random but were not. + +Each test here fails against the implementation that preceded it: + +* `RandomFlipTransformGPU` never read the flip flags its generator sampled, so it + flipped every configured axis, identically, on every call. +* The single-axis generators drew their axis in `make_samplers`, which kornia calls + once and caches -- so "degrade a random axis" degraded the same axis for the whole + run. +* `RandomLowResTransformGPU` read `flags["data_keys"]` unguarded, which only the mask + path injects. +* `RandomChooseXTransformsGPU` wrote into the caller's batch. +* Several transforms drew from Python's `random`, which `torch.manual_seed` does not + reach. +""" + +import torch + +from smauglab.transforms.gpu.base import AugmentationSequentialCustom +from smauglab.transforms.gpu.spatial import CropGenerator3D, RandomFlipTransformGPU, RandomLowResTransformGPU, ScaleGenerator3D +from smauglab.transforms.gpu.transforms_list import RandomChooseXTransformsGPU +from unit_tests.helpers import SmaugLabTestCase, first_output + + +class TestFlipIsActuallyRandom(SmaugLabTestCase): + def _pipeline(self, **kwargs): + return AugmentationSequentialCustom( + RandomFlipTransformGPU(p=1.0, **kwargs), + data_keys=["input", "mask"], + same_on_batch=False, + ) + + def test_two_seeds_give_two_different_flips(self): + volume, seg = self.tiny_volume(), self.tiny_seg() + pipeline = self._pipeline(flip_axis=(0, 1, 2)) + + outputs = [] + for seed in range(12): + torch.manual_seed(seed) + outputs.append(first_output(pipeline(volume.clone(), seg.clone())).clone()) + + distinct = {tuple(out.flatten()[:64].tolist()) for out in outputs} + self.assertGreater(len(distinct), 1, "every seed produced the same flip -- params['flip'] is being ignored") + + def test_batch_elements_flip_independently(self): + """The generator samples [B, 3]; the loop used to discard it and flip all of them.""" + torch.manual_seed(0) + volume = torch.rand(8, 1, 8, 8, 8) + seg = torch.zeros(8, 1, 8, 8, 8) + pipeline = self._pipeline(flip_axis=(0, 1, 2)) + + out = first_output(pipeline(volume.clone(), seg.clone())) + flipped = [bool(torch.allclose(out[b], torch.flip(volume[b], dims=(1, 2, 3)))) for b in range(8)] + + self.assertIn(False, flipped, "every batch element got the identical all-axis flip") + + def test_the_mask_is_flipped_the_same_way_as_the_image(self): + """Image and mask read the same params['flip'], so they cannot disagree.""" + torch.manual_seed(3) + volume = torch.rand(4, 1, 8, 8, 8) + seg = (volume > 0.5).float() + + result = self._pipeline(flip_axis=(0, 1, 2))(volume.clone(), seg.clone()) + image, mask = result[0], result[1] + + self.assertTrue(torch.equal((image > 0.5).float(), mask), "the mask was flipped differently from the image") + + def test_only_configured_axes_are_ever_flipped(self): + torch.manual_seed(1) + volume = torch.rand(6, 1, 8, 8, 8) + seg = torch.zeros(6, 1, 8, 8, 8) + + out = first_output(self._pipeline(flip_axis=(0,))(volume.clone(), seg.clone())) + for b in range(6): + unchanged = torch.allclose(out[b], volume[b]) + flipped_axis0 = torch.allclose(out[b], torch.flip(volume[b], dims=(1,))) + self.assertTrue(unchanged or flipped_axis0, f"batch element {b} was flipped along an axis that was not configured") + + +class TestSingleAxisIsRedrawnEveryCall(SmaugLabTestCase): + """The axis used to be chosen in make_samplers, which kornia calls once.""" + + def test_the_scale_generator_does_not_pin_one_axis_forever(self): + torch.manual_seed(0) + generator = ScaleGenerator3D(scale=(0.3, 1.0), one_dim=True) + generator.make_samplers(torch.device("cpu"), torch.float32) + + degraded_axes = set() + for _ in range(20): + scale = generator((4,), same_on_batch=False)["scale"] + # Exactly one axis per row is scaled; the rest sit at the neutral 1.0. + for row in scale: + self.assertEqual(int((row != 1.0).sum()), 1, "more than one axis was degraded") + degraded_axes.add(int((row != 1.0).nonzero().item())) + + self.assertGreater(len(degraded_axes), 1, "the same axis was degraded on every call") + + def test_same_on_batch_degrades_one_shared_axis(self): + torch.manual_seed(0) + generator = ScaleGenerator3D(scale=(0.3, 1.0), one_dim=True) + generator.make_samplers(torch.device("cpu"), torch.float32) + + scale = generator((5,), same_on_batch=True)["scale"] + chosen = {int((row != 1.0).nonzero().item()) for row in scale} + self.assertEqual(len(chosen), 1, "same_on_batch should pick one axis for the whole batch") + + def test_the_crop_generator_neutralises_position_at_the_centre(self): + """The old code copied the crop's neutral 1.0 onto `pos`, i.e. the far edge.""" + torch.manual_seed(0) + generator = CropGenerator3D(crop=(0.5, 0.9), pos=(0.2, 0.8), one_dim=True) + generator.make_samplers(torch.device("cpu"), torch.float32) + + params = generator((4,), same_on_batch=False) + crop, pos = params["crop"], params["pos"] + + for b in range(4): + kept = (crop[b] != 1.0).nonzero().flatten().tolist() + self.assertEqual(len(kept), 1) + untouched = [axis for axis in range(3) if axis != kept[0]] + for axis in untouched: + self.assertAlmostEqual(float(pos[b, axis]), 0.5, places=5, msg="a non-cropped axis was not centred") + + +class TestLowResRunsOutsideTheMaskPath(SmaugLabTestCase): + def test_it_runs_standalone(self): + """flags['data_keys'] is only injected by MaskSequentialOpsCustom.""" + torch.manual_seed(0) + transform = RandomLowResTransformGPU(p=1.0) + out = transform(self.tiny_volume()) + self.assertIsImageLike(first_output(out), self.tiny_volume(), "RandomLowResTransformGPU") + + def test_it_runs_inside_a_random_choose_bucket(self): + """The bucket calls apply_transform with the transform's own flags, which + carry no data_keys either.""" + torch.manual_seed(0) + bucket = RandomChooseXTransformsGPU( + transforms_list=[RandomLowResTransformGPU(p=1.0)], + num_transforms=1, + p=1.0, + ) + volume = self.tiny_volume() + out = bucket.apply_transform(volume.clone(), {}, {}, transform=None) + self.assertIsImageLike(out, volume, "RandomLowResTransformGPU in a bucket") + + +class TestRandomChooseDoesNotMutateItsInput(SmaugLabTestCase): + def test_the_callers_tensor_is_left_alone(self): + torch.manual_seed(0) + bucket = RandomChooseXTransformsGPU( + transforms_list=[RandomLowResTransformGPU(p=1.0)], + num_transforms=1, + p=1.0, + same_on_batch=False, + ) + volume = torch.rand(3, 1, 12, 12, 12) + before = volume.clone() + + bucket.apply_transform(volume, {}, {}, transform=None) + + self.assertTrue(torch.equal(volume, before), "RandomChooseXTransformsGPU wrote into the caller's batch") + + def test_an_empty_bucket_is_a_no_op(self): + bucket = RandomChooseXTransformsGPU(transforms_list=[], num_transforms=0, p=1.0) + volume = self.tiny_volume() + self.assertTrue(torch.equal(bucket.apply_transform(volume.clone(), {}, {}, transform=None), volume)) + + +class TestTorchSeedReachesEveryDraw(SmaugLabTestCase): + """`torch.manual_seed` alone must be enough; Python's `random` is seeded separately.""" + + def _run_twice(self, build): + outputs = [] + for _ in range(2): + torch.manual_seed(1234) + transform = build() + outputs.append(first_output(transform(self.tiny_volume())).clone()) + return outputs + + def test_randconv_is_reproducible_under_torch_seed_alone(self): + from smauglab.transforms.gpu.contrast import RandomRandConvGPU + + first, second = self._run_twice(lambda: RandomRandConvGPU(p=1.0, kernel_sizes=(1, 3, 5, 7))) + self.assertTrue(torch.equal(first, second), "RandomRandConvGPU drew its kernel size from an unseeded generator") + + def test_shared_choice_covers_the_whole_sequence(self): + from smauglab.transforms.rng import shared_choice + + torch.manual_seed(0) + options = (1, 3, 5, 7) + seen = {shared_choice(options) for _ in range(200)} + self.assertEqual(seen, set(options)) + + def test_shared_choice_rejects_an_empty_sequence(self): + from smauglab.transforms.rng import shared_choice + + with self.assertRaises(ValueError): + shared_choice([])