sdcflows.utils.epimanip module

Manipulation of EPI data.

sdcflows.utils.epimanip.epi_mask(in_file, out_file=None)[source]

Use grayscale morphological operations to obtain a quick mask of EPI data.

sdcflows.utils.epimanip.get_trt(in_meta, in_file=None, *, use_estimate=False, fallback=None)[source]

Obtain the total readout time \(T_\text{ro}\) from available metadata.

BIDS provides two standard mechanisms to store the total readout time, \(T_\text{ro}\), of EPI scans. The first option is that a TotalReadoutTime field is found in the JSON sidecar:

Parameters:
  • in_meta (dict) – BIDS metadata dictionary.

  • in_file (str, optional) – Path to the EPI file. Used to determine the number of voxels along the phase-encoding direction.

  • use_estimate (bool, optional) – Whether to use “Estimated*” fields to calculate the total readout time. These are generated by dcm2niix when authoritative metadata is not available but heuristic methods permit an estimation.

  • fallback (float, optional) – A fallback value, in seconds, to use when the total readout time cannot be calculated. This should only be used in situations where the field is to be determined from displacement fields, as in SyN-SDC. A recommended “plausible” value would be 0.03125, to minimize the impact of floating-point errors in the calculations.

Examples

>>> meta = {'TotalReadoutTime': 0.05251}
>>> get_trt(meta)
0.05251

Alternatively, the effective echo spacing \(t_\text{ees}\) (EffectiveEchoSpacing BIDS field) may be provided. Then, the total readout time \(T_\text{ro}\) can be calculated as follows:

\[T_\text{ro} = t_\text{ees} \cdot (N_\text{PE} - 1), \label{eq:rotime-ees}\tag{1}\]

where \(N_\text{PE}\) is the number of pixels along the PE direction on the reconstructed matrix.

>>> meta = {'EffectiveEchoSpacing': 0.00059,
...         'PhaseEncodingDirection': 'j-'}
>>> f"{get_trt(meta, in_file='epi.nii.gz'):g}"
'0.05251'

Using nonstandard metadata, there are further options. If the echo spacing \(t_\text{es}\) (do not confuse with the effective echo spacing, \(t_\text{ees}\)) is set and the parallel acceleration factor (GRAPPA, ARC, etc.) of the EPI \(f_\text{acc}\) is known, then it is possible to calculate the readout time as:

\[T_\text{ro} = t_\text{es} \cdot (\left\lfloor\frac{N_\text{PE}}{f_\text{acc}} \right\rfloor - 1).\]
>>> meta = {'EchoSpacing': 0.00119341,
...         'PhaseEncodingDirection': 'j-',
...         'ParallelReductionFactorInPlane': 2}
>>> f"{get_trt(meta, in_file='epi.nii.gz'):g}"
'0.05251'

Caution

Philips stores different parameter names, and there has been quite a bit of reverse-engineering and discussion around how to get the total readout-time right for the vendor.

The implementation done here follows the findings of Dr. Rorden, summarized in this post.

It seems to be possible to calculate the effective echo spacing (in seconds) as:

\[t_\text{ees} = \frac{f_\text{wfs}} {B_0 \gamma \Delta_\text{w/f} \cdot (f_\text{EPI} + 1)}, \label{eq:philips-ees}\tag{2}\]

where \(f_\text{wfs}\) is the water-fat-shift in pixels, \(B_0\) is the field strength in T, \(\gamma\) is the gyromagnetic ratio, \(\Delta_\text{w/f}\) is the water/fat difference in ppm and \(f_\text{EPI}\) is Philip’s «EPI factor,» which accounts for in-plane acceleration with SENSE. The problem with Philip’s «EPI factor» is that it is absolutely necessary to calculate the effective echo spacing, because the reported SENSE acceleration factor does not allow to calculate the effective train length from the reconstructed matrix size along the PE direction (neither from the acquisition matrix size if it is strangely found stored within the metadata). For \(B_0 = 3.0\) [T], then \(B_0 \gamma \Delta_\text{w/f} \approx 434.215\), as in early discussions held on the FSL listserv.

As per Dr. Rorden, Eq. \(\eqref{eq:philips-ees}\) is equivalent to the following formulation:

\[t_\text{ees} = \frac{f_\text{wfs}} {3.4 \cdot F_\text{img} \cdot (f_\text{EPI} + 1)},\]

where \(F_\text{img}\) is the «Imaging Frequency» in MHz, as reported by the Philips console. This second formulation seems to be preferred for the better accuracy of the Imaging Frequency field over the Magnetic field strength.

Once the effective echo spacing is obtained, the total readout time can then be calculated with Eq. \(\eqref{eq:rotime-ees}\).

>>> meta = {'WaterFatShift': 9.2227266,
...         'EPIFactor': 35,
...         'ImagingFrequency': 127.7325,
...         'PhaseEncodingDirection': 'j-'}
>>> f"{get_trt(meta, in_file='epi.nii.gz'):0.5f}"
'0.05251'
>>> meta = {'WaterFatShift': 9.2227266,
...         'EPIFactor': 35,
...         'MagneticFieldStrength': 3,
...         'PhaseEncodingDirection': 'j-'}
>>> f"{get_trt(meta, in_file='epi.nii.gz'):0.5f}"
'0.05251'

If enough metadata is not available, raise an error:

>>> get_trt({'PhaseEncodingDirection': 'j-'},
...         in_file='epi.nii.gz')  
Traceback (most recent call last):
ValueError:

dcm2niix may provide “EstimatedTotalReadoutTime” or “EstimatedEffectiveEchoSpacing” fields when converting Philips data. In order to use these fields, pass use_estimate=True:

>>> get_trt({'EstimatedTotalReadoutTime': 0.05251}, use_estimate=True)
0.05251
>>> meta = {'EstimatedEffectiveEchoSpacing': 0.00059,
...         'PhaseEncodingDirection': 'j-'}
>>> f"{get_trt(meta, in_file='epi.nii.gz', use_estimate=True):g}"
'0.05251'

Finally, if a fallback value is provided, it will be used when the total readout time cannot be calculated by any method:

>>> get_trt({}, fallback=0.03125)
0.03125

Thanks

With thanks to Dr. Rorden for his thorough assessment and validation on the matter, and to Pravesh Parekh for his wonderful review on NeuroStars.

See Also

Some useful links regarding the calculation of the readout time for Philips: