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,537 @@
|
|
|
1
|
+
####################################################################################################
|
|
2
|
+
# brainbeats.py #
|
|
3
|
+
####################################################################################################
|
|
4
|
+
# #
|
|
5
|
+
# Authors: J. P. Merkofer (j.p.merkofer@tue.nl) #
|
|
6
|
+
# #
|
|
7
|
+
# Created: 2024-08-25 #
|
|
8
|
+
# #
|
|
9
|
+
# Purpose: Definition of data modules, taking care of loading and processing of the data. #
|
|
10
|
+
# #
|
|
11
|
+
####################################################################################################
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
#*************#
|
|
15
|
+
# imports #
|
|
16
|
+
#*************#
|
|
17
|
+
import numpy as np
|
|
18
|
+
import os
|
|
19
|
+
import scipy.io
|
|
20
|
+
import shutup; shutup.please() # shut up warnings
|
|
21
|
+
|
|
22
|
+
from fsl_mrs.core.nifti_mrs import gen_nifti_mrs, split
|
|
23
|
+
from fsl_mrs.utils.mrs_io import read_FID
|
|
24
|
+
from fsl_mrs.utils.preproc import nifti_mrs_proc as proc
|
|
25
|
+
|
|
26
|
+
from tqdm import tqdm
|
|
27
|
+
|
|
28
|
+
# own
|
|
29
|
+
from augmentrum import Augmentrum
|
|
30
|
+
from augmentrum.processing.utils import (own_nifti_coil_combination,
|
|
31
|
+
own_nifti_coil_combination_adaptive,
|
|
32
|
+
resample_signal_fir, resample_signal_lp)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#**************************************************************************************************#
|
|
36
|
+
# Function: BrainBeatsData #
|
|
37
|
+
#**************************************************************************************************#
|
|
38
|
+
# #
|
|
39
|
+
# Helper function to load the Brain Beats study data and create an Augmentrum instance. #
|
|
40
|
+
# #
|
|
41
|
+
#**************************************************************************************************#
|
|
42
|
+
def BrainBeatsData(data_dir, batch_size=16, seed=0, val_frac=0.1, test_frac=0.1,
|
|
43
|
+
n_coils=(1, None), n_averages=(1, None), pipelines=None,
|
|
44
|
+
modes=None, backend='pytorch', volatile=False, **kwargs):
|
|
45
|
+
"""
|
|
46
|
+
Load BrainBeats data and create an Augmentrum instance.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
data_dir: Path to BrainBeats data directory
|
|
50
|
+
batch_size: Batch size for dataloaders
|
|
51
|
+
seed: Random seed for reproducibility
|
|
52
|
+
val_frac: Validation fraction (default 0.1)
|
|
53
|
+
test_frac: Test fraction (default 0.1)
|
|
54
|
+
n_coils: Coil sampling range (min, max) or None
|
|
55
|
+
n_averages: Average sampling range (min, max) or None
|
|
56
|
+
pipelines: Custom pipelines dict or None for defaults
|
|
57
|
+
modes: Sampling modes dict or None for defaults
|
|
58
|
+
backend: Backend to use ('numpy', 'pytorch', etc.)
|
|
59
|
+
volatile: If True, skip provenance logging
|
|
60
|
+
**kwargs: Additional parameters for modules
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Augmentrum instance with BrainBeats data loaded
|
|
64
|
+
"""
|
|
65
|
+
if modes is None:
|
|
66
|
+
modes = {'train': 'random', 'val': 'deterministic', 'test': 'deterministic'}
|
|
67
|
+
|
|
68
|
+
# Load all data once
|
|
69
|
+
loader = BrainBeatsDataModule()
|
|
70
|
+
data_dict = loader.load_all_niftis(data_dir)
|
|
71
|
+
|
|
72
|
+
data, water = [], []
|
|
73
|
+
for subject_id, sub_data in data_dict.items():
|
|
74
|
+
for scan_id, scans in sub_data.items():
|
|
75
|
+
data.append(scans['metab'])
|
|
76
|
+
water.append(scans['wref'])
|
|
77
|
+
|
|
78
|
+
# Create and return Augmentrum instance
|
|
79
|
+
return Augmentrum(
|
|
80
|
+
data=data,
|
|
81
|
+
water=water,
|
|
82
|
+
split_fractions={'val': val_frac, 'test': test_frac},
|
|
83
|
+
pipelines=pipelines,
|
|
84
|
+
modes=modes,
|
|
85
|
+
backend=backend,
|
|
86
|
+
batch_size=batch_size,
|
|
87
|
+
seed=seed,
|
|
88
|
+
volatile=volatile,
|
|
89
|
+
n_coils=n_coils,
|
|
90
|
+
n_averages=n_averages,
|
|
91
|
+
**kwargs
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
#**************************************************************************************************#
|
|
96
|
+
# Class BrainBeatsDataModule #
|
|
97
|
+
#**************************************************************************************************#
|
|
98
|
+
# #
|
|
99
|
+
# The data module to load the brain beats study data. #
|
|
100
|
+
# #
|
|
101
|
+
#**************************************************************************************************#
|
|
102
|
+
class BrainBeatsDataModule():
|
|
103
|
+
|
|
104
|
+
#*****************************#
|
|
105
|
+
# convert labraw to nifti #
|
|
106
|
+
#*****************************#
|
|
107
|
+
def labraw2nifti(self, data_dir, bw, cf, TE, TR, npoints, save_path=None):
|
|
108
|
+
from loading.loadLABRAW import load_lab_raw, FIRdownsample
|
|
109
|
+
|
|
110
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
111
|
+
# check if item is a directory and starts with 'B'
|
|
112
|
+
if os.path.isdir(data_dir + '/' + item) and item.startswith('B'):
|
|
113
|
+
# loop through folders
|
|
114
|
+
for folder in os.listdir(data_dir + '/' + item):
|
|
115
|
+
|
|
116
|
+
if folder.startswith('2019') or folder.startswith('2020'):
|
|
117
|
+
# loop through files
|
|
118
|
+
for subfolder in os.listdir(data_dir + '/' + item + '/' + folder):
|
|
119
|
+
for file in os.listdir(data_dir + '/' + item + '/' + folder + '/' + subfolder):
|
|
120
|
+
try:
|
|
121
|
+
if file.endswith('.raw.gz'): # extract
|
|
122
|
+
os.system(f'gunzip {data_dir}/{item}/{folder}/{subfolder}/{file}')
|
|
123
|
+
file = file[:-3]
|
|
124
|
+
|
|
125
|
+
if file.endswith('.raw'):
|
|
126
|
+
data, info = load_lab_raw(f'{data_dir}/{item}/{folder}/{subfolder}/{file}')
|
|
127
|
+
# data = FIRdownsample(data, npoints)
|
|
128
|
+
|
|
129
|
+
# alternative resampling(s)
|
|
130
|
+
data = resample_signal_lp(data, npoints, bw, axis=1)
|
|
131
|
+
# data = resample_signal_fir(data, npoints)
|
|
132
|
+
|
|
133
|
+
# get data and water
|
|
134
|
+
data_met = np.transpose(data[:, :, 0, :], (1, 0, 2))
|
|
135
|
+
data_wat = np.transpose(data[:, :, 1, :2], (1, 0, 2))
|
|
136
|
+
|
|
137
|
+
# convert to nifti
|
|
138
|
+
data_met = gen_nifti_mrs(data=data_met.reshape((1, 1, 1,) + data_met.shape),
|
|
139
|
+
dwelltime=1 / bw,
|
|
140
|
+
spec_freq=cf,
|
|
141
|
+
nucleus='1H',
|
|
142
|
+
dim_tags=['DIM_COIL', 'DIM_DYN', None],
|
|
143
|
+
no_conj=True,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
data_wat = gen_nifti_mrs(data=data_wat.reshape((1, 1, 1,) + data_wat.shape),
|
|
147
|
+
dwelltime=1 / bw,
|
|
148
|
+
spec_freq=cf,
|
|
149
|
+
nucleus='1H',
|
|
150
|
+
dim_tags=['DIM_COIL', 'DIM_DYN', None],
|
|
151
|
+
no_conj=True,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# add header fields
|
|
155
|
+
data_met.add_hdr_field('RepetitionTime', TR)
|
|
156
|
+
data_met.add_hdr_field('EchoTime', TE)
|
|
157
|
+
|
|
158
|
+
data_wat.add_hdr_field('RepetitionTime', TR)
|
|
159
|
+
data_wat.add_hdr_field('EchoTime', TE)
|
|
160
|
+
|
|
161
|
+
# save
|
|
162
|
+
if save_path is not None:
|
|
163
|
+
if 'pres' in file:
|
|
164
|
+
session = 'PreS'
|
|
165
|
+
elif 'posts2' in file:
|
|
166
|
+
session = 'PostS2'
|
|
167
|
+
elif 'posts' in file:
|
|
168
|
+
session = 'PostS'
|
|
169
|
+
else:
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
if not os.path.exists(f'{save_path}/{item}/'):
|
|
173
|
+
os.makedirs(f'{save_path}/{item}/')
|
|
174
|
+
|
|
175
|
+
data_met.save(f'{save_path}/{item}/metab_{session}')
|
|
176
|
+
data_wat.save(f'{save_path}/{item}/wref_{session}')
|
|
177
|
+
|
|
178
|
+
except:
|
|
179
|
+
print(f"Issues with f'{data_dir}/{item}/{folder}/{subfolder}/{file}, skipping...")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
#********************************#
|
|
183
|
+
# convert spar sdat to nifti #
|
|
184
|
+
#********************************#
|
|
185
|
+
def sdat2nifti(self, data_dir, bw, cf, TE, TR, save_path=None):
|
|
186
|
+
from spec2nii.Philips.philips import read_sdat, read_spar
|
|
187
|
+
|
|
188
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
189
|
+
# check if item is a directory and starts with 'B'
|
|
190
|
+
if os.path.isdir(f'{data_dir}/{item}/') and item.startswith('B'):
|
|
191
|
+
# loop through files
|
|
192
|
+
for file in os.listdir(f'{data_dir}/{item}/'):
|
|
193
|
+
if file.endswith('.SPAR'):
|
|
194
|
+
try:
|
|
195
|
+
params = read_spar(f'{data_dir}/{item}/{file}')
|
|
196
|
+
data = read_sdat(f'{data_dir}/{item}/{file[:-4]}SDAT',
|
|
197
|
+
params['samples'], params['rows'])
|
|
198
|
+
except:
|
|
199
|
+
print(f"Issues with f'{data_dir}/{item}/{file}, skipping...")
|
|
200
|
+
continue # skip to the next session if files are not found
|
|
201
|
+
|
|
202
|
+
# convert to nifti
|
|
203
|
+
data = gen_nifti_mrs(data=data.reshape((1, 1, 1,) + data.shape),
|
|
204
|
+
dwelltime=1 / bw,
|
|
205
|
+
spec_freq=cf,
|
|
206
|
+
nucleus='1H',
|
|
207
|
+
dim_tags=[None, None, None],
|
|
208
|
+
no_conj=True,
|
|
209
|
+
)
|
|
210
|
+
# add header fields
|
|
211
|
+
data.add_hdr_field('RepetitionTime', TR)
|
|
212
|
+
data.add_hdr_field('EchoTime', TE)
|
|
213
|
+
|
|
214
|
+
# save
|
|
215
|
+
if save_path is not None:
|
|
216
|
+
|
|
217
|
+
if 'act' in file:
|
|
218
|
+
ty = 'metab'
|
|
219
|
+
elif 'ref' in file:
|
|
220
|
+
ty = 'wref'
|
|
221
|
+
else:
|
|
222
|
+
continue
|
|
223
|
+
|
|
224
|
+
if 'PreS' in file:
|
|
225
|
+
session = 'PreS'
|
|
226
|
+
elif 'PostS2' in file:
|
|
227
|
+
session = 'PostS2'
|
|
228
|
+
elif 'PostS' in file:
|
|
229
|
+
session = 'PostS'
|
|
230
|
+
else:
|
|
231
|
+
continue
|
|
232
|
+
|
|
233
|
+
if not os.path.exists(f'{save_path}/{item}/'):
|
|
234
|
+
os.makedirs(f'{save_path}/{item}/')
|
|
235
|
+
|
|
236
|
+
if ty == 'wref':
|
|
237
|
+
data.save(f'{save_path}/{item}/{ty}_{session}')
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
#**************************#
|
|
241
|
+
# convert mat to nifti #
|
|
242
|
+
#**************************#
|
|
243
|
+
def mat2nifti(self, data_dir, bw, cf, TE, TR, save_path=None):
|
|
244
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
245
|
+
# check if item is a directory and starts with 'B'
|
|
246
|
+
if os.path.isdir(data_dir + '/' + item) and item.startswith('B'):
|
|
247
|
+
# loop through sessions (PreS, PostS, PostS2)
|
|
248
|
+
for session in ["PreS", "PostS", "PostS2"]:
|
|
249
|
+
try:
|
|
250
|
+
mat_met = scipy.io.loadmat(f'{data_dir}/{item}/Brainbeats_{item}_{session}_metab.mat')
|
|
251
|
+
mat_wat = scipy.io.loadmat(f'{data_dir}/{item}/Brainbeats_{item}_{session}_wref.mat')
|
|
252
|
+
mat_met = mat_met['mat_met'].squeeze(-2)
|
|
253
|
+
mat_wat = mat_wat['mat_wat'].squeeze(-2)
|
|
254
|
+
except FileNotFoundError:
|
|
255
|
+
print(f"Files not found for {item} session {session}...")
|
|
256
|
+
continue # skip to the next session if files are not found
|
|
257
|
+
|
|
258
|
+
# convert to nifti
|
|
259
|
+
data_met = gen_nifti_mrs(data=mat_met.reshape((1, 1, 1,) + mat_met.shape),
|
|
260
|
+
dwelltime=1 / bw,
|
|
261
|
+
spec_freq=cf,
|
|
262
|
+
nucleus='1H',
|
|
263
|
+
dim_tags=['DIM_COIL', 'DIM_DYN', None],
|
|
264
|
+
no_conj=True,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
data_wat = gen_nifti_mrs(data=mat_wat.reshape((1, 1, 1,) + mat_wat.shape),
|
|
268
|
+
dwelltime=1 / bw,
|
|
269
|
+
spec_freq=cf,
|
|
270
|
+
nucleus='1H',
|
|
271
|
+
dim_tags=['DIM_COIL', 'DIM_DYN', None],
|
|
272
|
+
no_conj=True,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# add header fields
|
|
276
|
+
data_met.add_hdr_field('RepetitionTime', TR)
|
|
277
|
+
data_met.add_hdr_field('EchoTime', TE)
|
|
278
|
+
|
|
279
|
+
data_wat.add_hdr_field('RepetitionTime', TR)
|
|
280
|
+
data_wat.add_hdr_field('EchoTime', TE)
|
|
281
|
+
|
|
282
|
+
# save
|
|
283
|
+
if save_path is not None:
|
|
284
|
+
if not os.path.exists(f'{save_path}/{item}/'):
|
|
285
|
+
os.makedirs(f'{save_path}/{item}/')
|
|
286
|
+
data_met.save(f'{save_path}/{item}/metab_{session}')
|
|
287
|
+
data_wat.save(f'{save_path}/{item}/wref_{session}')
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
#****************************************#
|
|
292
|
+
# segment processed data into blocks #
|
|
293
|
+
#****************************************#
|
|
294
|
+
def write_blocks_preproc(self, data_dir, save_path=None):
|
|
295
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
296
|
+
# check if item is a directory and starts with 'B'
|
|
297
|
+
if os.path.isdir(data_dir + '/' + item) and item.startswith('B'):
|
|
298
|
+
# loop through sessions (PreS, PostS, PostS2)
|
|
299
|
+
for session in ["PreS", "PostS", "PostS2"]:
|
|
300
|
+
try:
|
|
301
|
+
data_met = read_FID(f'{data_dir}/{item}/preproc_{session}/metab.nii.gz')
|
|
302
|
+
data_wat = read_FID(f'{data_dir}/{item}/preproc_{session}/wref.nii.gz')
|
|
303
|
+
avg_wat = read_FID(f'{data_dir}/{item}/preproc_{session}/wref_av.nii.gz')
|
|
304
|
+
|
|
305
|
+
except FileNotFoundError:
|
|
306
|
+
print(f"Files not found for {item} session {session}...")
|
|
307
|
+
continue # skip to the next session if files are not found
|
|
308
|
+
|
|
309
|
+
# process the files
|
|
310
|
+
if session == 'PreS':
|
|
311
|
+
data_met1, data_wat1 = self.process_nifti(data_met, data_wat)
|
|
312
|
+
|
|
313
|
+
elif session == 'PostS':
|
|
314
|
+
data_met2, data_met3 = split(data_met, 'DIM_DYN', 63)
|
|
315
|
+
_, data_met3 = split(data_met3, 'DIM_DYN', 15)
|
|
316
|
+
data_met3, data_met4 = split(data_met3, 'DIM_DYN', 63)
|
|
317
|
+
_, data_met4 = split(data_met4, 'DIM_DYN', 15)
|
|
318
|
+
data_met4, _ = split(data_met4, 'DIM_DYN', 63)
|
|
319
|
+
|
|
320
|
+
data_met2, data_wat2 = self.process_nifti(data_met2, data_wat)
|
|
321
|
+
data_met3, data_wat3 = self.process_nifti(data_met3, data_wat)
|
|
322
|
+
data_met4, data_wat4 = self.process_nifti(data_met4, data_wat)
|
|
323
|
+
|
|
324
|
+
elif session == 'PostS2':
|
|
325
|
+
data_met5, data_met6 = split(data_met, 'DIM_DYN', 63)
|
|
326
|
+
|
|
327
|
+
data_met5, data_wat5 = self.process_nifti(data_met5, data_wat)
|
|
328
|
+
data_met6, data_wat6 = self.process_nifti(data_met6, data_wat)
|
|
329
|
+
|
|
330
|
+
# save the data
|
|
331
|
+
if save_path is not None:
|
|
332
|
+
all_met = [data_met1, data_met2, data_met3, data_met4, data_met5, data_met6]
|
|
333
|
+
all_wat = [data_wat1, data_wat2, data_wat3, data_wat4, data_wat5, data_wat6]
|
|
334
|
+
for i in range(len(all_met)):
|
|
335
|
+
if not os.path.exists(f'{save_path}/{item}/block{i + 1}/'):
|
|
336
|
+
os.makedirs(f'{save_path}/{item}/block{i + 1}/')
|
|
337
|
+
all_met[i].save(f'{save_path}/{item}/block{i + 1}/metab')
|
|
338
|
+
all_wat[i].save(f'{save_path}/{item}/block{i + 1}/wref')
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
#**********************************#
|
|
342
|
+
# segment the data into blocks #
|
|
343
|
+
#**********************************#
|
|
344
|
+
def write_blocks(self, data_dir, save_path=None, process='custom', coil_combination='adaptive', limit_nsa=None):
|
|
345
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
346
|
+
# check if item is a directory and starts with 'B'
|
|
347
|
+
if os.path.isdir(data_dir + '/' + item) and item.startswith('B'):
|
|
348
|
+
# loop through sessions (PreS, PostS, PostS2)
|
|
349
|
+
for session in ["PreS", "PostS", "PostS2"]:
|
|
350
|
+
try:
|
|
351
|
+
data_met = read_FID(f'{data_dir}/{item}/metab_{session}.nii.gz')
|
|
352
|
+
data_wat = read_FID(f'{data_dir}/{item}/wref_{session}.nii.gz')
|
|
353
|
+
|
|
354
|
+
except FileNotFoundError:
|
|
355
|
+
print(f"Files not found for {item} session {session}...")
|
|
356
|
+
continue # skip to the next session if files are not found
|
|
357
|
+
|
|
358
|
+
# process the files
|
|
359
|
+
if session == 'PreS':
|
|
360
|
+
data_met1, data_wat1 = data_met, data_wat
|
|
361
|
+
|
|
362
|
+
elif session == 'PostS':
|
|
363
|
+
data_met2, data_met3 = split(data_met, 'DIM_DYN', 63)
|
|
364
|
+
_, data_met3 = split(data_met3, 'DIM_DYN', 15)
|
|
365
|
+
data_met3, data_met4 = split(data_met3, 'DIM_DYN', 63)
|
|
366
|
+
_, data_met4 = split(data_met4, 'DIM_DYN', 15)
|
|
367
|
+
data_met4, _ = split(data_met4, 'DIM_DYN', 63)
|
|
368
|
+
data_wat2, data_wat3, data_wat4 = data_wat, data_wat, data_wat
|
|
369
|
+
|
|
370
|
+
elif session == 'PostS2':
|
|
371
|
+
data_met5, data_met6 = split(data_met, 'DIM_DYN', 63)
|
|
372
|
+
data_wat5, data_wat6 = data_wat, data_wat
|
|
373
|
+
|
|
374
|
+
all_met = [data_met1, data_met2, data_met3, data_met4, data_met5, data_met6]
|
|
375
|
+
all_wat = [data_wat1, data_wat2, data_wat3, data_wat4, data_wat5, data_wat6]
|
|
376
|
+
|
|
377
|
+
# limit the number of averages if specified
|
|
378
|
+
if limit_nsa is not None and isinstance(limit_nsa, int):
|
|
379
|
+
for i in range(len(all_met)):
|
|
380
|
+
if all_met[i].shape[5] > limit_nsa + 1:
|
|
381
|
+
all_met[i], _ = split(all_met[i], 'DIM_DYN', limit_nsa)
|
|
382
|
+
elif all_met[i].shape[5] == limit_nsa + 1:
|
|
383
|
+
continue # do not split if the number of averages is equal to the limit
|
|
384
|
+
else:
|
|
385
|
+
print(f"Warning: {item} block {i + 1} has only {all_met[i].shape[5]} averages, "
|
|
386
|
+
f"not splitting.")
|
|
387
|
+
|
|
388
|
+
# save the data
|
|
389
|
+
if save_path is not None:
|
|
390
|
+
sub_folder = f'preproc_{limit_nsa + 1}' if limit_nsa is not None and isinstance(limit_nsa, int) else 'preproc'
|
|
391
|
+
for i in range(len(all_met)):
|
|
392
|
+
if process.lower() == 'custom':
|
|
393
|
+
if not os.path.exists(f'{save_path}/{item}/block{i + 1}/{sub_folder}/'):
|
|
394
|
+
os.makedirs(f'{save_path}/{item}/block{i + 1}/{sub_folder}/')
|
|
395
|
+
all_met[i], all_wat[i] = self.process_nifti(all_met[i], all_wat[i],
|
|
396
|
+
report=f'{save_path}/{item}/block{i + 1}/{sub_folder}/',
|
|
397
|
+
coil_combination=coil_combination)
|
|
398
|
+
all_met[i].save(f'{save_path}/{item}/block{i + 1}/{sub_folder}/metab')
|
|
399
|
+
all_wat[i].save(f'{save_path}/{item}/block{i + 1}/{sub_folder}/wref')
|
|
400
|
+
|
|
401
|
+
elif process.lower() == 'fsl-mrs':
|
|
402
|
+
if not os.path.exists(f'{save_path}/{item}/block{i + 1}/'):
|
|
403
|
+
os.makedirs(f'{save_path}/{item}/block{i + 1}/')
|
|
404
|
+
all_met[i].save(f'{save_path}/{item}/block{i + 1}/metab')
|
|
405
|
+
all_wat[i].save(f'{save_path}/{item}/block{i + 1}/wref')
|
|
406
|
+
|
|
407
|
+
os.system(f'fsl_mrs_preproc'
|
|
408
|
+
f' --data {save_path}/{item}/block{i + 1}/metab.nii.gz'
|
|
409
|
+
f' --reference {save_path}/{item}/block{i + 1}/wref.nii.gz'
|
|
410
|
+
f' --output {save_path}/{item}/block{i + 1}/{sub_folder}'
|
|
411
|
+
f' --hlsvd --conjugate --overwrite --report')
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
#**********************************#
|
|
415
|
+
# process the nifti data files #
|
|
416
|
+
#**********************************#
|
|
417
|
+
def process_nifti(self, data_met, data_wat, report=None, coil_combination='adaptive'):
|
|
418
|
+
|
|
419
|
+
data_met = proc.conjugate(data_met)
|
|
420
|
+
data_wat = proc.conjugate(data_wat)
|
|
421
|
+
|
|
422
|
+
avg_ref = proc.average(data_wat, 'DIM_DYN')
|
|
423
|
+
|
|
424
|
+
if coil_combination.lower() == 'adaptive':
|
|
425
|
+
data_met, data_wat = own_nifti_coil_combination_adaptive(data_met, data_wat, report=report)
|
|
426
|
+
elif coil_combination.lower() == 'simple':
|
|
427
|
+
data_met = own_nifti_coil_combination(data_met, avg_ref)
|
|
428
|
+
data_wat = own_nifti_coil_combination(data_wat, avg_ref)
|
|
429
|
+
elif coil_combination.lower() == 'fsl-mrs':
|
|
430
|
+
noise = None
|
|
431
|
+
no_prewhiten = False
|
|
432
|
+
from fsl_mrs.utils.preproc.combine import estimate_noise_cov, CovarianceEstimationError
|
|
433
|
+
stacked_data = []
|
|
434
|
+
for dd, _ in data_met.iterate_over_dims(dim='DIM_COIL', iterate_over_space=True,
|
|
435
|
+
reduce_dim_index=True):
|
|
436
|
+
stacked_data.append(dd)
|
|
437
|
+
try:
|
|
438
|
+
covariance = estimate_noise_cov(np.asarray(stacked_data))
|
|
439
|
+
except CovarianceEstimationError:
|
|
440
|
+
# if the attempt to form a covariance fails, disable prewhitening
|
|
441
|
+
no_prewhiten = True
|
|
442
|
+
|
|
443
|
+
data_met = proc.coilcombine(data_met, reference=avg_ref, report=report, noise=noise,
|
|
444
|
+
covariance=covariance, no_prewhiten=no_prewhiten)
|
|
445
|
+
data_wat = proc.coilcombine(data_wat, reference=avg_ref, noise=noise,
|
|
446
|
+
covariance=covariance, no_prewhiten=no_prewhiten)
|
|
447
|
+
|
|
448
|
+
data_met = proc.align(data_met, 'DIM_DYN', ppmlim=(0.2, 4.2), report=report) # align phases
|
|
449
|
+
data_wat = proc.align(data_wat, 'DIM_DYN', ppmlim=(0, 8))
|
|
450
|
+
|
|
451
|
+
data_met, _ = proc.remove_unlike(data_met, report=report) # remove outlier avergaes
|
|
452
|
+
data_met = proc.average(data_met, 'DIM_DYN', report=report) # combine averages
|
|
453
|
+
data_wat = proc.average(data_wat, 'DIM_DYN')
|
|
454
|
+
|
|
455
|
+
data_met = proc.ecc(data_met, data_wat, report=report) # eddy current correction
|
|
456
|
+
data_wat = proc.ecc(data_wat, data_wat)
|
|
457
|
+
|
|
458
|
+
# data_met = proc.truncate_or_pad(data_met, -1, 'first', report=report) # truncation
|
|
459
|
+
# data_wat = proc.truncate_or_pad(data_wat, -1, 'first')
|
|
460
|
+
|
|
461
|
+
data_met = proc.remove_peaks(data_met, [-0.15, -0.15], limit_units='ppm',
|
|
462
|
+
report=report) # remove residual water
|
|
463
|
+
data_met = proc.shift_to_reference(data_met, 3.027, (2.9, 3.1), report=report) # shift to ref
|
|
464
|
+
|
|
465
|
+
data_met = proc.phase_correct(data_met, (2.9, 3.1), report=report) # phase corretion
|
|
466
|
+
data_wat = proc.phase_correct(data_wat, (4.55, 4.7), hlsvd=False)
|
|
467
|
+
|
|
468
|
+
if report is not None:
|
|
469
|
+
# remove report if it exists already
|
|
470
|
+
if os.path.exists(report + 'mergedReports.html'): os.remove(report + 'mergedReports.html')
|
|
471
|
+
|
|
472
|
+
import subprocess
|
|
473
|
+
import glob
|
|
474
|
+
htmlfiles = glob.glob(os.path.join(report, '*.html'))
|
|
475
|
+
subprocess.call(['merge_mrs_reports', '-d', os.path.join(report, 'metab'),
|
|
476
|
+
'-o', report, '--delete'] + htmlfiles)
|
|
477
|
+
|
|
478
|
+
return data_met, data_wat
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
#*********************#
|
|
482
|
+
# load all niftis #
|
|
483
|
+
#*********************#
|
|
484
|
+
def load_all_niftis(self, data_dir):
|
|
485
|
+
from fsl_mrs.utils.mrs_io import read_FID
|
|
486
|
+
|
|
487
|
+
sessions = ["PreS", "PostS", "PostS2"]
|
|
488
|
+
data_dict = {}
|
|
489
|
+
|
|
490
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
491
|
+
if os.path.isdir(os.path.join(data_dir, item)) and item.startswith('B'):
|
|
492
|
+
data_dict[item] = {}
|
|
493
|
+
for session in sessions:
|
|
494
|
+
metab_path = os.path.join(data_dir, item, f'metab_{session}.nii.gz')
|
|
495
|
+
wref_path = os.path.join(data_dir, item, f'wref_{session}.nii.gz')
|
|
496
|
+
|
|
497
|
+
if os.path.exists(metab_path) and os.path.exists(wref_path):
|
|
498
|
+
try:
|
|
499
|
+
data_met = read_FID(metab_path)
|
|
500
|
+
data_wat = read_FID(wref_path)
|
|
501
|
+
data_dict[item][session] = {'metab': data_met, 'wref': data_wat}
|
|
502
|
+
except Exception as e:
|
|
503
|
+
print(f"Error loading {item} {session}: {e}")
|
|
504
|
+
else:
|
|
505
|
+
print(f"Missing data for {item} session {session}, skipping.")
|
|
506
|
+
return data_dict
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
#*********************#
|
|
510
|
+
# load all blocks #
|
|
511
|
+
#*********************#
|
|
512
|
+
def load_all_blocks(self, data_dir, internal='preproc', verbose=False):
|
|
513
|
+
from fsl_mrs.utils.mrs_io import read_FID
|
|
514
|
+
|
|
515
|
+
block_data = {}
|
|
516
|
+
|
|
517
|
+
for item in tqdm(os.listdir(data_dir)):
|
|
518
|
+
subj_path = os.path.join(data_dir, item)
|
|
519
|
+
if os.path.isdir(subj_path) and item.startswith('B'):
|
|
520
|
+
block_data[item] = {}
|
|
521
|
+
for block_name in sorted(os.listdir(subj_path)):
|
|
522
|
+
block_path = os.path.join(subj_path, block_name, internal)
|
|
523
|
+
metab_file = os.path.join(block_path, 'metab.nii.gz')
|
|
524
|
+
wref_file = os.path.join(block_path, 'wref.nii.gz')
|
|
525
|
+
|
|
526
|
+
if os.path.exists(metab_file) and os.path.exists(wref_file):
|
|
527
|
+
try:
|
|
528
|
+
data_met = read_FID(metab_file)
|
|
529
|
+
data_wat = read_FID(wref_file)
|
|
530
|
+
block_data[item][block_name] = {'metab': data_met, 'wref': data_wat}
|
|
531
|
+
except Exception as e:
|
|
532
|
+
print(f"Error loading {item} {block_name}: {e}")
|
|
533
|
+
else:
|
|
534
|
+
if verbose: print(f"Missing files in {item} {block_name}, skipping.")
|
|
535
|
+
|
|
536
|
+
return block_data
|
|
537
|
+
|