Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance support for GE data #132

Merged
merged 20 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ __pycache__
.DS_Store
*.png
prototyping
.idea/
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
This document contains the Spec2nii release history in reverse chronological order.

0.7.4 (WIP)
---------------------------------
- Refinements and improvements to the GE SVS pipeline from Mark Mikkelsen

0.7.3 (Tuesday 12th March 2024)
-------------------------------
- Siemens .rda format now had corrected and validated orientations (tested on VE11 baseline).
Expand Down
34 changes: 26 additions & 8 deletions spec2nii/GE/ge_hdr_fields.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'''List of GE p-file offsets.
"""List of GE p-file offsets.

This code is taken from the VESPA project https://scion.duhs.duke.edu/vespa/project.
I therefore include their BSD statement here.
Expand Down Expand Up @@ -60,7 +60,7 @@
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
"""

import ctypes as ct
import numpy as np
Expand Down Expand Up @@ -754,10 +754,16 @@ def get_pfile_hdr_fields(version):
plist.append(('rhr_rh_npasses', ct.c_short))
plist.append(('pad_xx', ct.c_char * 2))
plist.append(('rhr_rh_nslices', ct.c_short))
plist.append(('pad_xx', ct.c_char * 10))
plist.append(('rhr_rh_nechoes', ct.c_short))
plist.append(('rhr_rh_navs', ct.c_short))
plist.append(('rhr_rh_nframes', ct.c_short))
plist.append(('pad_xx', ct.c_char * 4))
plist.append(('rhr_rh_frame_size', ct.c_ushort))
plist.append(('rhr_rh_point_size', ct.c_short))
plist.append(('pad_xx', ct.c_char * 32))
plist.append(('pad_xx', ct.c_char * 18))
plist.append(('rhr_rh_da_xres', ct.c_short))
plist.append(('rhr_rh_da_yres', ct.c_short))
plist.append(('pad_xx', ct.c_char * 10))
plist.append(('rhr_rh_raw_pass_size', ct.c_uint))
plist.append(('pad_xx', ct.c_char * 80))
plist.append(('rhr_rh_dab[0]_start_rcv', ct.c_short))
Expand Down Expand Up @@ -978,10 +984,16 @@ def get_pfile_hdr_fields(version):
plist.append(('rhr_rh_npasses', ct.c_short))
plist.append(('pad_xx', ct.c_char * 2))
plist.append(('rhr_rh_nslices', ct.c_short))
plist.append(('pad_xx', ct.c_char * 10))
plist.append(('rhr_rh_nechoes', ct.c_short))
plist.append(('rhr_rh_navs', ct.c_short))
plist.append(('rhr_rh_nframes', ct.c_short))
plist.append(('pad_xx', ct.c_char * 4))
plist.append(('rhr_rh_frame_size', ct.c_ushort))
plist.append(('rhr_rh_point_size', ct.c_short))
plist.append(('pad_xx', ct.c_char * 32))
plist.append(('pad_xx', ct.c_char * 18))
plist.append(('rhr_rh_da_xres', ct.c_short))
plist.append(('rhr_rh_da_yres', ct.c_short))
plist.append(('pad_xx', ct.c_char * 10))
plist.append(('rhr_rh_raw_pass_size', ct.c_uint))
plist.append(('pad_xx', ct.c_char * 80))
plist.append(('rhr_rh_dab[0]_start_rcv', ct.c_short))
Expand Down Expand Up @@ -1202,10 +1214,16 @@ def get_pfile_hdr_fields(version):
plist.append(('rhr_rh_npasses', ct.c_short))
plist.append(('pad_xx', ct.c_char * 2))
plist.append(('rhr_rh_nslices', ct.c_short))
plist.append(('pad_xx', ct.c_char * 10))
plist.append(('rhr_rh_nechoes', ct.c_short))
plist.append(('rhr_rh_navs', ct.c_short))
plist.append(('rhr_rh_nframes', ct.c_short))
plist.append(('pad_xx', ct.c_char * 4))
plist.append(('rhr_rh_frame_size', ct.c_ushort))
plist.append(('rhr_rh_point_size', ct.c_short))
plist.append(('pad_xx', ct.c_char * 32))
plist.append(('pad_xx', ct.c_char * 18))
plist.append(('rhr_rh_da_xres', ct.c_short))
plist.append(('rhr_rh_da_yres', ct.c_short))
plist.append(('pad_xx', ct.c_char * 10))
plist.append(('rhr_rh_raw_pass_size', ct.c_uint))
plist.append(('pad_xx', ct.c_char * 80))
plist.append(('rhr_rh_dab[0]_start_rcv', ct.c_short))
Expand Down
52 changes: 28 additions & 24 deletions spec2nii/GE/ge_pfile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'''spec2nii module containing functions specific to interpreting the GE pfile format
"""spec2nii module containing functions specific to interpreting the GE pfile format

Author: William Clarke <[email protected]>
Copyright (C) 2020 University of Oxford
Expand Down Expand Up @@ -33,7 +33,7 @@

For portions of this code, copyright and license information differs from
the above. In these cases, copyright and/or license information is inline.
'''
"""

# Python modules
from datetime import datetime
Expand Down Expand Up @@ -83,25 +83,29 @@ def read_pfile(filename, fname_out):


def _process_svs_pfile(pfile):
'''Handle SVS data
"""Handle SVS data

:param Pfile pfile: Pfile object
:return: List of NIFTI MRS data objects
:return: List of file name suffixes
'''
"""
psd = pfile.hdr.rhi_psdname.decode('utf-8').lower()

if psd in ('probe-p', 'probe-s'):
# MM: Some 'gaba' psd strings contain full path names, so truncate to the end of the path
if psd.endswith('gaba'):
psd = 'gaba'

numecho = pfile.hdr.rhi_numecho

if psd in ('probe-p', 'probe-s', 'probe-p_ach'):
data, meta, dwelltime, fname_suffix = _process_probe_p(pfile)
elif psd in ('oslaser', 'slaser_cni'):
elif psd in ('oslaser', 'slaser_cni') and numecho == 1: # MM: If non-edited data, use _process_oslaser
data, meta, dwelltime, fname_suffix = _process_oslaser(pfile)
elif psd == 'oslaser' and numecho > 1: # MM: If edited data, use _process_gaba
data, meta, dwelltime, fname_suffix = _process_gaba(pfile)
elif psd == 'slaser':
data, meta, dwelltime, fname_suffix = _process_slaser(pfile)
elif psd in ('gaba', 'hbcd'):
data, meta, dwelltime, fname_suffix = _process_gaba(pfile)
elif 'jpress_ac' in psd: # Bergen patch
data, meta, dwelltime, fname_suffix = _process_gaba(pfile)
elif psd == 'jpress':
elif psd in ('jpress', 'jpress_ac', 'gaba', 'hbcd', 'probe-p-mega_rml', 'repress7'):
data, meta, dwelltime, fname_suffix = _process_gaba(pfile)
else:
raise UnsupportedPulseSequenceError(f'Unrecognised sequence {psd}.')
Expand All @@ -116,12 +120,12 @@ def _process_svs_pfile(pfile):


def _process_probe_p(pfile):
'''Extract metabolite and reference data from a prob_p format pfile
"""Extract metabolite and reference data from a prob_p format pfile

:param Pfile pfile: Pfile object
:return: List numpy data arrays
:return: List of file name suffixes
'''
"""

metab = pfile.map.raw_suppressed # typically (1,1,1,navg,ncoil,npts)
metab = np.transpose(metab, [0, 1, 2, 5, 4, 3]) # swap to (1,1,1,npts,ncoil,navg)
Expand All @@ -144,15 +148,15 @@ def _process_probe_p(pfile):


def _process_oslaser(pfile):
'''Extract metabolite and reference data from a oslaser format pfile
"""Extract metabolite and reference data from a oslaser format pfile

I think this is like the CMRR sLASER sequence with 2 ecc and 2 water scaling
scans at the start and end of each acquisition.

:param Pfile pfile: Pfile object
:return: List numpy data arrays
:return: List of file name suffixes
'''
"""

water = pfile.map.raw_unsuppressed # typically (1,1,1,navg,ncoil,npts)
metab = pfile.map.raw_suppressed
Expand All @@ -179,14 +183,14 @@ def _process_oslaser(pfile):


def _process_slaser(pfile):
'''Extract metabolite and reference data from a slaser format pfile
"""Extract metabolite and reference data from a slaser format pfile

This seems to be like a standard probe-p. Maybe slaser is the canonical vendor implementation.

:param Pfile pfile: Pfile object
:return: List numpy data arrays
:return: List of file name suffixes
'''
"""

metab = pfile.map.raw_suppressed # typically (1,1,1,navg,ncoil,npts)
metab = np.transpose(metab, [0, 1, 2, 5, 4, 3]) # swap to (1,1,1,npts,ncoil,navg)
Expand All @@ -203,12 +207,12 @@ def _process_slaser(pfile):


def _process_gaba(pfile):
'''Extract metabolite and reference data from a gaba (MPRESS) format pfile
"""Extract metabolite and reference data from a gaba (MPRESS) format pfile

:param Pfile pfile: Pfile object
:return: List numpy data arrays
:return: List of file name suffixes
'''
"""

# Note that custom mapper sorts dimensions already
metab = pfile.map.raw_suppressed
Expand All @@ -231,12 +235,12 @@ def _process_gaba(pfile):


def _process_mrsi_pfile(pfile):
'''Handle MRSI data
"""Handle MRSI data

:param Pfile pfile: Pfile object
:return: List of NIFTI MRS data objects
:return: List of file name suffixes
'''
"""
psd = pfile.hdr.rhi_psdname.decode('utf-8').lower()

known_formats = ('probe-p', 'probe-sl', 'slaser_cni', 'presscsi')
Expand Down Expand Up @@ -270,7 +274,7 @@ def fft_and_shift(x, axis):


def _calculate_affine_mrsi(pfile):
'''Calculate the 4x4 affine matrix for mrsi'''
"""Calculate the 4x4 affine matrix for mrsi"""

dcos = pfile.map.get_dcos.T
dcos[dcos == 0.0] = 0.0 # remove -0.0 values
Expand All @@ -292,7 +296,7 @@ def _calculate_affine_mrsi(pfile):


def _calculate_affine(pfile):
'''Calculate the 4x4 affine matrix'''
"""Calculate the 4x4 affine matrix"""

dcos = pfile.map.get_dcos.T
dcos[dcos == 0.0] = 0.0 # remove -0.0 values
Expand Down Expand Up @@ -321,7 +325,7 @@ def _populate_metadata(pfile, water_suppressed=True, data_dimensions=None):
:type pfile: pfile map object
:param water_suppressed: Set water suppression header field, defaults to True
:type water_suppressed: bool, optional
:param data_dimensions: If set to 5,6, or 7 will inlcude default dim tags for those diemnsions, defaults to None
:param data_dimensions: If set to 5,6, or 7 will include default dim tags for those dimensions, defaults to None
:type data_dimensions: int, optional
:return: Header extension object
:rtype: nifti_mrs.hdr_ext
Expand Down
Loading
Loading