Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
William Clarke
fsl_mrs
Commits
53cfec37
Commit
53cfec37
authored
Jul 22, 2021
by
William Clarke
Browse files
Add reshape nifti-mrs tool. Modify storage of dim tags in NIFTI_MRS class.
parent
731c7a45
Changes
5
Hide whitespace changes
Inline
Side-by-side
fsl_mrs/core/nifti_mrs.py
View file @
53cfec37
...
...
@@ -19,6 +19,24 @@ import fsl_mrs.core as core
from
fsl_mrs.utils.misc
import
checkCFUnits
dimension_tags
=
{
"DIM_COIL"
:
"For storage of data from each individual receiver coil element."
,
"DIM_DYN"
:
"For storage of each individual acquisition transient."
" E.g. for post-acquisition B0 drift correction."
,
"DIM_INDIRECT_0"
:
"The indirect detection dimension - necessary for 2D"
" (and greater) MRS acquisitions."
,
"DIM_INDIRECT_1"
:
"The indirect detection dimension - necessary for 2D"
" (and greater) MRS acquisitions."
,
"DIM_INDIRECT_2"
:
"The indirect detection dimension - necessary for 2D"
" (and greater) MRS acquisitions."
,
"DIM_PHASE_CYCLE"
:
"Used for the time-proportional phase incrementation method."
,
"DIM_EDIT"
:
"Used for edited MRS techniques such as MEGA or HERMES."
,
"DIM_MEAS"
:
"Used to indicate multiple repeats of the full sequence"
" contained within the same original data file."
,
"DIM_USER_0"
:
"User defined dimension."
,
"DIM_USER_1"
:
"User defined dimension."
,
"DIM_USER_2"
:
"User defined dimension."
}
def
gen_new_nifti_mrs
(
data
,
dwelltime
,
spec_freq
,
nucleus
=
'1H'
,
affine
=
None
,
dim_tags
=
[
None
,
None
,
None
]):
'''Generate a NIFTI_MRS object from a np array and header info.
...
...
@@ -107,20 +125,6 @@ class NIFTI_MRS(Image):
except
KeyError
:
raise
NotNIFTI_MRS
(
'NIFTI-MRS header extension must have nucleus and spectrometerFrequency keys.'
)
# Extract key parameters from the header extension
self
.
_set_dim_tags
()
def
_set_dim_tags
(
self
):
self
.
dim_tags
=
[
None
,
None
,
None
]
std_tags
=
[
'DIM_COIL'
,
'DIM_DYN'
,
'DIM_INDIRECT_0'
]
for
idx
in
range
(
3
):
curr_dim
=
idx
+
5
curr_tag
=
f
'dim_
{
curr_dim
}
'
if
curr_tag
in
self
.
hdr_ext
:
self
.
dim_tags
[
idx
]
=
self
.
hdr_ext
[
curr_tag
]
elif
curr_dim
<
self
.
ndim
:
self
.
dim_tags
[
idx
]
=
std_tags
[
idx
]
def
__getitem__
(
self
,
sliceobj
):
'''Apply conjugation at use. This swaps from the
NIFTI-MRS and Levvit inspired right-handed reference frame
...
...
@@ -179,7 +183,6 @@ class NIFTI_MRS(Image):
extension
=
Nifti1Extension
(
44
,
json_s
.
encode
(
'UTF-8'
))
self
.
header
.
extensions
.
clear
()
self
.
header
.
extensions
.
append
(
extension
)
self
.
_set_dim_tags
()
@
property
def
filename
(
self
):
...
...
@@ -190,6 +193,24 @@ class NIFTI_MRS(Image):
else
:
return
''
@
property
def
dim_tags
(
self
):
"""Return the three higher dimension tags"""
return
self
.
_read_dim_tags
()
def
_read_dim_tags
(
self
):
"""Read dim tags from current header extension"""
dim_tags
=
[
None
,
None
,
None
]
std_tags
=
[
'DIM_COIL'
,
'DIM_DYN'
,
'DIM_INDIRECT_0'
]
for
idx
in
range
(
3
):
curr_dim
=
idx
+
5
curr_tag
=
f
'dim_
{
curr_dim
}
'
if
curr_tag
in
self
.
hdr_ext
:
dim_tags
[
idx
]
=
self
.
hdr_ext
[
curr_tag
]
elif
curr_dim
<
self
.
ndim
:
dim_tags
[
idx
]
=
std_tags
[
idx
]
return
dim_tags
def
dim_position
(
self
,
dim_tag
):
'''Return position of dim if it exists.'''
if
dim_tag
in
self
.
dim_tags
:
...
...
@@ -237,6 +258,24 @@ class NIFTI_MRS(Image):
current_hdr_ext
.
pop
(
key
,
None
)
self
.
hdr_ext
=
current_hdr_ext
def
set_dim_tag
(
self
,
dim
,
tag
):
"""Set or update the 'dim_N' fields
Tag must be one of the standard-defined tags (e.g. DIM_DYN)
:param dim: The existing dim tag or python dimension index (i.e. N-1)
:type dim: str or int
:param tag: New tag
:type tag: str
"""
if
tag
not
in
dimension_tags
.
keys
():
raise
ValueError
(
f
'Tag must be one of:
{
", "
.
join
(
list
(
dimension_tags
.
keys
()))
}
.'
)
dim
=
self
.
_dim_tag_to_index
(
dim
)
current_hdr_ext
=
self
.
hdr_ext
current_hdr_ext
[
f
'dim_
{
dim
+
1
}
'
]
=
tag
self
.
hdr_ext
=
current_hdr_ext
def
set_dim_info
(
self
,
dim
,
info_str
):
"""Set or update the 'dim_N_info' field
...
...
@@ -309,8 +348,6 @@ class NIFTI_MRS(Image):
hdr_ext
[
f
'dim_
{
dd
}
_info'
]
=
hdr_ext
[
f
'dim_
{
dd
+
1
}
_info'
]
new_obj
.
hdr_ext
=
hdr_ext
new_obj
.
_set_dim_tags
()
return
new_obj
else
:
return
NIFTI_MRS
(
self
.
data
,
header
=
self
.
header
)
...
...
fsl_mrs/tests/test_core_nifti_mrs.py
View file @
53cfec37
...
...
@@ -177,6 +177,25 @@ def test_add_remove_field():
assert
'RepetitionTime'
not
in
nmrs
.
hdr_ext
def
test_set_dim_tag
():
nmrs
=
NIFTI_MRS
(
data
[
'unprocessed'
])
with
pytest
.
raises
(
ValueError
)
as
exc_info
:
nmrs
.
set_dim_tag
(
'DIM_DYN'
,
'DIM_FOO'
)
assert
exc_info
.
type
is
ValueError
assert
exc_info
.
value
.
args
[
0
]
==
\
'Tag must be one of: DIM_COIL, DIM_DYN, DIM_INDIRECT_0, DIM_INDIRECT_1, DIM_INDIRECT_2,'
\
' DIM_PHASE_CYCLE, DIM_EDIT, DIM_MEAS, DIM_USER_0, DIM_USER_1, DIM_USER_2.'
nmrs
.
set_dim_tag
(
'DIM_DYN'
,
'DIM_USER_0'
)
assert
nmrs
.
hdr_ext
[
'dim_6'
]
==
'DIM_USER_0'
assert
nmrs
.
dim_tags
==
[
'DIM_COIL'
,
'DIM_USER_0'
,
None
]
nmrs
.
set_dim_tag
(
4
,
'DIM_USER_1'
)
assert
nmrs
.
hdr_ext
[
'dim_5'
]
==
'DIM_USER_1'
assert
nmrs
.
dim_tags
==
[
'DIM_USER_1'
,
'DIM_USER_0'
,
None
]
def
test_set_dim_info
():
nmrs
=
NIFTI_MRS
(
data
[
'unprocessed'
])
nmrs
.
set_dim_info
(
'DIM_DYN'
,
'my info'
)
...
...
fsl_mrs/tests/test_utils_nifti_mrs_tools_reshape.py
0 → 100644
View file @
53cfec37
from
pathlib
import
Path
import
pytest
from
fsl_mrs.utils
import
mrs_io
from
fsl_mrs.utils
import
nifti_mrs_tools
as
nmrs_tools
testsPath
=
Path
(
__file__
).
parent
test_data
=
testsPath
/
'testdata'
/
'fsl_mrs_preproc'
/
'metab_raw.nii.gz'
def
test_reshape
():
# Data is (1, 1, 1, 4096, 32, 64) ['DIM_COIL', 'DIM_DYN', None]
nmrs
=
mrs_io
.
read_FID
(
test_data
)
new_shape
=
(
16
,
2
,
64
)
with
pytest
.
raises
(
TypeError
)
as
exc_info
:
reshaped
=
nmrs_tools
.
reshape
(
nmrs
,
new_shape
,
d6
=
'DIM_USER_0'
)
assert
exc_info
.
type
is
TypeError
assert
exc_info
.
value
.
args
[
0
]
==
'An appropriate d7 dim tag must be given as ndim = 7.'
reshaped
=
nmrs_tools
.
reshape
(
nmrs
,
new_shape
,
d6
=
'DIM_USER_0'
,
d7
=
'DIM_DYN'
)
assert
reshaped
.
data
.
shape
==
(
1
,
1
,
1
,
4096
,
16
,
2
,
64
)
assert
reshaped
.
dim_tags
==
[
'DIM_COIL'
,
'DIM_USER_0'
,
'DIM_DYN'
]
new_shape
=
(
16
,
-
1
,
64
)
reshaped
=
nmrs_tools
.
reshape
(
nmrs
,
new_shape
,
d7
=
'DIM_USER_0'
)
assert
reshaped
.
data
.
shape
==
(
1
,
1
,
1
,
4096
,
16
,
2
,
64
)
assert
reshaped
.
dim_tags
==
[
'DIM_COIL'
,
'DIM_DYN'
,
'DIM_USER_0'
]
fsl_mrs/utils/nifti_mrs_tools/__init__.py
View file @
53cfec37
from
.split_merge
import
split
,
merge
,
reorder
from
.reshape
import
reshape
fsl_mrs/utils/nifti_mrs_tools/reshape.py
0 → 100644
View file @
53cfec37
"""Tools for reshaping the higher dimensions of NIfTI-MRS
Author: Will Clarke <william.clarke@ndcn.ox.ac.uk>
Copyright (C) 2021 University of Oxford
"""
from
fsl_mrs.core
import
NIFTI_MRS
import
numpy
as
np
def
reshape
(
nmrs
,
reshape
,
d5
=
None
,
d6
=
None
,
d7
=
None
):
"""Reshape the higher dimensions (5-7) of an nifti-mrs file.
Uses numpy reshape syntax to reshape. Use -1 for automatic sizing.
If the dimension exists after reshaping a tag is required. If None is passed
but one already exists no change will be made. If no value exists then an
exception will be raised.
:param nmrs: Input NIfTI-MRS file
:type nmrs: NIFTI_MRS
:param reshape: Tuple of target sizes in style of numpy.reshape, higher dimensions only.
:type reshape: tuple
:param d5: Dimension tag to set dim_5, defaults to None
:type d5: str, optional
:param d6: Dimension tag to set dim_6, defaults to None
:type d6: str, optional
:param d7: Dimension tag to set dim_7, defaults to None
:type d7: str, optional
"""
shape
=
nmrs
.
data
.
shape
[
0
:
4
]
shape
+=
reshape
nmrs_reshaped
=
NIFTI_MRS
(
np
.
reshape
(
nmrs
.
data
,
shape
),
header
=
nmrs
.
header
)
# Note numerical index is N-1
if
d5
:
nmrs_reshaped
.
set_dim_tag
(
4
,
d5
)
elif
nmrs_reshaped
.
ndim
>
4
\
and
nmrs_reshaped
.
dim_tags
[
0
]
is
None
:
raise
TypeError
(
f
'An appropriate d5 dim tag must be given as ndim =
{
nmrs_reshaped
.
ndim
}
.'
)
if
d6
:
nmrs_reshaped
.
set_dim_tag
(
5
,
d6
)
elif
nmrs_reshaped
.
ndim
>
5
\
and
nmrs_reshaped
.
dim_tags
[
1
]
is
None
:
raise
TypeError
(
f
'An appropriate d6 dim tag must be given as ndim =
{
nmrs_reshaped
.
ndim
}
.'
)
if
d7
:
nmrs_reshaped
.
set_dim_tag
(
6
,
d7
)
elif
nmrs_reshaped
.
ndim
>
6
\
and
nmrs_reshaped
.
dim_tags
[
2
]
is
None
:
raise
TypeError
(
f
'An appropriate d7 dim tag must be given as ndim =
{
nmrs_reshaped
.
ndim
}
.'
)
return
nmrs_reshaped
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment