Skip to content

Commit

Permalink
Rel/0.6.9 (#79)
Browse files Browse the repository at this point in the history
* Messy fixes for new nifti-mrs-tools version. Adds handling of GE sequences.

* Prepare for release.

* new pymapvbvd not yet available
  • Loading branch information
wtclarke authored Jul 7, 2023
1 parent ad0c9f8 commit 232189c
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 18 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
This document contains the Spec2nii release history in reverse chronological order.

0.6.9 (Friday 7th July 2023)
----------------------------
- Add handling for the `fidall` sequence in GE.
- Add handling for the `slaser` sequence in GE.
- Update to nifti-mrs-tools 1.0.0 API.
- Minor code fixes contributed by the community.

0.6.8 (Wednesday 22nd March 2023)
---------------------------------
- Added handling for the GE jpress sequence (for JH HURCULES sequence)
Expand Down
2 changes: 1 addition & 1 deletion requirements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ dependencies:
- scipy
- brukerapi>=0.1.8
- pandas
- nifti-mrs>=0.1.3
- nifti-mrs>=1.0.0
9 changes: 2 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python

from setuptools import setup
from setuptools import setup, find_packages
import versioneer
import yaml

Expand All @@ -23,12 +23,7 @@
url='https://github.com/wtclarke/spec2nii',
long_description=long_description,
long_description_content_type="text/markdown",
packages=['spec2nii',
'spec2nii.Siemens',
'spec2nii.GSL',
'spec2nii.dcm2niiOrientation',
'spec2nii.Philips',
'spec2nii.GE'],
packages=find_packages(),
install_requires=install_requires,
classifiers=[
"Programming Language :: Python :: 3",
Expand Down
62 changes: 55 additions & 7 deletions spec2nii/GE/ge_pfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ def _process_svs_pfile(pfile):
data, meta, dwelltime, fname_suffix = _process_probe_p(pfile)
elif psd in ('oslaser', 'slaser_cni'):
data, meta, dwelltime, fname_suffix = _process_oslaser(pfile)
elif psd in ('slaser'):
data, meta, dwelltime, fname_suffix = _process_slaser(pfile)
elif psd == 'gaba':
data, meta, dwelltime, fname_suffix = _process_gaba(pfile)
elif 'jpress_ac' in psd: # Bergen patch
Expand Down Expand Up @@ -132,6 +134,12 @@ def _process_probe_p(pfile):
meta = _populate_metadata(pfile, water_suppressed=True)
meta_ref = _populate_metadata(pfile, water_suppressed=False)

meta.set_dim_info(0, 'DIM_COIL')
meta.set_dim_info(1, 'DIM_DYN')

meta_ref.set_dim_info(0, 'DIM_COIL')
meta_ref.set_dim_info(1, 'DIM_DYN')

return [metab, water], [meta, meta_ref], dwelltime, ['', '_ref']


Expand All @@ -154,23 +162,46 @@ def _process_oslaser(pfile):
data = []
meta = []
fname_suffix = []

data.append(water[:, :, :, :, :, [0, 1, 4, 5]])
meta.append(_populate_metadata(pfile, water_suppressed=False))
meta.append(_populate_metadata(pfile, water_suppressed=False, data_dimensions=data[0].ndim))
fname_suffix.append('_quant')
data.append(water[:, :, :, :, :, [2, 3, 6, 7]])
meta.append(_populate_metadata(pfile, water_suppressed=False))
meta.append(_populate_metadata(pfile, water_suppressed=False, data_dimensions=data[1].ndim))
fname_suffix.append('_ecc')

data.append(metab)
meta.append(_populate_metadata(pfile, water_suppressed=True))
meta.append(_populate_metadata(pfile, water_suppressed=True, data_dimensions=metab.ndim))
fname_suffix.append('')

dwelltime = 1 / pfile.hdr.rhr_spectral_width

return data, meta, dwelltime, fname_suffix


def _process_slaser(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)

water = pfile.map.raw_unsuppressed # typically (1,1,1,navg,ncoil,npts)
water = np.transpose(water, [0, 1, 2, 5, 4, 3]) # swap to (1,1,1,npts,ncoil,navg)

dwelltime = 1 / pfile.hdr.rhr_spectral_width

meta = _populate_metadata(pfile, water_suppressed=True, data_dimensions=metab.ndim)
meta_ref = _populate_metadata(pfile, water_suppressed=False, data_dimensions=water.ndim)

return [metab, water], [meta, meta_ref], dwelltime, ['', '_ref']


def _process_gaba(pfile):
'''Extract metabolite and reference data from a gaba (MPRESS) format pfile
Expand Down Expand Up @@ -231,6 +262,8 @@ def fft_and_shift(x, axis):

dwelltime = 1 / pfile.hdr.rhr_spectral_width
meta = _populate_metadata(pfile)
meta.set_dim_info(0, 'DIM_COIL')

orientation = NIFTIOrient(_calculate_affine_mrsi(pfile))

return [gen_nifti_mrs_hdr_ext(data, dwelltime, meta, orientation.Q44, no_conj=True), ], ['', ]
Expand Down Expand Up @@ -277,8 +310,22 @@ def _calculate_affine(pfile):
return affine


def _populate_metadata(pfile, water_suppressed=True):
''' Populate a nifti-mrs header extension with the requisite information'''
def _populate_metadata(pfile, water_suppressed=True, data_dimensions=None):
"""Populate a nifti-mrs header extension with metadata from the pfile
If (up to 7) data_dimensions are specified then default dimension tags
(coil, dyn, indirect) will be included. Otherwise manually specify
outside this function.
:param pfile: pfile object
: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
:type data_dimensions: int, optional
:return: Header extension object
:rtype: nifti_mrs.hdr_ext
"""
hdr = pfile.hdr
spec_frequency = float(pfile.hdr.rhr_rh_ps_mps_freq) / 1e7

Expand All @@ -304,7 +351,8 @@ def _populate_metadata(pfile, water_suppressed=True):

meta = Hdr_Ext(
spec_frequency,
nucleus)
nucleus,
dimensions=data_dimensions)

# Standard defined metadata
# # 5.1 MRS specific Tags
Expand Down
5 changes: 4 additions & 1 deletion spec2nii/GE/ge_read_pfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def get_mapper(self):

if psd in ('probe-p', 'probe-s'):
mapper = PfileMapper
elif psd in ('oslaser', 'slaser_cni'):
elif psd in ('oslaser', 'slaser_cni', 'slaser'):
mapper = PfileMapperSlaser
elif psd == 'presscsi':
mapper = PfileMapper
Expand Down Expand Up @@ -208,6 +208,9 @@ def get_mapper(self):
elif psd == 'jpress':
# wtc - Added for HURCULES data.
mapper = PfileMapperGaba
elif psd == 'fidall':
# WTC - added for JG's Hyperpolarised 13C data
mapper = PfileMapper
else:
raise UnknownPfile("No Pfile mapper for pulse sequence = %s" % psd)

Expand Down
17 changes: 16 additions & 1 deletion spec2nii/Philips/philips_data_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def read_data_list_pair(data_file, list_file, aux_file, special_case=None):
if data_type == 'STD_0'\
and (special_case == 'hyper' or 'hyper' in meta['ProtocolName'].lower()):
out_hyper, meta_hyper = _special_case_hyper(out_data, meta)
# Handle the main acqusition of the HYPER (short TE + editing) sequence
# Handle the main acquisition of the HYPER (short TE + editing) sequence

# Insert spatial dimensions
out_shortte = out_hyper[0].reshape((1, 1, 1) + out_hyper[0].shape)
Expand All @@ -119,6 +119,10 @@ def read_data_list_pair(data_file, list_file, aux_file, special_case=None):
and (special_case == 'hyper' or 'hyper' in meta['ProtocolName'].lower()):
# Handle the water ref acqusition of the HYPER sequence

meta.set_dim_info(
0,
'DIM_COIL')

meta.set_dim_info(
1,
'DIM_USER_0',
Expand Down Expand Up @@ -285,6 +289,13 @@ def _special_case_hyper(data, meta):
meta_short_te = meta.copy()
meta_edited = meta.copy()

meta_short_te.set_dim_info(
0,
'DIM_COIL')
meta_short_te.set_dim_info(
1,
'DIM_DYN')

edit_pulse_1 = 1.9
edit_pulse_2 = 4.58
edit_pulse_off = 4.18
Expand All @@ -296,6 +307,10 @@ def _special_case_hyper(data, meta):
"C": {"PulseOffset": edit_pulse_1, "PulseDuration": 0.02},
"D": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02}}

meta_edited.set_dim_info(
0,
'DIM_COIL')

meta_edited.set_dim_info(
1,
'DIM_EDIT',
Expand Down
2 changes: 1 addition & 1 deletion spec2nii/other.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def insert_hdr_ext(args):
from nifti_mrs.hdr_ext import Hdr_Ext

nimg = Image(args.file)
hdr_ext = Hdr_Ext.from_header_ext(new_hdr, dimensions=nimg.ndim)
hdr_ext = Hdr_Ext.from_header_ext(new_hdr)
nifti_mrs_img = gen_nifti_mrs_hdr_ext(
nimg[:],
nimg.header['pixdim'][4],
Expand Down

0 comments on commit 232189c

Please sign in to comment.