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

BF: Correct Universal editing sequence HERMES conditions and tests for XA50 twix and RDA #130

Merged
merged 2 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ This document contains the Spec2nii release history in reverse chronological ord
-----------
- Siemens .rda format now had corrected and validated orientations (tested on VE11 baseline).
- Siemens .rda format now handles MRSI/CSI data and matches DICOM output. Validated on VE11 baseline data.
- Fixes in Siemens Twix special case for universal editing sequence (HERMES conditions).
- Added handling of custom Bruker sequences `mt_sLASER`, `mt_MEGA_sLASER_V35` and `cl_STELASER_PA360_b`.

0.7.2 (Thursday 7th December 2023)
----------------------------------
Expand Down
25 changes: 13 additions & 12 deletions spec2nii/Siemens/twix_special_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,24 @@ def mgs_svs_ed_twix(twixObj, reord_data, meta_obj, dim_tags):
"OFF": {"PulseOffset": edit_pulse_off, "PulseDuration": pulse_length}}
elif seq_mode == 1.0:
# HERMES GABA GSH (3 edit, 1 ctrl condition)
edit_cases = 3
edit_cases = 4
dim_info = "HERMES j-difference editing, GABA GSH, four conditions"
dim_header = {"EditCondition": ["A", "B", "C", "D"]}
edit_pulse_val = {
"A": {"PulseOffset": edit_pulse_1, "PulseDuration": 0.02},
"B": {"PulseOffset": None, "PulseDuration": None},
"C": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02},
"D": {"PulseOffset": [edit_pulse_1, edit_pulse_off], "PulseDuration": 0.02}}
"B": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02},
"C": {"PulseOffset": edit_pulse_2, "PulseDuration": 0.02},
"D": {"PulseOffset": [edit_pulse_1, edit_pulse_2], "PulseDuration": 0.02}}
elif seq_mode == 2.0:
# HERMES GABA GSH EtOH (3 edit, 1 ctrl condition)
edit_cases = 4
dim_info = "HERMES j-difference editing, GABA GSH EtOH, four conditions"
dim_header = {"EditCondition": ["A", "B", "C", "D"]}
edit_pulse_val = {
"A": {"PulseOffset": [edit_pulse_1, edit_pulse_2], "PulseDuration": 0.02},
"B": {"PulseOffset": [edit_pulse_3, edit_pulse_2], "PulseDuration": None},
"C": {"PulseOffset": [edit_pulse_1, edit_pulse_3], "PulseDuration": 0.02},
"D": {"PulseOffset": None, "PulseDuration": None}}
"A": {"PulseOffset": [edit_pulse_1, edit_pulse_3], "PulseDuration": 0.02},
"B": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02},
"C": {"PulseOffset": [edit_pulse_2, edit_pulse_3], "PulseDuration": 0.02},
"D": {"PulseOffset": [edit_pulse_1, edit_pulse_2], "PulseDuration": 0.02}}
elif seq_mode == 3.0:
# HERCULES (4 edit conditions)
edit_cases = 4
Expand All @@ -170,16 +170,17 @@ def mgs_svs_ed_twix(twixObj, reord_data, meta_obj, dim_tags):
"B": {"PulseOffset": [edit_pulse_off, edit_pulse_2], "PulseDuration": 0.02},
"C": {"PulseOffset": edit_pulse_1, "PulseDuration": 0.02},
"D": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02}}
elif seq_mode == 3.0:
elif seq_mode == 4.0:
# This is possibly only a condition for smm_svs_herc as not present in VE11c version.
# HERMES GABA LAC (3 edit 1 ctrl conditions)
edit_cases = 4
dim_info = "HERMES j-difference editing, GABA LAC, four conditions"
dim_header = {"EditCondition": ["A", "B", "C", "D"]}
edit_pulse_val = {
"A": {"PulseOffset": edit_pulse_1, "PulseDuration": 0.02},
"B": {"PulseOffset": None, "PulseDuration": None},
"C": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02},
"D": {"PulseOffset": [edit_pulse_1, edit_pulse_off], "PulseDuration": 0.02}}
"B": {"PulseOffset": edit_pulse_off, "PulseDuration": 0.02},
"C": {"PulseOffset": edit_pulse_2, "PulseDuration": 0.02},
"D": {"PulseOffset": [edit_pulse_1, edit_pulse_2], "PulseDuration": 0.02}}
else:
raise ValueError('Unknown sequence mode in mgs_svs_ed sequence.')

Expand Down
2 changes: 1 addition & 1 deletion tests/spec2nii_test_data
Submodule spec2nii_test_data updated from 9322e3 to d300d5
20 changes: 20 additions & 0 deletions tests/test_siemens_rda.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

xa20_svs_path = siemens_path / 'XAData' / 'XA20/rda/spct_002.MR.MRI-LAB Test_Dir.5.1.114540.rda'
xa31_locale_svs_path = siemens_path / 'XAData' / 'XA31/rda/locale_XA31.rda'
xa50_path = siemens_path / 'XAData' / 'XA50' / 'Phantom_20240129.MR.10.1.155227.rda'

latin1_encoding = siemens_path / 'rda' / 'latin1.rda'

Expand Down Expand Up @@ -147,6 +148,25 @@ def test_xa31_locale_svs(tmp_path):
assert np.iscomplexobj(img_t.dataobj)


def test_xa50_svs(tmp_path):

subprocess.run([
'spec2nii', 'rda',
'-f', 'xa50_svs',
'-o', tmp_path,
'-j', xa50_path])

img_t = read_nifti_mrs(tmp_path / 'xa50_svs.nii.gz')

hdr_ext_codes = img_t.header.extensions.get_codes()
hdr_ext = json.loads(img_t.header.extensions[hdr_ext_codes.index(44)].get_content())

hdr_ext['ResonantNucleus'] = ['1H', ]

assert img_t.shape == (1, 1, 1, 2048)
assert np.iscomplexobj(img_t.dataobj)


def test_latin_encoding(tmp_path):

subprocess.check_call(['spec2nii', 'rda',
Expand Down
22 changes: 22 additions & 0 deletions tests/test_twix.py → tests/test_siemens_twix.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# Special cased data
hercules_ve = siemens_path / 'HERCULES' / 'Siemens_TIEMO_HERC.dat'
hercules_xa30 = siemens_path / 'HERCULES' / 'meas_MID02595_FID60346_HERC.dat'
hermes_xa50 = siemens_path / 'XAData' / 'XA50' / 'smm_svs_herc_v2_hermes.dat'


def test_VB(tmp_path):
Expand Down Expand Up @@ -202,6 +203,27 @@ def test_XA_HERCULES(tmp_path):
assert hdr_ext['dim_7'] == 'DIM_DYN'


def test_XA_HERMES(tmp_path):

subprocess.check_call(['spec2nii', 'twix',
'-e', 'image',
'-f', 'hermes_xa',
'-o', tmp_path,
'-j', str(hermes_xa50)])

img_t = read_nifti_mrs(tmp_path / 'hermes_xa.nii.gz')

hdr_ext_codes = img_t.header.extensions.get_codes()
hdr_ext = json.loads(img_t.header.extensions[hdr_ext_codes.index(44)].get_content())

assert img_t.shape == (1, 1, 1, 4096, 42, 4, 80)
assert np.iscomplexobj(img_t.dataobj)

assert hdr_ext['dim_5'] == 'DIM_COIL'
assert hdr_ext['dim_6'] == 'DIM_EDIT'
assert hdr_ext['dim_7'] == 'DIM_DYN'


def test_twix_mrsi_orientation(tmp_path):
'''Test that the (empty) mrsi has information matching the twix equivalent.'''

Expand Down
Loading