Skip to content

Commit

Permalink
ENH: spec2nii insert handles non-compliant NIfTI files. (#70)
Browse files Browse the repository at this point in the history
* spec2nii insert handles non-compliant nifti files.

* Correct date in changelog.
  • Loading branch information
wtclarke authored Mar 15, 2023
1 parent a12538c commit acd024a
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
This document contains the Spec2nii release history in reverse chronological order.

0.6.7 (Wednesday 15th March 2023)
---------------------------------
- `spec2nii insert` can now insert a compliant header object into a non-compliant NIfTI file to generate a new NIfTI-MRS file.
- `spec2nii insert` can now update dwelltime using the optional `--dwelltime` argument.

0.6.6 (Thursday 9th March 2023)
-------------------------------
- Added in ability to generate empty NIfTI-MRS for twix-pathway MRSI scans.
Expand Down
26 changes: 22 additions & 4 deletions spec2nii/other.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import json
import pprint

from nifti_mrs.nifti_mrs import NIFTI_MRS
from nifti_mrs.nifti_mrs import NIFTI_MRS, NotNIFTI_MRS


def dump_headers(args):
Expand Down Expand Up @@ -52,13 +52,31 @@ def insert_hdr_ext(args):
:return: List of output names
:rtype: [str,]
"""
# Load data
nifti_mrs_img = NIFTI_MRS(args.file)

with open(args.json_file) as jf:
new_hdr = json.load(jf)

nifti_mrs_img.hdr_ext = new_hdr
# Load data
try:
nifti_mrs_img = NIFTI_MRS(args.file)
nifti_mrs_img.hdr_ext = new_hdr

except NotNIFTI_MRS:
print(f'{args.file} is not compliant with the NIfTI-MRS standard, attempting to convert')
from fsl.data.image import Image
from nifti_mrs.create_nmrs import gen_nifti_mrs_hdr_ext
from nifti_mrs.hdr_ext import Hdr_Ext

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

if args.dwelltime is not None:
nifti_mrs_img.dwelltime = args.dwelltime

# Process output name.
if args.fileout:
Expand Down
2 changes: 2 additions & 0 deletions spec2nii/spec2nii.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ def add_common_parameters(subparser):
parser_insert = subparsers.add_parser('insert', help='Insert json formatted file into existing NIfTI-MRS file.')
parser_insert.add_argument('file', help='NIFTI-MRS file', type=Path)
parser_insert.add_argument('json_file', help='JSON file to insert', type=Path)
parser_insert.add_argument("--dwelltime", type=float,
help="Specify a new dwelltime (1/bandwidth, pixdim[4]) value in seconds.")
parser_insert.add_argument("-f", "--fileout", type=str,
help="Output file base name (default = input file name)")
parser_insert.add_argument("-o", "--outdir", type=Path,
Expand Down
56 changes: 55 additions & 1 deletion tests/test_other_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
'''

import subprocess
from .io_for_tests import read_nifti_mrs
from pathlib import Path
import json

import numpy as np
from fsl.data.image import Image

from .io_for_tests import read_nifti_mrs

# Data paths
siemens_path = Path(__file__).parent / 'spec2nii_test_data' / 'Siemens'
data_path = siemens_path / 'VBData' / 'Twix' / 'meas_MID151_svs_se_C_T15_S10_10_FID108741.dat'
Expand Down Expand Up @@ -74,6 +78,7 @@ def test_insert(tmp_path):
subprocess.check_call(['spec2nii', 'insert',
'-o', tmp_path,
'-f', 'new',
'--dwelltime', '0.0005',
str(tmp_path / 'original.nii.gz'),
str(tmp_path / 'new.json')])

Expand All @@ -82,3 +87,52 @@ def test_insert(tmp_path):
hdr_ext = json.loads(img.header.extensions[hdr_ext_codes.index(44)].get_content())

assert extracted_hdr == hdr_ext
assert np.isclose(img.header['pixdim'][4], 0.0005)


def test_insert_not_nmrs(tmp_path):
# Create a small dummy file that doesn't conform to the standard

affine = np.random.random((4, 4))
affine[3, 0] = 0.0
affine[3, 1] = 0.0
affine[3, 2] = 0.0
affine[3, 3] = 1.0

data = np.ones((3, 3, 3, 256), dtype=float) + 1j * np.ones((3, 3, 3, 256), dtype=float)

non_compliant = Image(data, header=None, xform=affine)
non_compliant.header['pixdim'][4] = 1 / 1000
non_compliant.save(tmp_path / 'noncompliant.nii.gz')

newhd = {
'SpectrometerFrequency': 123.456789,
'ResonantNucleus': '1H'}

with open(tmp_path / 'new.json', 'w') as jf:
json.dump(newhd, jf)

subprocess.check_call(['spec2nii', 'insert',
'-o', tmp_path,
'-f', 'compliant',
str(tmp_path / 'noncompliant.nii.gz'),
str(tmp_path / 'new.json')])

img = read_nifti_mrs(tmp_path / 'compliant.nii.gz')
hdr_ext_codes = img.header.extensions.get_codes()
hdr_ext = json.loads(img.header.extensions[hdr_ext_codes.index(44)].get_content())

assert hdr_ext['ResonantNucleus'][0] == '1H'
assert img.header.get_intent()[2].split('_')[0] == 'mrs'
assert np.allclose(img.affine, affine)
assert np.isclose(img.header['pixdim'][4], 1 / 1000)

subprocess.check_call(['spec2nii', 'insert',
'-o', tmp_path,
'-f', 'compliant_dt',
'--dwelltime', '0.0005',
str(tmp_path / 'noncompliant.nii.gz'),
str(tmp_path / 'new.json')])

img = read_nifti_mrs(tmp_path / 'compliant_dt.nii.gz')
assert np.isclose(img.header['pixdim'][4], 0.0005)

0 comments on commit acd024a

Please sign in to comment.