augmentrum 0.0.1__py3-none-any.whl
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/__init__.py +23 -0
- augmentrum/augmentation/__init__.py +32 -0
- augmentrum/augmentation/amplitude_scaling.py +170 -0
- augmentrum/augmentation/apodization.py +275 -0
- augmentrum/augmentation/artificial_peaks.py +334 -0
- augmentrum/augmentation/baseline_augmentation.py +544 -0
- augmentrum/augmentation/eddy_current.py +278 -0
- augmentrum/augmentation/gaussian_noise.py +211 -0
- augmentrum/augmentation/line_broadening.py +179 -0
- augmentrum/augmentation/phase_frequency.py +259 -0
- augmentrum/augmentation/residual_water.py +264 -0
- augmentrum/augmentation/spatial_augmentations.py +859 -0
- augmentrum/augmentation/spurious_echoes.py +378 -0
- augmentrum/augmentation/zero_fill.py +163 -0
- augmentrum/core/__init__.py +10 -0
- augmentrum/core/augmentrum.py +1172 -0
- augmentrum/core/base_module.py +423 -0
- augmentrum/core/dataset_utils.py +417 -0
- augmentrum/core/nifti_mrs_plus.py +1079 -0
- augmentrum/core/pipeline.py +358 -0
- augmentrum/dataset/brainbeats.py +537 -0
- augmentrum/dataset/cows.py +302 -0
- augmentrum/dataset/fmrsinpain.py +208 -0
- augmentrum/processing/bicubic_interpolator.py +296 -0
- augmentrum/processing/nifti_raw_processor.py +389 -0
- augmentrum/processing/tricubic_interpolator.py +293 -0
- augmentrum/processing/utils.py +414 -0
- augmentrum/resources/__init__.py +14 -0
- augmentrum/resources/scans/__init__.py +0 -0
- augmentrum/sampling/coil_average_sampler.py +274 -0
- augmentrum/sampling/kspace_reconstructor.py +275 -0
- augmentrum/sampling/kspace_sampling.py +2562 -0
- augmentrum/sampling/subject_splitter.py +131 -0
- augmentrum/utils/__init__.py +10 -0
- augmentrum/utils/philips.py +90 -0
- augmentrum/utils/plotting.py +651 -0
- augmentrum/utils/tensor_ops.py +203 -0
- augmentrum-0.0.1.dist-info/METADATA +189 -0
- augmentrum-0.0.1.dist-info/RECORD +42 -0
- augmentrum-0.0.1.dist-info/WHEEL +5 -0
- augmentrum-0.0.1.dist-info/licenses/LICENSE +202 -0
- augmentrum-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
####################################################################################################
|
|
2
|
+
# subject_splitter.py #
|
|
3
|
+
####################################################################################################
|
|
4
|
+
# #
|
|
5
|
+
# Authors: J. P. Merkofer (j.p.merkofer@tue.nl) #
|
|
6
|
+
# #
|
|
7
|
+
# Created: 2025-10-07 #
|
|
8
|
+
# #
|
|
9
|
+
# Purpose: Implements SubjectSplitter for reproducible subject-wise train/val/test splits. #
|
|
10
|
+
# Supports stratified or random splitting modes. #
|
|
11
|
+
# #
|
|
12
|
+
####################################################################################################
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
#*************#
|
|
16
|
+
# imports #
|
|
17
|
+
#*************#
|
|
18
|
+
import torch
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#**************************************************************************************************#
|
|
22
|
+
# Class SubjectSampler #
|
|
23
|
+
#**************************************************************************************************#
|
|
24
|
+
# #
|
|
25
|
+
# Simplistic subject splitter for train/val/test splits. Supports random splitting. #
|
|
26
|
+
# #
|
|
27
|
+
#**************************************************************************************************#
|
|
28
|
+
class SubjectSplitter:
|
|
29
|
+
"""
|
|
30
|
+
Splits subjects into train/val/test sets.
|
|
31
|
+
|
|
32
|
+
Works with any backend (NIfTI_MRS_Plus, lists, or raw data).
|
|
33
|
+
Returns splits in the same format as input.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, data, water=None, seed=0, val_frac=0.1, test_frac=0.1):
|
|
37
|
+
"""
|
|
38
|
+
Initialization.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
data: Input data (NIfTI_MRS_Plus, list, or other).
|
|
42
|
+
water: Water reference data (same format as data), optional.
|
|
43
|
+
seed (int): Random seed for reproducibility.
|
|
44
|
+
val_frac (float): Fraction of data for validation set.
|
|
45
|
+
test_frac (float): Fraction of data for test set.
|
|
46
|
+
"""
|
|
47
|
+
self.data = data
|
|
48
|
+
self.water = water
|
|
49
|
+
self.val_frac = val_frac
|
|
50
|
+
self.test_frac = test_frac
|
|
51
|
+
self.seed = seed
|
|
52
|
+
torch.manual_seed(seed)
|
|
53
|
+
|
|
54
|
+
def __call__(self, **kwargs):
|
|
55
|
+
"""
|
|
56
|
+
Perform the split and return the dictionary of splits.
|
|
57
|
+
"""
|
|
58
|
+
return self.split()
|
|
59
|
+
|
|
60
|
+
def split(self):
|
|
61
|
+
"""
|
|
62
|
+
Splits subjects into train/val/test sets.
|
|
63
|
+
|
|
64
|
+
Handles NIfTI_MRS_Plus with any backend by converting to list,
|
|
65
|
+
splitting, and optionally converting back.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
dict: Dictionary with 'train', 'val', and 'test' keys.
|
|
69
|
+
Each value is a tuple of (data, water).
|
|
70
|
+
"""
|
|
71
|
+
# Import here to avoid circular imports
|
|
72
|
+
from augmentrum.core import NIfTI_MRS_Plus
|
|
73
|
+
|
|
74
|
+
# Store original type and backend for later reconstruction
|
|
75
|
+
is_nifti_plus = isinstance(self.data, NIfTI_MRS_Plus)
|
|
76
|
+
original_backend = self.data.backend if is_nifti_plus else None
|
|
77
|
+
original_volatile = self.data.volatile if is_nifti_plus else False
|
|
78
|
+
|
|
79
|
+
# Convert to list for splitting
|
|
80
|
+
if is_nifti_plus:
|
|
81
|
+
data_list = self.data.to_nifti_list()
|
|
82
|
+
else:
|
|
83
|
+
data_list = self.data if isinstance(self.data, list) else list(self.data)
|
|
84
|
+
|
|
85
|
+
if self.water is not None:
|
|
86
|
+
if isinstance(self.water, NIfTI_MRS_Plus):
|
|
87
|
+
water_list = self.water.to_nifti_list()
|
|
88
|
+
else:
|
|
89
|
+
water_list = self.water if isinstance(self.water, list) else list(self.water)
|
|
90
|
+
|
|
91
|
+
# Validate that water_list and data_list have the same length
|
|
92
|
+
if len(water_list) != len(data_list):
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"Mismatch between data and water lists: "
|
|
95
|
+
f"data has {len(data_list)} subjects but water has {len(water_list)} subjects. "
|
|
96
|
+
f"They must have the same length."
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
water_list = None
|
|
100
|
+
|
|
101
|
+
# Perform random split
|
|
102
|
+
idxs = torch.randperm(len(data_list)).tolist()
|
|
103
|
+
n_total = len(idxs)
|
|
104
|
+
n_test = int(n_total * self.test_frac)
|
|
105
|
+
n_val = int(n_total * self.val_frac)
|
|
106
|
+
|
|
107
|
+
def get_lists(sel):
|
|
108
|
+
data_sel = [data_list[i] for i in sel]
|
|
109
|
+
water_sel = [water_list[i] for i in sel] if water_list is not None else None
|
|
110
|
+
|
|
111
|
+
# Optionally convert back to NIfTI_MRS_Plus with original backend
|
|
112
|
+
if is_nifti_plus and len(data_sel) > 0:
|
|
113
|
+
data_sel = NIfTI_MRS_Plus(
|
|
114
|
+
nifti_list=data_sel,
|
|
115
|
+
backend=original_backend,
|
|
116
|
+
volatile=original_volatile
|
|
117
|
+
)
|
|
118
|
+
if water_sel is not None:
|
|
119
|
+
water_sel = NIfTI_MRS_Plus(
|
|
120
|
+
nifti_list=water_sel,
|
|
121
|
+
backend=original_backend,
|
|
122
|
+
volatile=original_volatile
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return data_sel, water_sel
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
'train': get_lists(idxs[n_test + n_val:]),
|
|
129
|
+
'val': get_lists(idxs[n_test:n_test + n_val]),
|
|
130
|
+
'test': get_lists(idxs[:n_test]),
|
|
131
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
####################################################################################################
|
|
2
|
+
# philips.py #
|
|
3
|
+
####################################################################################################
|
|
4
|
+
# #
|
|
5
|
+
# Authors: J. P. Merkofer (j.p.merkofer@tue.nl) #
|
|
6
|
+
# #
|
|
7
|
+
# Created: 20/06/22 #
|
|
8
|
+
# #
|
|
9
|
+
# Purpose: Contains various Philips specific utils. #
|
|
10
|
+
# #
|
|
11
|
+
####################################################################################################
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#*************#
|
|
15
|
+
# imports #
|
|
16
|
+
#*************#
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
#****************#
|
|
21
|
+
# definition #
|
|
22
|
+
#****************#
|
|
23
|
+
index_options = ['STD', 'REJ', 'PHC', 'FRC', 'NOI', 'NAV']
|
|
24
|
+
|
|
25
|
+
index_headers = ['typ', 'mix', 'dyn', 'card', 'echo', 'loca', 'chan',
|
|
26
|
+
'extr1', 'extr2', 'kx', 'ky', 'kz', 'aver', 'sign',
|
|
27
|
+
'rf', 'grad', 'enc', 'rtop', 'rr', 'size', 'offset']
|
|
28
|
+
|
|
29
|
+
indices = ['chan', 'aver', 'dyn', 'card', 'echo', 'loca', 'extr1', 'extr2']
|
|
30
|
+
|
|
31
|
+
defaults = {'chan': 'DIM_COIL',
|
|
32
|
+
'aver': 'DIM_DYN',
|
|
33
|
+
'dyn': 'DIM_DYN',
|
|
34
|
+
'echo': 'DIM_INDIRECT_0'}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
#***********************#
|
|
38
|
+
# read a .data file #
|
|
39
|
+
#***********************#
|
|
40
|
+
def read_Philips_data(data_file, df):
|
|
41
|
+
"""
|
|
42
|
+
Reads a .data file with given .list data frame. Parts of the code are taken from:
|
|
43
|
+
https://github.com/wtclarke/spec2nii/blob/master/spec2nii/philips_data_list.py
|
|
44
|
+
|
|
45
|
+
@param data_file -- The path to the data file.
|
|
46
|
+
@param df -- The data frame obtained from the .list file.
|
|
47
|
+
|
|
48
|
+
@returns -- A dict containing the data.
|
|
49
|
+
"""
|
|
50
|
+
with open(data_file, 'rb') as f:
|
|
51
|
+
raw = np.fromfile(f, dtype='<f4')
|
|
52
|
+
raw = raw[0::2] + 1j * raw[1::2]
|
|
53
|
+
|
|
54
|
+
data_types = df.typ.unique()
|
|
55
|
+
output_dict = {}
|
|
56
|
+
for tt in data_types:
|
|
57
|
+
curr_df = df.loc[df.typ == tt, :]
|
|
58
|
+
if tt == 'NOI':
|
|
59
|
+
# special simple case for NOI
|
|
60
|
+
spec_res = int(curr_df['size'].max() / 8)
|
|
61
|
+
ncha = curr_df['chan'].max() + 1
|
|
62
|
+
nloca = curr_df['loca'].max() + 1
|
|
63
|
+
output_dict[tt] = np.zeros((spec_res, ncha, nloca), dtype=np.complex128)
|
|
64
|
+
else:
|
|
65
|
+
n_mix = curr_df['mix'].max() + 1
|
|
66
|
+
# other types might use all the loops
|
|
67
|
+
for mix in range(n_mix):
|
|
68
|
+
curr_mix_df = curr_df.loc[curr_df.mix == mix, :]
|
|
69
|
+
shape = []
|
|
70
|
+
shape.append(int(curr_mix_df['size'].max() / 8))
|
|
71
|
+
for ind in indices:
|
|
72
|
+
shape.append(curr_mix_df[ind].max() + 1)
|
|
73
|
+
output_dict[f'{tt}_{mix}'] = np.zeros(shape, dtype=np.complex128)
|
|
74
|
+
|
|
75
|
+
# now extract data
|
|
76
|
+
offset = 0
|
|
77
|
+
for ind in range(df.shape[0]):
|
|
78
|
+
cdf = df.iloc[ind]
|
|
79
|
+
tt = cdf.typ
|
|
80
|
+
dsize = int(cdf['size'].max() / 8)
|
|
81
|
+
if tt == 'NOI':
|
|
82
|
+
output_dict[tt][:, cdf.chan, cdf.loca] = raw[offset:(offset + dsize)]
|
|
83
|
+
else:
|
|
84
|
+
mix = cdf.mix
|
|
85
|
+
ind = [cdf[ii] for ii in indices]
|
|
86
|
+
ind = tuple([slice(None), ] + ind)
|
|
87
|
+
output_dict[f'{tt}_{mix}'][ind] = raw[offset:(offset + dsize)]
|
|
88
|
+
offset += dsize
|
|
89
|
+
|
|
90
|
+
return output_dict
|