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.
Files changed (42) hide show
  1. augmentrum/__init__.py +23 -0
  2. augmentrum/augmentation/__init__.py +32 -0
  3. augmentrum/augmentation/amplitude_scaling.py +170 -0
  4. augmentrum/augmentation/apodization.py +275 -0
  5. augmentrum/augmentation/artificial_peaks.py +334 -0
  6. augmentrum/augmentation/baseline_augmentation.py +544 -0
  7. augmentrum/augmentation/eddy_current.py +278 -0
  8. augmentrum/augmentation/gaussian_noise.py +211 -0
  9. augmentrum/augmentation/line_broadening.py +179 -0
  10. augmentrum/augmentation/phase_frequency.py +259 -0
  11. augmentrum/augmentation/residual_water.py +264 -0
  12. augmentrum/augmentation/spatial_augmentations.py +859 -0
  13. augmentrum/augmentation/spurious_echoes.py +378 -0
  14. augmentrum/augmentation/zero_fill.py +163 -0
  15. augmentrum/core/__init__.py +10 -0
  16. augmentrum/core/augmentrum.py +1172 -0
  17. augmentrum/core/base_module.py +423 -0
  18. augmentrum/core/dataset_utils.py +417 -0
  19. augmentrum/core/nifti_mrs_plus.py +1079 -0
  20. augmentrum/core/pipeline.py +358 -0
  21. augmentrum/dataset/brainbeats.py +537 -0
  22. augmentrum/dataset/cows.py +302 -0
  23. augmentrum/dataset/fmrsinpain.py +208 -0
  24. augmentrum/processing/bicubic_interpolator.py +296 -0
  25. augmentrum/processing/nifti_raw_processor.py +389 -0
  26. augmentrum/processing/tricubic_interpolator.py +293 -0
  27. augmentrum/processing/utils.py +414 -0
  28. augmentrum/resources/__init__.py +14 -0
  29. augmentrum/resources/scans/__init__.py +0 -0
  30. augmentrum/sampling/coil_average_sampler.py +274 -0
  31. augmentrum/sampling/kspace_reconstructor.py +275 -0
  32. augmentrum/sampling/kspace_sampling.py +2562 -0
  33. augmentrum/sampling/subject_splitter.py +131 -0
  34. augmentrum/utils/__init__.py +10 -0
  35. augmentrum/utils/philips.py +90 -0
  36. augmentrum/utils/plotting.py +651 -0
  37. augmentrum/utils/tensor_ops.py +203 -0
  38. augmentrum-0.0.1.dist-info/METADATA +189 -0
  39. augmentrum-0.0.1.dist-info/RECORD +42 -0
  40. augmentrum-0.0.1.dist-info/WHEEL +5 -0
  41. augmentrum-0.0.1.dist-info/licenses/LICENSE +202 -0
  42. 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
+