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)[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:

>>> 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:

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: