augmentrum 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {augmentrum-0.0.2 → augmentrum-0.0.3}/MANIFEST.in +0 -1
- {augmentrum-0.0.2/augmentrum.egg-info → augmentrum-0.0.3}/PKG-INFO +97 -17
- {augmentrum-0.0.2 → augmentrum-0.0.3}/README.md +71 -13
- augmentrum-0.0.3/augmentrum/__init__.py +41 -0
- augmentrum-0.0.3/augmentrum/augmentation/__init__.py +56 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/augmentation/amplitude_scaling.py +29 -17
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/augmentation/apodization.py +108 -54
- augmentrum-0.0.3/augmentrum/augmentation/artificial_peaks.py +344 -0
- augmentrum-0.0.3/augmentrum/augmentation/baseline_augmentation.py +704 -0
- augmentrum-0.0.3/augmentrum/augmentation/eddy_current.py +486 -0
- augmentrum-0.0.3/augmentrum/augmentation/line_broadening.py +485 -0
- augmentrum-0.0.3/augmentrum/augmentation/macromolecules.py +758 -0
- augmentrum-0.0.3/augmentrum/augmentation/noise.py +1003 -0
- augmentrum-0.0.3/augmentrum/augmentation/phase_frequency.py +385 -0
- augmentrum-0.0.3/augmentrum/augmentation/residual_water.py +377 -0
- augmentrum-0.0.3/augmentrum/augmentation/spatial_augmentations.py +1019 -0
- augmentrum-0.0.3/augmentrum/augmentation/spurious_echoes.py +709 -0
- augmentrum-0.0.3/augmentrum/augmentation/transient_synthesis.py +303 -0
- augmentrum-0.0.3/augmentrum/augmentation/updating.py +590 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/augmentation/zero_fill.py +33 -16
- augmentrum-0.0.3/augmentrum/core/__init__.py +23 -0
- augmentrum-0.0.3/augmentrum/core/augmentrum.py +1932 -0
- augmentrum-0.0.3/augmentrum/core/base_module.py +833 -0
- augmentrum-0.0.3/augmentrum/core/dataset_utils.py +528 -0
- augmentrum-0.0.3/augmentrum/core/kernels.py +2346 -0
- augmentrum-0.0.3/augmentrum/core/pipeline.py +753 -0
- augmentrum-0.0.3/augmentrum/core/pool.py +611 -0
- augmentrum-0.0.3/augmentrum/core/precision.py +109 -0
- augmentrum-0.0.3/augmentrum/dataset/biggaba.py +286 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/dataset/brainbeats.py +6 -14
- augmentrum-0.0.3/augmentrum/dataset/cows.py +792 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/dataset/fmrsinpain.py +4 -12
- augmentrum-0.0.3/augmentrum/dataset/mrsi_challenge.py +874 -0
- augmentrum-0.0.3/augmentrum/physics/__init__.py +19 -0
- augmentrum-0.0.3/augmentrum/physics/seq_file.py +137 -0
- augmentrum-0.0.3/augmentrum/processing/__init__.py +51 -0
- augmentrum-0.0.3/augmentrum/processing/domain.py +215 -0
- augmentrum-0.0.3/augmentrum/processing/edit_combination.py +126 -0
- augmentrum-0.0.3/augmentrum/processing/interpolating.py +733 -0
- augmentrum-0.0.3/augmentrum/processing/raw_processing.py +2196 -0
- augmentrum-0.0.3/augmentrum/processing/torch_engine.py +1383 -0
- augmentrum-0.0.3/augmentrum/processing/utils.py +816 -0
- augmentrum-0.0.3/augmentrum/sampling/__init__.py +76 -0
- augmentrum-0.0.3/augmentrum/sampling/coil_sampling.py +898 -0
- augmentrum-0.0.3/augmentrum/sampling/dimension_sampling.py +495 -0
- augmentrum-0.0.3/augmentrum/sampling/kspace_reconstructor.py +713 -0
- augmentrum-0.0.3/augmentrum/sampling/kspace_sampling.py +4415 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/sampling/subject_splitter.py +113 -15
- augmentrum-0.0.3/augmentrum/utils/__init__.py +29 -0
- augmentrum-0.0.3/augmentrum/utils/download.py +178 -0
- augmentrum-0.0.3/augmentrum/utils/geometry.py +138 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/utils/plotting.py +238 -19
- {augmentrum-0.0.2 → augmentrum-0.0.3/augmentrum.egg-info}/PKG-INFO +97 -17
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum.egg-info/SOURCES.txt +26 -11
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum.egg-info/requires.txt +32 -3
- {augmentrum-0.0.2 → augmentrum-0.0.3}/pyproject.toml +40 -10
- augmentrum-0.0.3/tests/test_module_precision.py +72 -0
- augmentrum-0.0.3/tests/test_module_registry.py +262 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/tests/test_readme_table.py +131 -147
- augmentrum-0.0.3/tests/test_reproducibility.py +141 -0
- augmentrum-0.0.2/assets/figures/logo.png +0 -0
- augmentrum-0.0.2/augmentrum/__init__.py +0 -23
- augmentrum-0.0.2/augmentrum/augmentation/__init__.py +0 -32
- augmentrum-0.0.2/augmentrum/augmentation/artificial_peaks.py +0 -334
- augmentrum-0.0.2/augmentrum/augmentation/baseline_augmentation.py +0 -544
- augmentrum-0.0.2/augmentrum/augmentation/eddy_current.py +0 -278
- augmentrum-0.0.2/augmentrum/augmentation/gaussian_noise.py +0 -211
- augmentrum-0.0.2/augmentrum/augmentation/line_broadening.py +0 -179
- augmentrum-0.0.2/augmentrum/augmentation/phase_frequency.py +0 -259
- augmentrum-0.0.2/augmentrum/augmentation/residual_water.py +0 -264
- augmentrum-0.0.2/augmentrum/augmentation/spatial_augmentations.py +0 -859
- augmentrum-0.0.2/augmentrum/augmentation/spurious_echoes.py +0 -378
- augmentrum-0.0.2/augmentrum/core/__init__.py +0 -10
- augmentrum-0.0.2/augmentrum/core/augmentrum.py +0 -1172
- augmentrum-0.0.2/augmentrum/core/base_module.py +0 -423
- augmentrum-0.0.2/augmentrum/core/dataset_utils.py +0 -417
- augmentrum-0.0.2/augmentrum/core/nifti_mrs_plus.py +0 -1079
- augmentrum-0.0.2/augmentrum/core/pipeline.py +0 -358
- augmentrum-0.0.2/augmentrum/dataset/cows.py +0 -302
- augmentrum-0.0.2/augmentrum/processing/bicubic_interpolator.py +0 -296
- augmentrum-0.0.2/augmentrum/processing/nifti_raw_processor.py +0 -389
- augmentrum-0.0.2/augmentrum/processing/tricubic_interpolator.py +0 -293
- augmentrum-0.0.2/augmentrum/processing/utils.py +0 -414
- augmentrum-0.0.2/augmentrum/resources/__init__.py +0 -14
- augmentrum-0.0.2/augmentrum/resources/scans/__init__.py +0 -0
- augmentrum-0.0.2/augmentrum/sampling/coil_average_sampler.py +0 -274
- augmentrum-0.0.2/augmentrum/sampling/kspace_reconstructor.py +0 -275
- augmentrum-0.0.2/augmentrum/sampling/kspace_sampling.py +0 -2562
- augmentrum-0.0.2/augmentrum/utils/__init__.py +0 -10
- augmentrum-0.0.2/augmentrum/utils/tensor_ops.py +0 -203
- {augmentrum-0.0.2 → augmentrum-0.0.3}/LICENSE +0 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/assets/figures/overview.png +0 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum/utils/philips.py +0 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum.egg-info/dependency_links.txt +0 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/augmentrum.egg-info/top_level.txt +0 -0
- {augmentrum-0.0.2 → augmentrum-0.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: augmentrum
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: A Data Augmentation Package for MR Spectroscopy
|
|
5
5
|
Author-email: "John T. LaMaster" <jlamaste@gmail.com>, "Julian P. Merkofer" <j.p.merkofer@tue.nl>, "Kay C. Igwe" <kci2104@columbia.edu>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -23,14 +23,14 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: numpy>=1.21.0
|
|
25
25
|
Requires-Dist: scipy>=1.7.0
|
|
26
|
-
Requires-Dist: torch>=1.10.0
|
|
27
|
-
Requires-Dist: fsl-mrs>=2.1.0
|
|
28
26
|
Requires-Dist: nifti-mrs>=0.1.0
|
|
27
|
+
Requires-Dist: nifti-mrs-plus[ops]>=0.2.1
|
|
29
28
|
Requires-Dist: spec2nii>=0.6.0
|
|
30
|
-
Requires-Dist: torchkbnufft>=1.3.0
|
|
31
29
|
Requires-Dist: tqdm>=4.62.0
|
|
32
30
|
Requires-Dist: matplotlib>=3.4.0
|
|
33
31
|
Requires-Dist: shutup>=0.2.0
|
|
32
|
+
Provides-Extra: fsl
|
|
33
|
+
Requires-Dist: fsl-mrs>=2.1.0; extra == "fsl"
|
|
34
34
|
Provides-Extra: dev
|
|
35
35
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
36
36
|
Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
|
|
@@ -38,20 +38,42 @@ Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
|
38
38
|
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
39
39
|
Requires-Dist: flake8>=4.0.0; extra == "dev"
|
|
40
40
|
Requires-Dist: mypy>=0.950; extra == "dev"
|
|
41
|
+
Provides-Extra: torch
|
|
42
|
+
Requires-Dist: torch>=1.10.0; extra == "torch"
|
|
41
43
|
Provides-Extra: jax
|
|
42
44
|
Requires-Dist: jax>=0.4.0; extra == "jax"
|
|
43
45
|
Requires-Dist: jaxlib>=0.4.0; extra == "jax"
|
|
46
|
+
Provides-Extra: nufft
|
|
47
|
+
Requires-Dist: torch>=1.10.0; extra == "nufft"
|
|
48
|
+
Requires-Dist: torchkbnufft>=1.3.0; extra == "nufft"
|
|
49
|
+
Provides-Extra: tensorflow
|
|
50
|
+
Requires-Dist: tensorflow>=2.8.0; extra == "tensorflow"
|
|
51
|
+
Provides-Extra: csm
|
|
52
|
+
Requires-Dist: h5py>=3.0.0; extra == "csm"
|
|
44
53
|
Provides-Extra: keras
|
|
45
54
|
Requires-Dist: keras>=3.0.0; extra == "keras"
|
|
55
|
+
Provides-Extra: seq
|
|
56
|
+
Requires-Dist: pypulseq>=1.4.0; extra == "seq"
|
|
57
|
+
Provides-Extra: docs
|
|
58
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
59
|
+
Requires-Dist: sphinx-autobuild; extra == "docs"
|
|
60
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
61
|
+
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
62
|
+
Requires-Dist: myst-parser; extra == "docs"
|
|
63
|
+
Requires-Dist: furo; extra == "docs"
|
|
46
64
|
Provides-Extra: all
|
|
65
|
+
Requires-Dist: torch>=1.10.0; extra == "all"
|
|
66
|
+
Requires-Dist: torchkbnufft>=1.3.0; extra == "all"
|
|
67
|
+
Requires-Dist: h5py>=3.0.0; extra == "all"
|
|
47
68
|
Requires-Dist: tensorflow>=2.8.0; extra == "all"
|
|
48
69
|
Requires-Dist: keras>=3.0.0; extra == "all"
|
|
49
70
|
Requires-Dist: jax>=0.4.0; extra == "all"
|
|
50
71
|
Requires-Dist: jaxlib>=0.4.0; extra == "all"
|
|
72
|
+
Requires-Dist: pypulseq>=1.4.0; extra == "all"
|
|
51
73
|
Dynamic: license-file
|
|
52
74
|
|
|
53
75
|
<div align="center">
|
|
54
|
-
<img src="https://raw.githubusercontent.com/julianmer/Augmentrum/main/assets/
|
|
76
|
+
<img src="https://raw.githubusercontent.com/julianmer/Augmentrum/main/assets/logo/logo.png" alt="Augmentrum Logo" width="100" style="margin-bottom: -10px;"/>
|
|
55
77
|
<h1 style="margin-top: 5px; margin-bottom: 5px;">Augmentrum</h1>
|
|
56
78
|
<p style="margin-top: 0px;"><em>A Data Augmentation Package for MR Spectroscopy</em></p>
|
|
57
79
|
|
|
@@ -131,6 +153,58 @@ augmenter = Augmentrum(
|
|
|
131
153
|
train_data = augmenter.dataloader()
|
|
132
154
|
```
|
|
133
155
|
|
|
156
|
+
### Supervised Pairs: Taps and `outputs`
|
|
157
|
+
|
|
158
|
+
Any pipeline stage can be named with a `'tap:<name>'` marker and yielded by the
|
|
159
|
+
dataloaders through the `outputs` spec — an arbitrarily nested tuple of stage
|
|
160
|
+
tokens (`'data'` / `'water'` for the pipeline end, `'<tap>'` / `'<tap>.water'`
|
|
161
|
+
for a tapped stage). The classic `(data, water)` pair stays the default.
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
augmenter = Augmentrum(
|
|
165
|
+
data=data,
|
|
166
|
+
pipeline=['coil_sampling', 'macromolecules', 'tap:clean', 'undersampling'],
|
|
167
|
+
outputs=(('data', 'water'), ('clean', 'clean.water')),
|
|
168
|
+
backend='pytorch',
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# supervised reconstruction pairs: input fully augmented, target frozen at the tap
|
|
172
|
+
for (x, x_water), (y, y_water) in augmenter.dataloader():
|
|
173
|
+
train_step(x, y)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Per-Step Parameters, Seeds and Subject-Wise Splits
|
|
177
|
+
|
|
178
|
+
A keyword argument reaches every module whose constructor names it, so
|
|
179
|
+
`lb_hz=(0, 5)` would move `LineBroadening` *and* `Apodization` (you get a
|
|
180
|
+
warning). A pipeline entry can carry its own kwargs, which reach that step only
|
|
181
|
+
and override the globals there; a kwarg no module accepts raises with the
|
|
182
|
+
closest valid names instead of being ignored.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
augmenter = Augmentrum(
|
|
186
|
+
data=data,
|
|
187
|
+
groups=subject_ids, # one id per item: a subject never straddles splits
|
|
188
|
+
split_fractions={'val': 0.1, 'test': 0.1},
|
|
189
|
+
pipeline=[
|
|
190
|
+
'coil_sampling',
|
|
191
|
+
{'noise': {'sigma_frac': (0.0, 0.02)}},
|
|
192
|
+
{'line_broadening': {'lb_hz': (0.0, 5.0)}},
|
|
193
|
+
('apodization', {'mode': 'exponential', 'lb_hz': 2.0}),
|
|
194
|
+
'baseline_bspline', # registry alias fixing mode='bspline'
|
|
195
|
+
],
|
|
196
|
+
n_coils=(1, 8), # integer-typed ranges are inclusive
|
|
197
|
+
seed=1, # fixes the split, subject draws, ranges and modules
|
|
198
|
+
)
|
|
199
|
+
augmenter.split_groups['val'] # the subject ids held out for validation
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Without `groups`, a user-defined `SubjectID` header-extension field is used
|
|
203
|
+
when every item carries one. `seed` makes the whole run replay — including
|
|
204
|
+
`num_workers > 0` DataLoaders, where each worker is reseeded from it — and
|
|
205
|
+
`augmenter.reseed(k)` restarts every stream; `'fixed'` mode draws its ranges
|
|
206
|
+
once per split and keeps them across `dataloader()` calls.
|
|
207
|
+
|
|
134
208
|
---
|
|
135
209
|
|
|
136
210
|
## Module Reference & Backend Support
|
|
@@ -143,30 +217,36 @@ no manual conversion needed.
|
|
|
143
217
|
| Module | Modes / Methods | NIfTI | NumPy | PyTorch | TensorFlow | JAX | Keras |
|
|
144
218
|
|:---|:--------------------------------------------------------|:-----:|:---:|:---:|:---:|:---:|:---:|
|
|
145
219
|
| `AmplitudeScaling` | uniform, normal | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
146
|
-
| `Apodization` | exponential, truncate
|
|
220
|
+
| `Apodization` | exponential, gaussian, hamming, truncate | ✓ | ✓† | ✓† | ✓† | ✓† | ✓† |
|
|
147
221
|
| `ArtificialPeaks` | Lorentzian, Gaussian, Voigt | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
222
|
+
| `AverageSampler` | random, consecutive, strided, deterministic | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
148
223
|
| `BaselineAugmentation` | random_walk, bspline, polynomial | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
149
|
-
| `
|
|
150
|
-
| `EddyCurrent` | synthetic, water
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
224
|
+
| `CoilSampler` | draw random, reweight, grow from CSMs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
225
|
+
| `EddyCurrent` | synthetic, water (call-time reference or source library) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
226
|
+
| `EditCombiner` | diff, sum along DIM_EDIT | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
227
|
+
| `FrequencyShift` | shift_hz | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
228
|
+
| `Noise` | sigma, sigma_frac, snr, snr_db | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
229
|
+
| `KspaceUndersampling` | cartesian, gridded, nufft, dual trajectory | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
154
230
|
| `LineBroadening` | lorentzian, gaussian, voigt | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
155
|
-
| `
|
|
231
|
+
| `Macromolecules` | parametrized, measured, supplied | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
232
|
+
| `RawProcessor` | coil combination, registration, ECC, ... | ✓ | ✓‡ | ✓‡ | ✓‡ | ✓‡ | ✓‡ |
|
|
156
233
|
| `PhaseShift` | zero_order, first_order | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
157
|
-
| `ResidualWater` |
|
|
158
|
-
| `SpatialAugmentations` | 2-D / 3-D affine, flip, zoom, shear |
|
|
159
|
-
| `SpuriousEchoes` | replica, hybrid
|
|
234
|
+
| `ResidualWater` | lobes, turco (7-Lorentzian WaterFit) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
235
|
+
| `SpatialAugmentations` | 2-D / 3-D affine, flip, zoom, shear | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
236
|
+
| `SpuriousEchoes` | echo, replica, hybrid | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
237
|
+
| `Tap` | marker: snapshot a stage for `outputs` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
238
|
+
| `TransientSynthesizer` | correlated drift, respiration, motion events | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
160
239
|
| `ZeroFill` | pad FID to target length | ✓ | ✓† | ✓† | ✓† | ✓† | ✓† |
|
|
161
240
|
|
|
162
241
|
**✓** native — data tensor stays in the target framework throughout.
|
|
163
242
|
**~** automatic — the base class routes the call through the NIfTI-list path; the result is returned in the original backend format. Functional, but not natively differentiable.
|
|
164
243
|
**—** not supported / not applicable.
|
|
165
244
|
|
|
166
|
-
|
|
245
|
+
Modes that differ in backend support get a row each, named `Module[mode]`.
|
|
167
246
|
|
|
168
|
-
>
|
|
247
|
+
> **†** `Apodization[truncate]` and `ZeroFill` both change `N_PTS`. Because `target_pts` / `n_pts` is a module-level scalar, the **same length is applied uniformly to every batch member** — the output is still a uniform tensor.
|
|
169
248
|
|
|
249
|
+
> **‡** `RawProcessor[remove_outliers]` on tensor backends **zeroes outlier transients instead of dropping them** — a batched tensor cannot go ragged.
|
|
170
250
|
|
|
171
251
|
---
|
|
172
252
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/julianmer/Augmentrum/main/assets/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/julianmer/Augmentrum/main/assets/logo/logo.png" alt="Augmentrum Logo" width="100" style="margin-bottom: -10px;"/>
|
|
3
3
|
<h1 style="margin-top: 5px; margin-bottom: 5px;">Augmentrum</h1>
|
|
4
4
|
<p style="margin-top: 0px;"><em>A Data Augmentation Package for MR Spectroscopy</em></p>
|
|
5
5
|
|
|
@@ -79,6 +79,58 @@ augmenter = Augmentrum(
|
|
|
79
79
|
train_data = augmenter.dataloader()
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
### Supervised Pairs: Taps and `outputs`
|
|
83
|
+
|
|
84
|
+
Any pipeline stage can be named with a `'tap:<name>'` marker and yielded by the
|
|
85
|
+
dataloaders through the `outputs` spec — an arbitrarily nested tuple of stage
|
|
86
|
+
tokens (`'data'` / `'water'` for the pipeline end, `'<tap>'` / `'<tap>.water'`
|
|
87
|
+
for a tapped stage). The classic `(data, water)` pair stays the default.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
augmenter = Augmentrum(
|
|
91
|
+
data=data,
|
|
92
|
+
pipeline=['coil_sampling', 'macromolecules', 'tap:clean', 'undersampling'],
|
|
93
|
+
outputs=(('data', 'water'), ('clean', 'clean.water')),
|
|
94
|
+
backend='pytorch',
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# supervised reconstruction pairs: input fully augmented, target frozen at the tap
|
|
98
|
+
for (x, x_water), (y, y_water) in augmenter.dataloader():
|
|
99
|
+
train_step(x, y)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Per-Step Parameters, Seeds and Subject-Wise Splits
|
|
103
|
+
|
|
104
|
+
A keyword argument reaches every module whose constructor names it, so
|
|
105
|
+
`lb_hz=(0, 5)` would move `LineBroadening` *and* `Apodization` (you get a
|
|
106
|
+
warning). A pipeline entry can carry its own kwargs, which reach that step only
|
|
107
|
+
and override the globals there; a kwarg no module accepts raises with the
|
|
108
|
+
closest valid names instead of being ignored.
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
augmenter = Augmentrum(
|
|
112
|
+
data=data,
|
|
113
|
+
groups=subject_ids, # one id per item: a subject never straddles splits
|
|
114
|
+
split_fractions={'val': 0.1, 'test': 0.1},
|
|
115
|
+
pipeline=[
|
|
116
|
+
'coil_sampling',
|
|
117
|
+
{'noise': {'sigma_frac': (0.0, 0.02)}},
|
|
118
|
+
{'line_broadening': {'lb_hz': (0.0, 5.0)}},
|
|
119
|
+
('apodization', {'mode': 'exponential', 'lb_hz': 2.0}),
|
|
120
|
+
'baseline_bspline', # registry alias fixing mode='bspline'
|
|
121
|
+
],
|
|
122
|
+
n_coils=(1, 8), # integer-typed ranges are inclusive
|
|
123
|
+
seed=1, # fixes the split, subject draws, ranges and modules
|
|
124
|
+
)
|
|
125
|
+
augmenter.split_groups['val'] # the subject ids held out for validation
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Without `groups`, a user-defined `SubjectID` header-extension field is used
|
|
129
|
+
when every item carries one. `seed` makes the whole run replay — including
|
|
130
|
+
`num_workers > 0` DataLoaders, where each worker is reseeded from it — and
|
|
131
|
+
`augmenter.reseed(k)` restarts every stream; `'fixed'` mode draws its ranges
|
|
132
|
+
once per split and keeps them across `dataloader()` calls.
|
|
133
|
+
|
|
82
134
|
---
|
|
83
135
|
|
|
84
136
|
## Module Reference & Backend Support
|
|
@@ -91,30 +143,36 @@ no manual conversion needed.
|
|
|
91
143
|
| Module | Modes / Methods | NIfTI | NumPy | PyTorch | TensorFlow | JAX | Keras |
|
|
92
144
|
|:---|:--------------------------------------------------------|:-----:|:---:|:---:|:---:|:---:|:---:|
|
|
93
145
|
| `AmplitudeScaling` | uniform, normal | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
94
|
-
| `Apodization` | exponential, truncate
|
|
146
|
+
| `Apodization` | exponential, gaussian, hamming, truncate | ✓ | ✓† | ✓† | ✓† | ✓† | ✓† |
|
|
95
147
|
| `ArtificialPeaks` | Lorentzian, Gaussian, Voigt | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
148
|
+
| `AverageSampler` | random, consecutive, strided, deterministic | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
96
149
|
| `BaselineAugmentation` | random_walk, bspline, polynomial | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
97
|
-
| `
|
|
98
|
-
| `EddyCurrent` | synthetic, water
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
150
|
+
| `CoilSampler` | draw random, reweight, grow from CSMs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
151
|
+
| `EddyCurrent` | synthetic, water (call-time reference or source library) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
152
|
+
| `EditCombiner` | diff, sum along DIM_EDIT | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
153
|
+
| `FrequencyShift` | shift_hz | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
154
|
+
| `Noise` | sigma, sigma_frac, snr, snr_db | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
155
|
+
| `KspaceUndersampling` | cartesian, gridded, nufft, dual trajectory | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
102
156
|
| `LineBroadening` | lorentzian, gaussian, voigt | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
103
|
-
| `
|
|
157
|
+
| `Macromolecules` | parametrized, measured, supplied | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
158
|
+
| `RawProcessor` | coil combination, registration, ECC, ... | ✓ | ✓‡ | ✓‡ | ✓‡ | ✓‡ | ✓‡ |
|
|
104
159
|
| `PhaseShift` | zero_order, first_order | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
105
|
-
| `ResidualWater` |
|
|
106
|
-
| `SpatialAugmentations` | 2-D / 3-D affine, flip, zoom, shear |
|
|
107
|
-
| `SpuriousEchoes` | replica, hybrid
|
|
160
|
+
| `ResidualWater` | lobes, turco (7-Lorentzian WaterFit) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
161
|
+
| `SpatialAugmentations` | 2-D / 3-D affine, flip, zoom, shear | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
162
|
+
| `SpuriousEchoes` | echo, replica, hybrid | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
163
|
+
| `Tap` | marker: snapshot a stage for `outputs` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
164
|
+
| `TransientSynthesizer` | correlated drift, respiration, motion events | ~ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
108
165
|
| `ZeroFill` | pad FID to target length | ✓ | ✓† | ✓† | ✓† | ✓† | ✓† |
|
|
109
166
|
|
|
110
167
|
**✓** native — data tensor stays in the target framework throughout.
|
|
111
168
|
**~** automatic — the base class routes the call through the NIfTI-list path; the result is returned in the original backend format. Functional, but not natively differentiable.
|
|
112
169
|
**—** not supported / not applicable.
|
|
113
170
|
|
|
114
|
-
|
|
171
|
+
Modes that differ in backend support get a row each, named `Module[mode]`.
|
|
115
172
|
|
|
116
|
-
>
|
|
173
|
+
> **†** `Apodization[truncate]` and `ZeroFill` both change `N_PTS`. Because `target_pts` / `n_pts` is a module-level scalar, the **same length is applied uniformly to every batch member** — the output is still a uniform tensor.
|
|
117
174
|
|
|
175
|
+
> **‡** `RawProcessor[remove_outliers]` on tensor backends **zeroes outlier transients instead of dropping them** — a batched tensor cannot go ragged.
|
|
118
176
|
|
|
119
177
|
---
|
|
120
178
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
####################################################################################################
|
|
2
|
+
# __init__.py #
|
|
3
|
+
####################################################################################################
|
|
4
|
+
# #
|
|
5
|
+
# Authors: J. T. LaMaster (john.t.lamaster@gmail.com) #
|
|
6
|
+
# J. P. Merkofer (j.p.merkofer@tue.nl) #
|
|
7
|
+
# K. C. Igwe (kci2104@columbia.edu) #
|
|
8
|
+
# #
|
|
9
|
+
# Created: 2025-10-25 #
|
|
10
|
+
# #
|
|
11
|
+
# Purpose: Package entry point. Re-exports the public API: the Augmentrum container, the pipeline, #
|
|
12
|
+
# and every augmentation module. #
|
|
13
|
+
# #
|
|
14
|
+
####################################################################################################
|
|
15
|
+
|
|
16
|
+
__version__ = "0.0.3"
|
|
17
|
+
__author__ = "John T. LaMaster, Julian P. Merkofer, Kay C. Igwe"
|
|
18
|
+
__email__ = "j.p.merkofer@tue.nl"
|
|
19
|
+
|
|
20
|
+
# Main API
|
|
21
|
+
#*************#
|
|
22
|
+
# imports #
|
|
23
|
+
#*************#
|
|
24
|
+
import nifti_mrs_plus
|
|
25
|
+
|
|
26
|
+
# Claim the NIfTI-MRS provenance record before anything can write one. The
|
|
27
|
+
# container is a shared transport, so left alone it would credit itself for
|
|
28
|
+
# work Augmentrum did.
|
|
29
|
+
nifti_mrs_plus.set_provenance("Augmentrum", __version__)
|
|
30
|
+
|
|
31
|
+
from augmentrum.core import Augmentrum, NIfTI_MRS_Plus, Backend, BaseModule, AugmentationPipeline # noqa: E402
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"__version__",
|
|
35
|
+
"Augmentrum",
|
|
36
|
+
"NIfTI_MRS_Plus",
|
|
37
|
+
"Backend",
|
|
38
|
+
"BaseModule",
|
|
39
|
+
"AugmentationPipeline",
|
|
40
|
+
]
|
|
41
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
####################################################################################################
|
|
2
|
+
# augmentation/__init__.py #
|
|
3
|
+
####################################################################################################
|
|
4
|
+
# #
|
|
5
|
+
# Authors: K. C. Igwe (kci2104@columbia.edu) #
|
|
6
|
+
# J. P. Merkofer (j.p.merkofer@tue.nl) #
|
|
7
|
+
# #
|
|
8
|
+
# Created: 2026-02-13 #
|
|
9
|
+
# #
|
|
10
|
+
# Purpose: Re-exports the augmentation modules, each a BaseModule that perturbs MRS data in a #
|
|
11
|
+
# physically motivated way. #
|
|
12
|
+
# #
|
|
13
|
+
####################################################################################################
|
|
14
|
+
|
|
15
|
+
#*************#
|
|
16
|
+
# imports #
|
|
17
|
+
#*************#
|
|
18
|
+
from augmentrum.augmentation.noise import Noise
|
|
19
|
+
from augmentrum.augmentation.line_broadening import LineBroadening
|
|
20
|
+
from augmentrum.augmentation.baseline_augmentation import BaselineAugmentation
|
|
21
|
+
from augmentrum.augmentation.residual_water import ResidualWater
|
|
22
|
+
from augmentrum.augmentation.spurious_echoes import SpuriousEchoes
|
|
23
|
+
from augmentrum.augmentation.artificial_peaks import ArtificialPeaks
|
|
24
|
+
from augmentrum.augmentation.eddy_current import EddyCurrent
|
|
25
|
+
from augmentrum.augmentation.apodization import Apodization
|
|
26
|
+
from augmentrum.augmentation.phase_frequency import PhaseShift, FrequencyShift
|
|
27
|
+
from augmentrum.augmentation.amplitude_scaling import AmplitudeScaling
|
|
28
|
+
from augmentrum.augmentation.spatial_augmentations import SpatialAugmentations
|
|
29
|
+
from augmentrum.augmentation.transient_synthesis import TransientSynthesizer
|
|
30
|
+
from augmentrum.augmentation.zero_fill import ZeroFill
|
|
31
|
+
from augmentrum.augmentation.macromolecules import (
|
|
32
|
+
Macromolecules, MMSource, Parametrized, SemiParametrized, Measured, Supplied,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
'Noise',
|
|
37
|
+
'LineBroadening',
|
|
38
|
+
'BaselineAugmentation',
|
|
39
|
+
'ResidualWater',
|
|
40
|
+
'Macromolecules',
|
|
41
|
+
'MMSource',
|
|
42
|
+
'Parametrized',
|
|
43
|
+
'SemiParametrized',
|
|
44
|
+
'Measured',
|
|
45
|
+
'Supplied',
|
|
46
|
+
'SpuriousEchoes',
|
|
47
|
+
'ArtificialPeaks',
|
|
48
|
+
'EddyCurrent',
|
|
49
|
+
'Apodization',
|
|
50
|
+
'PhaseShift',
|
|
51
|
+
'FrequencyShift',
|
|
52
|
+
'AmplitudeScaling',
|
|
53
|
+
'SpatialAugmentations',
|
|
54
|
+
'TransientSynthesizer',
|
|
55
|
+
'ZeroFill',
|
|
56
|
+
]
|
|
@@ -16,18 +16,23 @@
|
|
|
16
16
|
#*************#
|
|
17
17
|
# imports #
|
|
18
18
|
#*************#
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
19
21
|
from typing import List, Optional, Union, Tuple
|
|
20
22
|
import numpy as np
|
|
21
|
-
from fsl_mrs.core import NIFTI_MRS
|
|
22
23
|
|
|
23
24
|
from augmentrum.core.base_module import BaseModule
|
|
24
|
-
from augmentrum.
|
|
25
|
-
from
|
|
25
|
+
from augmentrum.processing.utils import to_backend
|
|
26
|
+
from nifti_mrs_plus import Backend
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
# AmplitudeScaling
|
|
30
|
-
|
|
29
|
+
#**************************************************************************************************#
|
|
30
|
+
# Class AmplitudeScaling #
|
|
31
|
+
#**************************************************************************************************#
|
|
32
|
+
# #
|
|
33
|
+
# Apply amplitude scaling to MRS data. #
|
|
34
|
+
# #
|
|
35
|
+
#**************************************************************************************************#
|
|
31
36
|
class AmplitudeScaling(BaseModule):
|
|
32
37
|
"""
|
|
33
38
|
Apply amplitude scaling to MRS data.
|
|
@@ -65,6 +70,12 @@ class AmplitudeScaling(BaseModule):
|
|
|
65
70
|
SUPPORTED_BACKENDS = [Backend.NIFTI_LIST, Backend.NUMPY, Backend.PYTORCH,
|
|
66
71
|
Backend.TENSORFLOW, Backend.JAX, Backend.KERAS]
|
|
67
72
|
|
|
73
|
+
# Acts on every coil and transient alike, so per-sample masks pass through.
|
|
74
|
+
MASKS = 'pass'
|
|
75
|
+
|
|
76
|
+
# A scale broadcasts, so a batch can carry one factor per sample.
|
|
77
|
+
PER_SAMPLE_PARAMS = ('scale_factor',)
|
|
78
|
+
|
|
68
79
|
def __init__(
|
|
69
80
|
self,
|
|
70
81
|
scale_factor: Union[float, Tuple[float, float]] = 1.0,
|
|
@@ -73,29 +84,30 @@ class AmplitudeScaling(BaseModule):
|
|
|
73
84
|
**kwargs
|
|
74
85
|
):
|
|
75
86
|
"""Initialize AmplitudeScaling module."""
|
|
76
|
-
super().__init__(
|
|
87
|
+
super().__init__()
|
|
77
88
|
|
|
78
89
|
self.scale_factor = scale_factor
|
|
79
90
|
self.distribution = distribution
|
|
80
91
|
self.seed = seed
|
|
81
|
-
self.rng = np.random.default_rng(seed)
|
|
82
92
|
|
|
83
93
|
# Validate distribution
|
|
84
94
|
if distribution not in ['uniform', 'normal']:
|
|
85
95
|
raise ValueError(f"distribution must be 'uniform' or 'normal', got {distribution}")
|
|
86
96
|
|
|
87
|
-
def _get_scale_factor(self) -> float:
|
|
88
|
-
"""Get scale factor (sample if range
|
|
97
|
+
def _get_scale_factor(self, index: int = 0) -> float:
|
|
98
|
+
"""Get scale factor (sample if a range, index if a per-sample vector)."""
|
|
99
|
+
if isinstance(self.scale_factor, np.ndarray) and self.scale_factor.ndim:
|
|
100
|
+
return float(self.sample_of(self.scale_factor, index))
|
|
89
101
|
if isinstance(self.scale_factor, (tuple, list)):
|
|
90
102
|
min_scale, max_scale = self.scale_factor
|
|
91
103
|
|
|
92
104
|
if self.distribution == 'uniform':
|
|
93
|
-
return self.rng.uniform(min_scale, max_scale)
|
|
105
|
+
return self.rng.numpy_rng().uniform(min_scale, max_scale)
|
|
94
106
|
elif self.distribution == 'normal':
|
|
95
107
|
# Use range as mean ± std
|
|
96
108
|
mean = (min_scale + max_scale) / 2.0
|
|
97
109
|
std = (max_scale - min_scale) / 4.0 # ~95% within range
|
|
98
|
-
return self.rng.normal(mean, std)
|
|
110
|
+
return self.rng.numpy_rng().normal(mean, std)
|
|
99
111
|
else:
|
|
100
112
|
return float(self.scale_factor)
|
|
101
113
|
|
|
@@ -117,9 +129,9 @@ class AmplitudeScaling(BaseModule):
|
|
|
117
129
|
"""
|
|
118
130
|
scaled_data = []
|
|
119
131
|
|
|
120
|
-
for nifti in data_list:
|
|
121
|
-
# Get scale factor (sample if random)
|
|
122
|
-
scale = self._get_scale_factor()
|
|
132
|
+
for i, nifti in enumerate(data_list):
|
|
133
|
+
# Get scale factor (sample if random, index if per-sample)
|
|
134
|
+
scale = self._get_scale_factor(i)
|
|
123
135
|
|
|
124
136
|
# Get data
|
|
125
137
|
fid = nifti[:].copy()
|
|
@@ -154,14 +166,14 @@ class AmplitudeScaling(BaseModule):
|
|
|
154
166
|
"""
|
|
155
167
|
# Get scale factor for each sample in batch
|
|
156
168
|
batch_size = data.shape[0]
|
|
157
|
-
scales = np.array([self._get_scale_factor() for
|
|
169
|
+
scales = np.array([self._get_scale_factor(i) for i in range(batch_size)])
|
|
158
170
|
|
|
159
171
|
# Reshape scales for broadcasting
|
|
160
172
|
scale_shape = [batch_size] + [1] * (data.ndim - 1)
|
|
161
173
|
scales = scales.reshape(scale_shape)
|
|
162
174
|
|
|
163
175
|
# Apply scaling
|
|
164
|
-
data_scaled = data *
|
|
176
|
+
data_scaled = data * to_backend(scales, data)
|
|
165
177
|
|
|
166
178
|
return data_scaled, water
|
|
167
179
|
|