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
Package Registry
Model registry
Operate
Environments
Terraform modules
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
Evan Edmond
fslpy
Commits
0f60a744
Commit
0f60a744
authored
5 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
ENH: Fleshed out x5 implementation
parent
d3cf8335
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
fsl/transform/x5.py
+246
-101
246 additions, 101 deletions
fsl/transform/x5.py
with
246 additions
and
101 deletions
fsl/transform/x5.py
+
246
−
101
View file @
0f60a744
...
@@ -344,11 +344,89 @@ X5_FORMAT = 'X5'
...
@@ -344,11 +344,89 @@ X5_FORMAT = 'X5'
X5_VERSION
=
'
0.0.1
'
X5_VERSION
=
'
0.0.1
'
class
InvalidFile
Error
(
Exception
):
class
X5
Error
(
Exception
):
"""
Error raised if an invalid/incompatible file is detected.
"""
"""
Error raised if an invalid/incompatible file is detected.
"""
pass
pass
def
readLinearX5
(
fname
):
"""
Read a linear X5 transformation file from ``fname``.
:arg fname: File name to read from
:returns: A tuple containing:
- A ``(4, 4)`` ``numpy`` array containing the affine
transformation
- A :class:`.Nifti` instance representing the source space
- A :class:`.Nifti` instance representing the reference space
"""
with
h5py
.
File
(
fname
,
'
r
'
)
as
f
:
_validateMetadata
(
f
[
'
/
'
])
xform
=
_readAffine
(
f
[
'
/
'
])
src
=
_readSpace
(
f
[
'
/From
'
])
ref
=
_readSpace
(
f
[
'
/To
'
])
return
xform
,
src
,
ref
def
writeLinearX5
(
fname
,
xform
,
src
,
ref
):
"""
Write a linear transformation to the specified file.
:arg fname: File name to write to
:arg xform: ``(4, 4)`` ``numpy`` array containing the affine transformation
:arg src: :class:`.Nifti` image representing the source space
:arg ref: :class:`.Nifti` image representing the reference
"""
with
h5py
.
File
(
fname
,
'
w
'
)
as
f
:
_writeMetadata
(
f
)
_writeAffine
(
f
,
xform
)
from_
=
f
.
create_group
(
'
/From
'
)
to
=
f
.
create_group
(
'
/To
'
)
_writeSpace
(
from_
,
src
)
_writeSpace
(
to
,
ref
)
def
readNonLinearX5
(
fname
):
"""
Read a nonlinear X5 transformation file from ``fname``.
:arg fname: File name to read from
:returns: A :class:`.DisplacementField` or :class:`.CoefficientField`
"""
with
h5py
.
File
(
fname
,
'
r
'
)
as
f
:
root
=
f
[
'
/
'
]
_validateNonLinearTransform
(
root
)
if
root
[
'
SubType
'
]
==
'
displacement
'
:
field
=
_readDisplacementField
(
root
)
elif
root
[
'
SubType
'
]
==
'
coefficient
'
:
field
=
_readCoefficientField
(
root
)
return
field
def
writeNonLinearX5
(
fname
,
field
):
"""
Write a nonlinear X5 transformation file to ``fname``.
:arg fname: File name to write to
:arg field: A :class:`.DisplacementField` or :class:`.CoefficientField`
"""
with
h5py
.
File
(
fname
,
'
w
'
)
as
f
:
_writeMetadata
(
f
)
f
.
attrs
[
'
Type
'
]
=
'
nonlinear
'
if
isinstance
(
field
,
nonlinear
.
DisplacementField
):
_writeDisplacementField
(
f
,
field
)
elif
isinstance
(
field
,
nonlinear
.
CoefficientField
):
_writeCoefficientField
(
f
,
field
)
def
_writeMetadata
(
group
):
def
_writeMetadata
(
group
):
"""
Writes a metadata block into the given group.
"""
Writes a metadata block into the given group.
...
@@ -360,8 +438,10 @@ def _writeMetadata(group):
...
@@ -360,8 +438,10 @@ def _writeMetadata(group):
def
_validateMetadata
(
group
):
def
_validateMetadata
(
group
):
"""
Reads a metadata block from the given group, and raises an
"""
Reads a metadata block from the given group, and raises a :exc:`X5Error`
:exc:`InvalidFileError` if it does not look valid.
if it does not look valid.
:arg group: A ``h5py.Group`` object.
"""
"""
try
:
try
:
...
@@ -369,30 +449,30 @@ def _validateMetadata(group):
...
@@ -369,30 +449,30 @@ def _validateMetadata(group):
version
=
group
.
attrs
[
'
Version
'
]
version
=
group
.
attrs
[
'
Version
'
]
except
Exception
:
except
Exception
:
raise
InvalidFile
Error
(
'
File does not appear to be an X5 file
'
)
raise
X5
Error
(
'
File does not appear to be an X5 file
'
)
if
(
format
!=
X5_FORMAT
)
or
(
version
!=
X5_VERSION
):
if
(
format
!=
X5_FORMAT
)
or
(
version
!=
X5_VERSION
):
raise
InvalidFile
Error
(
'
Incompatible format/version (required: {}/{},
'
raise
X5
Error
(
'
Incompatible format/version (required: {}/{},
'
'
present: {}/{})
'
.
format
(
X5_FORMAT
,
X5_VERSION
,
'
present: {}/{})
'
.
format
(
X5_FORMAT
,
X5_VERSION
,
format
,
version
))
format
,
version
))
def
_readAffine
(
group
):
def
_readAffine
(
group
):
"""
Reads an affine from the given group
,
"""
Reads an affine from the given group
.
:arg group: A ``h5py.Group`` object.
:arg group: A ``h5py.Group`` object.
:returns: ``numpy`` array containing a ``(4, 4)`` affine transformation.
:returns: ``numpy`` array containing a ``(4, 4)`` affine transformation.
"""
"""
if
group
.
attrs
[
'
Type
'
]
!=
'
linear
'
:
if
group
.
attrs
[
'
Type
'
]
!=
'
linear
'
:
raise
Value
Error
(
'
Not a linear transform
'
)
raise
X5
Error
(
'
Not a linear transform
'
)
xform
=
np
.
array
(
group
[
'
Transform
'
]
)
xform
=
group
[
'
Transform
'
]
if
xform
.
shape
!=
(
4
,
4
):
if
xform
.
shape
!=
(
4
,
4
):
raise
Value
Error
(
'
Not a linear transform
'
)
raise
X5
Error
(
'
Not a linear transform
'
)
return
xform
return
np
.
array
(
xform
)
def
_writeAffine
(
group
,
xform
):
def
_writeAffine
(
group
,
xform
):
...
@@ -421,7 +501,7 @@ def _readSpace(group):
...
@@ -421,7 +501,7 @@ def _readSpace(group):
import
fsl.data.image
as
fslimage
import
fsl.data.image
as
fslimage
if
group
.
attrs
[
'
Type
'
]
!=
'
image
'
:
if
group
.
attrs
[
'
Type
'
]
!=
'
image
'
:
raise
Value
Error
(
'
Not an image
mapping
'
)
raise
X5
Error
(
'
Not an image
space
'
)
shape
=
group
.
attrs
[
'
Size
'
]
shape
=
group
.
attrs
[
'
Size
'
]
pixdim
=
group
.
attrs
[
'
Scales
'
]
pixdim
=
group
.
attrs
[
'
Scales
'
]
...
@@ -448,130 +528,195 @@ def _writeSpace(group, img):
...
@@ -448,130 +528,195 @@ def _writeSpace(group, img):
_writeAffine
(
mapping
,
img
.
getAffine
(
'
voxel
'
,
'
world
'
))
_writeAffine
(
mapping
,
img
.
getAffine
(
'
voxel
'
,
'
world
'
))
def
_readDisplacementField
(
group
):
def
_validateNonLinearTransform
(
group
):
"""
"""
Checks that the attributes of the given group, assumed to contain a
nonlinear transform, are valid. Raises a :exc:`X5Error` if not.
:arg group: A ``h5py.Group`` object.
"""
"""
field
=
np
.
array
(
group
[
'
Transform
'
])
type
=
group
.
attrs
[
'
Type
'
]
dispType
=
group
.
attrs
[
'
Representation
'
]
subtype
=
group
.
attrs
[
'
SubType
'
]
src
=
_readSpace
(
group
[
'
From
'
])
repr
=
group
.
attrs
[
'
Representation
'
]
ref
=
_readSpace
(
group
[
'
To
'
])
pre
=
_readAffine
(
group
[
'
Pre
'
])
post
=
_readAffine
(
group
[
'
Post
'
])
srcToRef
=
_readAffine
(
group
[
'
InitialAlignment
'
])
refSpace
=
affine
.
identify
(
ref
,
pre
,
from_
=
'
world
'
)[
1
]
if
type
!=
'
nonlinear
'
:
srcSpace
=
affine
.
identify
(
src
,
post
,
to
=
'
world
'
)[
0
]
raise
X5Error
(
'
Not a nonlinear transform
'
)
return
nonlinear
.
DisplacementField
(
field
,
if
subtype
not
in
(
'
displacement
'
,
'
coefficient
'
):
src
,
raise
X5Error
(
'
Unrecognised nonlinear subtype: {}
'
.
format
(
subtype
))
ref
,
srcSpace
=
srcSpace
,
refSpace
=
refSpace
,
srcToRefMat
=
srcToRef
,
dispType
=
dispType
)
if
(
subtype
==
'
displacement
'
)
and
\
(
repr
not
in
(
'
absolute
'
,
'
relative
'
)):
raise
X5Error
(
'
Unrecognised nonlinear representation:
'
'
{}
'
.
format
(
repr
))
def
_readCoefficientField
(
group
):
if
(
subtype
==
'
coefficient
'
)
and
\
"""
(
repr
not
in
(
'
quadratic bspline
'
,
'
cubic bspline
'
)):
raise
X5Error
(
'
Unrecognised nonlinear representation:
'
'
{}
'
.
format
(
repr
))
def
_readNonLinearCommon
(
group
):
"""
Reads the spaces and affines from the given group, assumed to contain a
nonlinear transform.
:arg group: A ``h5py.Group`` object.
:returns: A tuple containing:
- A :class:`.Nifti` representing the source
- A :class:`.Nifti` representing the reference
- A ``(4, 4)`` ``numpy`` array containing the pre affine, or
``None`` if there is not one.
- A ``(4, 4)`` ``numpy`` array containing the post affine, or
``None`` if there is not one.
- A ``(4, 4)`` ``numpy`` array containing the initial
alignment affine, or ``None`` if there is not one.
- A string describing the source space - see
:meth:`.Nifti.getAffine`
- A string describing the reference space - see
:meth:`.Nifti.getAffine`
"""
"""
field
=
np
.
array
(
group
[
'
Transform
'
])
fieldType
=
group
.
attrs
[
'
Representation
'
]
src
=
_readSpace
(
group
[
'
From
'
])
ref
=
_readSpace
(
group
[
'
To
'
])
pre
=
_readAffine
(
group
[
'
Pre
'
])
post
=
_readAffine
(
group
[
'
Post
'
])
srcToRef
=
_readAffine
(
group
[
'
InitialAlignment
'
])
spacing
=
group
[
'
Parameters
'
].
attrs
[
'
Spacing
'
]
src
=
_readSpace
(
group
[
'
From
'
])
refToField
=
_readAffine
(
group
[
'
Parameters
'
][
'
ReferenceToField
'
])
ref
=
_readSpace
(
group
[
'
To
'
])
fieldToRef
=
affine
.
inverse
(
refToField
)
refSpace
=
affine
.
identify
(
ref
,
pre
,
from_
=
'
world
'
)[
1
]
pre
=
group
.
get
(
'
Pre
'
)
srcSpace
=
affine
.
identify
(
src
,
post
,
to
=
'
world
'
)[
0
]
post
=
group
.
get
(
'
Post
'
)
init
=
group
.
get
(
'
InitialAlignment
'
)
return
nonlinear
.
CoefficientField
(
field
,
if
pre
is
not
None
:
pre
=
_readAffine
(
pre
)
src
,
if
post
is
not
None
:
post
=
_readAffine
(
post
)
ref
,
if
init
is
not
None
:
init
=
_readAffine
(
init
)
srcSpace
=
srcSpace
,
refSpace
=
refSpace
,
fieldType
=
fieldType
,
knotSpacing
=
spacing
,
srcToRefMat
=
srcToRef
,
fieldToRefMat
=
fieldToRef
)
refSpace
=
'
world
'
srcSpace
=
'
world
'
try
:
if
pre
is
not
None
:
refSpace
=
affine
.
identify
(
ref
,
pre
,
from_
=
'
world
'
)[
1
]
if
post
is
not
None
:
srcSpace
=
affine
.
identify
(
src
,
post
,
to
=
'
world
'
)[
0
]
except
ValueError
:
raise
X5Error
(
'
Invalid pre/post affine
'
)
return
src
,
ref
,
pre
,
post
,
init
,
srcSpace
,
refSpace
def
readLinearX5
(
fname
):
"""
Read a linear X5 transformation file from ``fname``.
:return: A tuple containing:
def
_writeNonLinearCommon
(
group
,
field
):
"""
Writes the spaces and affines for the given nonlinear transform to the
given group.
- A ``(4, 4)`` ``numpy`` array containing the affine
:arg group: A ``h5py.Group`` object.
transformation
:arg field: A :class:`.NonLinearTransform` object
- A :class:`.Nifti` instance representing the source space
- A :class:`.Nifti` instance representing the reference space
"""
"""
with
h5py
.
File
(
fname
,
'
r
'
)
as
f
:
_validateMetadata
(
f
[
'
/
'
])
xform
=
_readAffine
(
f
[
'
/
'
])
src
=
_readSpace
(
f
[
'
/From
'
])
ref
=
_readSpace
(
f
[
'
/To
'
])
return
xform
,
src
,
ref
_writeSpace
(
group
.
create_group
(
'
From
'
),
field
.
src
)
_writeSpace
(
group
.
create_group
(
'
To
'
),
field
.
ref
)
pre
=
field
.
ref
.
getAffine
(
'
world
'
,
field
.
refSpace
)
post
=
field
.
src
.
getAffine
(
field
.
srcSpace
,
'
world
'
)
def
writeLinearX5
(
fname
,
xform
,
src
,
ref
):
_writeAffine
(
group
.
create_group
(
'
Pre
'
),
pre
)
"""
Write a linear transformation to the specified file.
_writeAffine
(
group
.
create_group
(
'
Post
'
),
post
)
:arg fname: File name to write to
if
field
.
srcToRefMat
is
not
None
:
:arg xform: ``(4, 4)`` ``numpy`` array containing the affine transformation
_writeAffine
(
group
.
create_group
(
'
InitialAlignment
'
,
field
.
srcToRefMat
))
:arg src: :class:`.Nifti` image representing the source space
:arg ref: :class:`.Nifti` image representing the reference
"""
with
h5py
.
File
(
fname
,
'
w
'
)
as
f
:
_writeMetadata
(
f
)
_writeAffine
(
f
,
xform
)
from_
=
f
.
create_group
(
'
/From
'
)
def
_readDisplacementField
(
group
):
to
=
f
.
create_group
(
'
/To
'
)
"""
Reads a nonlinear displacement field from the given group.
_writeSpace
(
from_
,
src
)
:arg group: A ``h5py.Group`` object.
_writeSpace
(
to
,
ref
)
:returns: A :class:`.DisplacementField` object
"""
src
,
ref
,
pre
,
post
,
init
,
srcSpace
,
refSpace
=
_readNonLinearCommon
(
group
)
field
=
np
.
array
(
group
[
'
Transform
'
])
dtype
=
group
[
'
Representation
'
]
field
=
nonlinear
.
DisplacementField
(
field
,
src
=
src
,
ref
=
ref
,
srcSpace
=
srcSpace
,
refSpace
=
refSpace
,
dispType
=
dtype
,
srcToRefMat
=
init
)
return
field
def
readNonLinearX5
(
fname
):
def
_writeDisplacementField
(
group
,
field
):
"""
Writes a nonlinear displacement field to the given group.
:arg group: A ``h5py.Group`` object.
:arg field: A :class:`.DisplacementField` object
"""
"""
_writeNonLinearCommon
(
group
,
field
)
group
.
attrs
[
'
SubType
'
]
=
'
displacement
'
group
.
attrs
[
'
Representation
'
]
=
field
.
displacementType
xform
=
np
.
field
.
data
.
astype
(
np
.
float64
)
group
.
create_dataset
(
'
Transform
'
,
data
=
xform
)
def
_readCoefficientField
(
group
):
"""
Reads a nonlinear coefficient field from the given group.
:arg group: A ``h5py.Group`` object.
:returns: A :class:`.CoefficientField` object
"""
"""
from
.
import
n
on
l
inear
src
,
ref
,
pre
,
post
,
init
,
srcSpace
,
refSpace
=
_readN
on
L
inear
Common
(
group
)
with
h5py
.
File
(
fname
,
'
r
'
)
as
f
:
field
=
np
.
array
(
group
[
'
Transform
'
])
field
=
_readNonLinearTransform
(
f
[
'
/
'
])
ftype
=
group
[
'
Representation
'
]
src
=
_readSpace
(
f
[
'
/From
'
])
spacing
=
group
[
'
Parameters/Spacing
'
]
ref
=
_readSpace
(
f
[
'
/To
'
])
refToField
=
_readAffine
(
group
[
'
Parameters/ReferenceToField
'
])
fieldToRef
=
affine
.
invert
(
refToField
)
if
ftype
==
'
quadratic bspline
'
:
ftype
=
'
quadratic
'
elif
ftype
==
'
cubic bspline
'
:
ftype
=
'
cubic
'
# TODO coefficient fields
if
spacing
.
shape
!=
3
:
return
nonlinear
.
DisplacementField
(
field
,
raise
X5Error
(
'
Invalid spacing: {}
'
.
format
(
spacing
))
field
=
nonlinear
.
CoefficientField
(
field
,
src
=
src
,
src
=
src
,
ref
=
ref
,
ref
=
ref
,
srcSpace
=
'
world
'
,
srcSpace
=
srcSpace
,
refSpace
=
'
world
'
)
refSpace
=
refSpace
,
fieldType
=
ftype
,
knotSpacing
=
spacing
,
fieldToRefMat
=
fieldToRef
,
srcToRefMat
=
init
)
return
field
def
writeNonLinearX5
(
fname
,
field
):
"""
def
_writeCoefficientField
(
group
,
field
):
"""
Writes a nonlinear coefficient field to the given group.
:arg group: A ``h5py.Group`` object.
:arg field: A :class:`.CoefficientField` object
"""
"""
# TODO coefficient
field
s
_writeNonLinearCommon
(
group
,
field
)
with
h5py
.
File
(
fname
,
'
w
'
)
as
f
:
group
.
attrs
[
'
SubType
'
]
=
'
coefficient
'
_writeMetadata
(
f
)
_writeNonLinearTransform
(
f
,
field
.
data
)
from_
=
f
.
create_group
(
'
/From
'
)
if
field
.
fieldType
==
'
cubic
'
:
to
=
f
.
create_group
(
'
/To
'
)
group
.
attrs
[
'
Representation
'
]
=
'
cubic bspline
'
elif
field
.
fieldType
==
'
quadratic
'
:
group
.
attrs
[
'
Representation
'
]
=
'
quadratic bspline
'
xform
=
np
.
field
.
data
.
astype
(
np
.
float64
)
group
.
create_dataset
(
'
Transform
'
,
data
=
xform
)
params
=
group
.
create_group
(
'
Parameters
'
)
refToField
=
params
.
create_group
(
'
ReferenceToField
'
)
_writeLinearMapping
(
from_
,
field
.
src
)
params
.
attrs
[
'
Spacing
'
]
=
field
.
knotSpacing
_write
LinearMapping
(
to
,
field
.
ref
)
_write
Affine
(
refToField
,
field
.
refToFieldMat
)
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