wtclarke / nifti_mrs_tools

Software tools for the NIfTI-MRS data format
BSD 3-Clause "New" or "Revised" License
0 stars 4 forks source link

FSL MRS issue traced back to ValueError in nifti_mrs #21

Open JohnLaMaster opened 8 months ago

JohnLaMaster commented 8 months ago

I am trying to use FSL MRS interactively and I've run into the following error:

Cell In[29], line 2
      1 # %%
----> 2 data = mrs_io.read_FID(path)
      3 mrs = data.mrs(basis_file=fsl_basis_file_path)                     
      4             #    ref_data=final_wref)

File ~/Documents/Repositories/fsl_mrs/fsl_mrs/utils/mrs_io/main.py:91, in read_FID(filename)
     88     filename = Path(filename).resolve()
     90 try:
---> 91     return fsl_nmrs.NIFTI_MRS(filename)
     92 except (NotNIFTI_MRS, fslpath.PathError):
     93     data_type, id_ext = _check_datatype(Path(filename))

File ~/Documents/Repositories/fsl_mrs/fsl_mrs/core/nifti_mrs.py:100, in NIFTI_MRS.__init__(self, *args, **kwargs)
     54 def __init__(self, *args, **kwargs):
     55     """Create a NIFTI_MRS object with the given image data or file name.
     56 
     57     This is a wrapper around the nifti_mrs.nifti_mrs.NIFTI_MRS class to
   (...)
     98     (if it is called).
     99     """
--> 100     super().__init__(*args, **kwargs)
...
    105         else:
--> 106             raise ValueError(f'User-defined key {key} must contain a "Description" field"')
    108 return obj

ValueError: User-defined key conversion_time must contain a "Description" field"

This happens with every input file, but we used FSL MRS to fit all of this data this summer, so I know it worked a few months ago.

I went through the data file using the following code:

img = nib.load(path)
data = img.get_fdata(dtype=np.complex64)
header = img.header
hdr_ext_codes = img.header.extensions.get_codes()
mrs_hdr_ext = json.loads(img.header.extensions[hdr_ext_codes.index(44)].get_content())
print(mrs_hdr_ext)

And got the following output showing that 'conversion_time' does in fact have a filled in value pair.

{'SpectrometerFrequency': [123.2],
 'ResonantNucleus': ['1H'],
 'EchoTime': 0.144,
 'RepetitionTime': 2.0,
 'DeviceSerialNumber': 'NA',
 'Manufacturer': 'simulated',
 'ManufacturersModelName': 'NA',
 'SoftwareVersions': 'NA',
 'PatientDoB': 'NA',
 'PatientName': 'NA',
 'PatientPosition': 'HFS',
 'PatientSex': 'NA',
 'PatientWeight': 0,
 'ConversionMethod': 'Manual',
 'ConversionTime': '2023-04-22T15:58:10.128',
 'OriginalFile': ['denoiser_physics_model_512_dataset.mat'],
 'conversion_time': '2023-06-02T09:04:55.836'}

Do you have any suggestions for what could be causing this and how to fix it or work around it?

Best, John

wtclarke commented 8 months ago

Hi John,

This will be an effect of me making sure that the NIfTI-MRS class validation tools abide strictly by the NIfTI-MRS standard, and then making the more recent versions of FSL-MRS require a more recent version of the NIfTI-MRS tools package (with the stricter validation). The requirement for a description was always in the standard for any user-defined header values, because we allow anything to be put in that doesn't collide with those defined in the standard, and therefore it's useful to know what the header values mean. On the flip side, if you form the files using these tools it should always be compliant.

In this particular case it looks like there are two values of conversion time:

'ConversionTime': '2023-04-22T15:58:10.128',
'conversion_time': '2023-06-02T09:04:55.836'

The first, ConversionTime is in the format expected by the standard, and so doesn't need a description. The second is a user-defined value and as such requires a description subfield. I.e. it would need to be 'conversion_time': {'Value': '2023-06-02T09:04:55.836', 'Description' : 'my special conversion time parameter'}.

In this instance you can probably just blow away that second conversion time and then it will be compliant.

wtclarke commented 8 months ago

You can do this by loading the file using the NIFTI_MRS class directly and setting the argument validate_on_creation to False, and then modifying the header.

Something like

from fsl_mrs.core.nifti_mrs import NIFTI_MRS
non_compliant_file = NIFTI_MRS('path/to/data.nii', validate_on_creation=False)
non_compliant_file.remove_hdr_field('conversion_time')
non_compliant_file.save('path/to/compliant_file.nii')
JohnLaMaster commented 8 months ago

Hi Will, thank you very much! It seems I will need to update my notebook for converting my simulations to NIfTI-MRS. I'll give your suggestion a try.

Thanks again!