From 8ba03ebddc0b10561464dbdfa9f4f8a83b39756c Mon Sep 17 00:00:00 2001 From: wtclarke Date: Sat, 2 Mar 2024 13:33:00 +0000 Subject: [PATCH] Validated svs rda orientations. --- CHANGELOG.md | 5 +++++ spec2nii/Siemens/rda.py | 33 +++++++++++++++-------------- tests/spec2nii_test_data | 2 +- tests/test_siemens_rda.py | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b269505..23edd76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ This document contains the Spec2nii release history in reverse chronological order. +0.7.3 (WIP) +----------- +- Siemens .rda format now had corrected and validated orientations (tested on VE11 baseline). +- + 0.7.2 (Thursday 7th December 2023) ---------------------------------- - SpectralWidth now added to header extension automatically to match bids specification. diff --git a/spec2nii/Siemens/rda.py b/spec2nii/Siemens/rda.py index 294948f..11ed44d 100644 --- a/spec2nii/Siemens/rda.py +++ b/spec2nii/Siemens/rda.py @@ -91,9 +91,9 @@ def convert_rda(rda_path, fname_out, verbose): ' Please contribute test data if you can!') imagePositionPatient = np.asarray([ - _locale_float(hdr['PositionVector[0]']), - _locale_float(hdr['PositionVector[1]']), - _locale_float(hdr['PositionVector[2]'])]) + _locale_float(hdr['VOIPositionSag']), + _locale_float(hdr['VOIPositionCor']), + _locale_float(hdr['VOIPositionTra'])]) imageOrientationPatient = np.asarray([ [_locale_float(hdr['RowVector[0]']), _locale_float(hdr['ColumnVector[0]'])], @@ -137,13 +137,14 @@ def extractRdaMetadata(hdr): hdr['Nucleus']) # Standard defined metadata - def set_standard_def(nifti_mrs_key, location, key, cast=None): - if key in location\ - and location[key] is not None: + def set_standard_def(nifti_mrs_key, key, cast=None): + if key in hdr\ + and hdr[key] is not None\ + and hdr[key]: if cast is not None: - obj.set_standard_def(nifti_mrs_key, cast(location[key])) + obj.set_standard_def(nifti_mrs_key, cast(hdr[key])) else: - obj.set_standard_def(nifti_mrs_key, location[key]) + obj.set_standard_def(nifti_mrs_key, hdr[key]) # # 5.1 MRS specific Tags # 'EchoTime' @@ -180,22 +181,22 @@ def set_standard_def(nifti_mrs_key, location, key, cast=None): # # 5.3 Sequence information # 'SequenceName' - obj.set_standard_def('SequenceName', hdr['SequenceName']) + set_standard_def('SequenceName', 'SequenceName') # 'ProtocolName' - obj.set_standard_def('ProtocolName', hdr['ProtocolName']) + set_standard_def('ProtocolName', 'ProtocolName') # # 5.4 Sequence information # 'PatientPosition' - obj.set_standard_def('PatientPosition', hdr['PatientPosition']) + set_standard_def('PatientPosition', 'PatientPosition') # 'PatientName' - obj.set_standard_def('PatientName', hdr['PatientName']) + set_standard_def('PatientName', 'PatientName') # 'PatientID' - obj.set_standard_def('PatientID', hdr['PatientID']) + set_standard_def('PatientID', 'PatientID') # 'PatientWeight' - obj.set_standard_def('PatientWeight', _locale_float(hdr['PatientWeight'])) + set_standard_def('PatientWeight', 'PatientWeight', cast=_locale_float) # 'PatientDoB' - obj.set_standard_def('PatientDoB', hdr['PatientBirthDate']) + set_standard_def('PatientDoB', 'PatientBirthDate') # 'PatientSex' - obj.set_standard_def('PatientSex', hdr['PatientSex']) + set_standard_def('PatientSex', 'PatientSex') # # 5.5 Provenance and conversion metadata obj.set_standard_def('ConversionMethod', f'spec2nii v{spec2nii_ver}') diff --git a/tests/spec2nii_test_data b/tests/spec2nii_test_data index ca9850c..9322e3d 160000 --- a/tests/spec2nii_test_data +++ b/tests/spec2nii_test_data @@ -1 +1 @@ -Subproject commit ca9850cac4f64b4a1f402d58c73a665492f68b9a +Subproject commit 9322e3de8b109016af010ce6051c02ee578cfb5d diff --git a/tests/test_siemens_rda.py b/tests/test_siemens_rda.py index b8f2be8..648e5d1 100644 --- a/tests/test_siemens_rda.py +++ b/tests/test_siemens_rda.py @@ -20,6 +20,50 @@ latin1_encoding = siemens_path / 'rda' / 'latin1.rda' +ve_path = siemens_path / 'VEData' / 'rda' +paired_ve_data = { + 'svs_se_c_t15_s10_r10': 'svs_se_c>t15>s10_R10_12_1', + 'svs_se_iso_tra_sat': 'svs_se_iso_tra_sat_13_1', + 'svs_se_s_c10_t5_r10': 'svs_se_s>c10>t5_R10_11_1', + 'svs_se_t_c15_s10_r10': 'svs_se_t>c15>s10_R10_10_1'} + + +def test_ve_against_dicom(tmp_path): + for rda_file in ve_path.glob("svs_se*.rda"): + print(rda_file.stem) + dcm_dir = rda_file.parent.parent / 'DICOM' / paired_ve_data[rda_file.stem] + + subprocess.run([ + 'spec2nii', 'rda', + '-f', rda_file.stem + '_rda', + '-o', tmp_path, + '-j', rda_file]) + + subprocess.run([ + 'spec2nii', 'dicom', + '-f', rda_file.stem + '_dcm', + '-o', tmp_path, + '-j', dcm_dir]) + + rda_out = tmp_path / (rda_file.stem + '_rda.nii.gz') + dcm_out = tmp_path / (rda_file.stem + '_dcm.nii.gz') + assert rda_out.exists() + assert dcm_out.exists() + + dcm = read_nifti_mrs(dcm_out) + rda = read_nifti_mrs(rda_out) + assert dcm.shape == rda.shape + assert np.allclose(dcm.get_fdata(dtype=complex), rda.get_fdata(dtype=complex)) + assert np.allclose(dcm.get_sform(), rda.get_sform()) + + hdr_ext_codes = dcm.header.extensions.get_codes() + hdr_ext_dcm = json.loads(dcm.header.extensions[hdr_ext_codes.index(44)].get_content()) + hdr_ext_codes = rda.header.extensions.get_codes() + hdr_ext_rda = json.loads(rda.header.extensions[hdr_ext_codes.index(44)].get_content()) + + for key in ['SpectrometerFrequency', 'ResonantNucleus', 'EchoTime', 'RepetitionTime']: + assert hdr_ext_dcm[key] == hdr_ext_rda[key] + def test_xa20_svs(tmp_path):