Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
fslpy
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Analyze
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
FSL
fslpy
Commits
db21c20b
Commit
db21c20b
authored
4 years ago
by
Michiel Cottaar
Browse files
Options
Downloads
Patches
Plain Diff
BUG: set undefined axes to None
parent
c95dacf1
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
fsl/data/cifti.py
+54
-34
54 additions, 34 deletions
fsl/data/cifti.py
with
54 additions
and
34 deletions
fsl/data/cifti.py
+
54
−
34
View file @
db21c20b
...
...
@@ -7,7 +7,7 @@ The data can be read from NIFTI, GIFTI, or CIFTI files.
Non-sparse volumetric or surface representations can be extracte.
"""
from
nibabel.cifti2
import
cifti2_axes
from
typing
import
Sequence
from
typing
import
Sequence
,
Optional
import
numpy
as
np
from
fsl.data.image
import
Image
import
nibabel
as
nib
...
...
@@ -40,45 +40,59 @@ class Cifti:
- :py:class:`BrainModelAxis <cifti2_axes.BrainModelAxis>`
- :py:class:`ParcelsAxis <cifti2_axes.ParcelsAxis>`
"""
def
__init__
(
self
,
arr
:
np
.
ndarray
,
axes
:
Sequence
[
cifti2_axes
.
Axis
]):
def
__init__
(
self
,
arr
:
np
.
ndarray
,
axes
:
Sequence
[
Optional
[
cifti2_axes
.
Axis
]
,
...]
):
"""
Defines a new dataset in greyordinate space
:param data: (..., N) array for N greyordinates or parcels
:param data: (..., N) array for N greyordinates or parcels
; can contain Nones for undefined axes
:param axes: sequence of CIFTI axes describing the data along each dimension
"""
self
.
arr
=
arr
axes
=
tuple
(
axes
)
while
self
.
arr
.
ndim
>
len
(
axes
):
axes
=
(
None
,
)
+
axes
self
.
axes
=
axes
if
arr
.
shape
[
-
len
(
axes
):]
!=
tuple
(
len
(
ax
)
for
ax
in
axes
):
raise
ValueError
(
f
"
Shape of axes
{
tuple
(
len
(
ax
)
for
ax
in
axes
)
}
does not match shape of array
{
self
.
arr
.
shape
}
"
)
if
not
all
(
ax
is
None
or
len
(
ax
)
==
sz
for
ax
,
sz
in
zip
(
axes
,
self
.
arr
.
shape
)):
raise
ValueError
(
f
"
Shape of axes
{
tuple
(
-
1
if
ax
is
None
else
len
(
ax
)
for
ax
in
axes
)
}
does not
"
f
"
match shape of array
{
self
.
arr
.
shape
}
"
)
def
to_cifti
(
self
,
other
_ax
e
s
=
None
):
def
to_cifti
(
self
,
default
_ax
i
s
=
None
):
"""
Create a CIFTI image from the data
:param other_axes: overwrites the :mod:`cifti2_axes` to be used to write to create the CIFTI image
:param default_axis: What to use as an axis along any undefined dimensions
- By default an error is raised
- if set to
"
scalar
"
a ScalarAxis is used with names of
"
default {index}
"
- if set to
"
series
"
a SeriesAxis is used
:return: nibabel CIFTI image
"""
if
other_axes
is
None
:
if
len
(
self
.
axes
)
!=
self
.
data
.
ndim
:
raise
ValueError
(
"
Can not store to CIFTI without defining what is stored along the other dimensions
"
)
other_axes
=
self
.
axes
[:
-
1
]
if
any
(
ax
is
None
for
ax
in
self
.
axes
):
if
default_axis
is
None
:
raise
ValueError
(
"
Can not store to CIFTI without defining what is stored along each dimension
"
)
elif
default_axis
==
'
scalar
'
:
def
get_axis
(
n
:
int
):
return
cifti2_axes
.
ScalarAxis
([
f
'
default
{
idx
+
1
}
'
for
idx
in
range
(
n
)])
elif
default_axis
==
'
series
'
:
def
get_axis
(
n
:
int
):
return
cifti2_axes
.
SeriesAxis
(
0
,
1
,
n
)
else
:
raise
ValueError
(
f
"
default_axis should be set to None,
'
scalar
'
, or
'
series
'
, not
{
default_axis
}
"
)
new_axes
=
[
get_axis
(
sz
)
if
ax
is
None
else
ax
for
ax
,
sz
in
zip
(
self
.
axes
,
self
.
arr
.
shape
)
]
else
:
if
len
(
other_axes
)
!=
self
.
data
.
ndim
-
1
:
raise
ValueError
(
"
Number of axis does not match dimensionality of the data
"
)
if
tuple
(
len
(
ax
)
for
ax
in
other_axes
)
!=
self
.
data
.
shape
[:
-
1
]:
raise
ValueError
(
"
Size of other axes does not match data size
"
)
new_axes
=
list
(
self
.
axes
)
data
=
self
.
data
if
data
.
ndim
==
1
:
# CIFTI axes are always at least 2D
data
=
data
[
None
,
:]
other_axes
=
[
cifti2_axes
.
ScalarAxis
([
'
default
'
])
]
new_axes
.
insert
(
0
,
cifti2_axes
.
ScalarAxis
([
'
default
'
])
)
return
cifti2_axes
.
Cifti2Image
(
data
,
header
=
list
(
other_axes
)
+
[
self
.
axes
[
-
1
]]
)
return
cifti2_axes
.
Cifti2Image
(
data
,
header
=
new_axes
)
@classmethod
def
from_cifti
(
cls
,
filename
,
writable
=
False
):
...
...
@@ -110,25 +124,30 @@ class Cifti:
return
ParcelCifti
(
data
,
axes
)
raise
ValueError
(
"
Last axis of CIFTI object should be a BrainModelAxis or ParcelsAxis
"
)
def
writ
e
(
self
,
cifti_filename
,
other
_ax
e
s
=
None
):
def
sav
e
(
self
,
cifti_filename
,
default
_ax
i
s
=
None
):
"""
Writes this sparse representation to/from a filename
:param cifti_filename: output filename
:param other_axes: overwrites the :mod:`cifti2_axes` to be used to write to the file
:param default_axis: What to use as an axis along any undefined dimensions
- By default an error is raised
- if set to
"
scalar
"
a ScalarAxis is used with names of
"
default {index}
"
- if set to
"
series
"
a SeriesAxis is used
:return:
"""
self
.
to_cifti
(
other
_ax
e
s
).
to_filename
(
addExt
(
cifti_filename
,
defaultExt
=
self
.
extension
))
self
.
to_cifti
(
default
_ax
i
s
).
to_filename
(
addExt
(
cifti_filename
,
defaultExt
=
self
.
extension
))
def
read
(
cls
,
filename
,
mask_values
=
(
0
,
np
.
nan
),
writable
=
False
):
@classmethod
def
load
(
cls
,
filename
,
mask_values
=
(
0
,
np
.
nan
),
writable
=
False
):
"""
Reads greyordinate data from the given file
File can be:
- NIFTI mask
- GIFTI mask
- CIFTI file
- NIFTI mask
- GIFTI mask
- CIFTI file
:param filename: input filename
:param mask_values: which values are outside of the mask for NIFTI or GIFTI input
...
...
@@ -140,17 +159,19 @@ class Cifti:
else
:
img
=
filename
if
isinstance
(
img
,
nib
.
Nifti1Image
):
if
writable
:
raise
ValueError
(
"
Can not open NIFTI file in writable mode
"
)
return
cls
.
from_image
(
Image
(
img
),
mask_values
)
if
isinstance
(
img
,
nib
.
Cifti2Image
):
return
cls
.
from_cifti
(
img
,
writable
=
writable
)
if
isinstance
(
img
,
nib
.
GiftiImage
):
if
writable
:
raise
ValueError
(
"
Can not open GIFTI file in writable mode
"
)
return
cls
.
from_gifti
(
img
,
mask_values
)
raise
ValueError
(
f
"
I do not know how to convert
{
type
(
img
)
}
into greyordinates (from
{
filename
}
)
"
)
try
:
vol_img
=
Image
(
img
)
except
ValueError
:
raise
ValueError
(
f
"
I do not know how to convert
{
type
(
img
)
}
into greyordinates (from
{
filename
}
)
"
)
if
writable
:
raise
ValueError
(
"
Can not open NIFTI file in writable mode
"
)
return
cls
.
from_image
(
vol_img
,
mask_values
)
@classmethod
def
from_gifti
(
cls
,
filename
,
mask_values
=
(
0
,
np
.
nan
)):
...
...
@@ -191,7 +212,7 @@ class Cifti:
"""
Creates a new greyordinate object from a NIFTI file
:param
filename: NIFTI filename or
Image object
:param
image: FSL :class:`image.
Image
`
object
:param mask_values: which values to mask out
:return: greyordinate object representing the unmasked voxels
"""
...
...
@@ -211,7 +232,6 @@ class Cifti:
return
cifti2_axes
.
GreyOrdinates
(
inverted_data
,
[
bm_axes
])
class
DenseCifti
(
Cifti
):
"""
Represents sparse data defined for a subset of voxels and vertices (i.e., greyordinates)
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment