26
26
"""
27
27
28
28
29
- def get_trt (in_meta , in_file = None ):
29
+ def get_trt (
30
+ in_meta ,
31
+ in_file = None ,
32
+ * ,
33
+ use_estimate = False ,
34
+ fallback = None ,
35
+ ):
30
36
r"""
31
37
Obtain the *total readout time* :math:`T_\text{ro}` from available metadata.
32
38
@@ -43,6 +49,26 @@ def get_trt(in_meta, in_file=None):
43
49
>>> nb.Nifti1Image(np.zeros((90, 90, 60)), None, None).to_filename(
44
50
... tmpdir.join('epi.nii.gz').strpath)
45
51
52
+ Parameters
53
+ ----------
54
+ in_meta: :class:`dict`
55
+ BIDS metadata dictionary.
56
+ in_file: :class:`str`, optional
57
+ Path to the EPI file. Used to determine the number of voxels along the
58
+ phase-encoding direction.
59
+ use_estimate: :class:`bool`, optional
60
+ Whether to use "Estimated*" fields to calculate the total readout time.
61
+ These are generated by dcm2niix when authoritative metadata is not available
62
+ but heuristic methods permit an estimation.
63
+ fallback: :class:`float`, optional
64
+ A fallback value, in seconds, to use when the total readout time cannot be
65
+ calculated. This should only be used in situations where the field is to be
66
+ determined from displacement fields, as in SyN-SDC.
67
+ A recommended "plausible" value would be 0.03125, to minimize the impact of
68
+ floating-point errors in the calculations.
69
+
70
+ Examples
71
+ --------
46
72
47
73
>>> meta = {'TotalReadoutTime': 0.05251}
48
74
>>> get_trt(meta)
@@ -159,6 +185,23 @@ def get_trt(in_meta, in_file=None):
159
185
Traceback (most recent call last):
160
186
ValueError:
161
187
188
+ dcm2niix may provide "EstimatedTotalReadoutTime" or "EstimatedEffectiveEchoSpacing"
189
+ fields when converting Philips data. In order to use these fields, pass
190
+ ``use_estimate=True``:
191
+
192
+ >>> get_trt({'EstimatedTotalReadoutTime': 0.05251}, use_estimate=True)
193
+ 0.05251
194
+ >>> meta = {'EstimatedEffectiveEchoSpacing': 0.00059,
195
+ ... 'PhaseEncodingDirection': 'j-'}
196
+ >>> f"{get_trt(meta, in_file='epi.nii.gz', use_estimate=True):g}"
197
+ '0.05251'
198
+
199
+ Finally, if a fallback value is provided, it will be used when the total readout
200
+ time cannot be calculated by any method:
201
+
202
+ >>> get_trt({}, fallback=0.03125)
203
+ 0.03125
204
+
162
205
.. testcleanup::
163
206
164
207
>>> os.chdir(cwd)
@@ -194,42 +237,54 @@ def get_trt(in_meta, in_file=None):
194
237
raise ValueError (f"'{ trt } '" )
195
238
196
239
return trt
240
+ elif use_estimate and "EstimatedTotalReadoutTime" in in_meta :
241
+ trt = in_meta .get ("EstimatedTotalReadoutTime" )
242
+ if not trt :
243
+ raise ValueError (f"'{ trt } '" )
244
+
245
+ return trt
246
+
247
+ if "PhaseEncodingDirection" in in_meta :
248
+ # npe = N voxels PE direction
249
+ pe_index = "ijk" .index (in_meta ["PhaseEncodingDirection" ][0 ])
250
+ npe = nb .load (in_file ).shape [pe_index ]
197
251
198
- # npe = N voxels PE direction
199
- pe_index = "ijk" .index (in_meta ["PhaseEncodingDirection" ][0 ])
200
- npe = nb .load (in_file ).shape [pe_index ]
201
-
202
- # Use case 2: EES is defined
203
- ees = in_meta .get ("EffectiveEchoSpacing" )
204
- if ees :
205
- # Effective echo spacing means that acceleration factors have been accounted for.
206
- return ees * (npe - 1 )
207
-
208
- try :
209
- echospacing = in_meta ["EchoSpacing" ]
210
- acc_factor = in_meta ["ParallelReductionFactorInPlane" ]
211
- except KeyError :
212
- pass
213
- else :
214
- # etl = effective train length
215
- etl = npe // acc_factor
216
- return echospacing * (etl - 1 )
217
-
218
- # Use case 3 (Philips scans)
219
- try :
220
- wfs = in_meta ["WaterFatShift" ]
221
- epifactor = in_meta ["EPIFactor" ]
222
- except KeyError :
223
- pass
224
- else :
225
- wfs_hz = (
226
- (in_meta .get ("ImagingFrequency" , 0 ) * 3.39941 )
227
- or (in_meta .get ("MagneticFieldStrength" , 0 ) * 144.7383333 )
228
- or None
229
- )
230
- if wfs_hz :
231
- ees = wfs / (wfs_hz * (epifactor + 1 ))
252
+ # Use case 2: EES is defined
253
+ ees = in_meta .get ("EffectiveEchoSpacing" )
254
+ if ees :
255
+ # Effective echo spacing means that acceleration factors have been accounted for.
232
256
return ees * (npe - 1 )
257
+ elif use_estimate and "EstimatedEffectiveEchoSpacing" in in_meta :
258
+ return in_meta .get ("EstimatedEffectiveEchoSpacing" ) * (npe - 1 )
259
+
260
+ try :
261
+ echospacing = in_meta ["EchoSpacing" ]
262
+ acc_factor = in_meta ["ParallelReductionFactorInPlane" ]
263
+ except KeyError :
264
+ pass
265
+ else :
266
+ # etl = effective train length
267
+ etl = npe // acc_factor
268
+ return echospacing * (etl - 1 )
269
+
270
+ # Use case 3 (Philips scans)
271
+ try :
272
+ wfs = in_meta ["WaterFatShift" ]
273
+ epifactor = in_meta ["EPIFactor" ]
274
+ except KeyError :
275
+ pass
276
+ else :
277
+ wfs_hz = (
278
+ (in_meta .get ("ImagingFrequency" , 0 ) * 3.39941 )
279
+ or (in_meta .get ("MagneticFieldStrength" , 0 ) * 144.7383333 )
280
+ or None
281
+ )
282
+ if wfs_hz :
283
+ ees = wfs / (wfs_hz * (epifactor + 1 ))
284
+ return ees * (npe - 1 )
285
+
286
+ if fallback :
287
+ return fallback
233
288
234
289
raise ValueError ("Unknown total-readout time specification" )
235
290
0 commit comments