2626"""
2727
2828
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+ ):
3036 r"""
3137 Obtain the *total readout time* :math:`T_\text{ro}` from available metadata.
3238
@@ -43,6 +49,26 @@ def get_trt(in_meta, in_file=None):
4349 >>> nb.Nifti1Image(np.zeros((90, 90, 60)), None, None).to_filename(
4450 ... tmpdir.join('epi.nii.gz').strpath)
4551
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+ --------
4672
4773 >>> meta = {'TotalReadoutTime': 0.05251}
4874 >>> get_trt(meta)
@@ -159,6 +185,23 @@ def get_trt(in_meta, in_file=None):
159185 Traceback (most recent call last):
160186 ValueError:
161187
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+
162205 .. testcleanup::
163206
164207 >>> os.chdir(cwd)
@@ -194,42 +237,54 @@ def get_trt(in_meta, in_file=None):
194237 raise ValueError (f"'{ trt } '" )
195238
196239 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 ]
197251
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.
232256 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
233288
234289 raise ValueError ("Unknown total-readout time specification" )
235290
0 commit comments