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
FSL
fslpy
Commits
3d4a345d
Commit
3d4a345d
authored
Oct 08, 2020
by
Paul McCarthy
🚵
Browse files
Merge branch 'rel/3.3.1' into 'v3.3'
Rel/3.3.1 See merge request fsl/fslpy!256
parents
09c4e379
82b90ca4
Pipeline
#5652
passed with stages
in 5 minutes and 3 seconds
Changes
5
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
CHANGELOG.rst
View file @
3d4a345d
This document contains the ``fslpy`` release history in reverse chronological
This document contains the ``fslpy`` release history in reverse chronological
order.
order.
3.3.1 (Thursday 8th October 2020)
---------------------------------
Changed
^^^^^^^
* The :func:`.affine.decompose` and :func:`.affine.compose` functions now
have the ability to return/accept shear components.
Fixed
^^^^^
* Fixed a bug in the :func:`.affine.decompose` function which was corrupting
the scale estimates when given an affine containing shears.
3.3.0 (Tuesday 22nd September 2020)
3.3.0 (Tuesday 22nd September 2020)
-----------------------------------
-----------------------------------
...
...
fsl/transform/affine.py
View file @
3d4a345d
...
@@ -120,7 +120,7 @@ def scaleOffsetXform(scales, offsets):
...
@@ -120,7 +120,7 @@ def scaleOffsetXform(scales, offsets):
return
xform
return
xform
def
compose
(
scales
,
offsets
,
rotations
,
origin
=
None
):
def
compose
(
scales
,
offsets
,
rotations
,
origin
=
None
,
shears
=
None
):
"""Compose a transformation matrix out of the given scales, offsets
"""Compose a transformation matrix out of the given scales, offsets
and axis rotations.
and axis rotations.
...
@@ -133,6 +133,8 @@ def compose(scales, offsets, rotations, origin=None):
...
@@ -133,6 +133,8 @@ def compose(scales, offsets, rotations, origin=None):
:arg origin: Origin of rotation - must be scaled by the ``scales``.
:arg origin: Origin of rotation - must be scaled by the ``scales``.
If not provided, the rotation origin is ``(0, 0, 0)``.
If not provided, the rotation origin is ``(0, 0, 0)``.
:arg shears: Sequence of three shear values
"""
"""
preRotate
=
np
.
eye
(
4
)
preRotate
=
np
.
eye
(
4
)
...
@@ -154,6 +156,7 @@ def compose(scales, offsets, rotations, origin=None):
...
@@ -154,6 +156,7 @@ def compose(scales, offsets, rotations, origin=None):
scale
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
scale
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
offset
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
offset
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
rotate
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
rotate
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
shear
=
np
.
eye
(
4
,
dtype
=
np
.
float64
)
scale
[
0
,
0
]
=
scales
[
0
]
scale
[
0
,
0
]
=
scales
[
0
]
scale
[
1
,
1
]
=
scales
[
1
]
scale
[
1
,
1
]
=
scales
[
1
]
...
@@ -164,10 +167,15 @@ def compose(scales, offsets, rotations, origin=None):
...
@@ -164,10 +167,15 @@ def compose(scales, offsets, rotations, origin=None):
rotate
[:
3
,
:
3
]
=
rotations
rotate
[:
3
,
:
3
]
=
rotations
return
concat
(
offset
,
postRotate
,
rotate
,
preRotate
,
scale
)
if
shears
is
not
None
:
shear
[
0
,
1
]
=
shears
[
0
]
shear
[
0
,
2
]
=
shears
[
1
]
shear
[
1
,
2
]
=
shears
[
2
]
return
concat
(
offset
,
postRotate
,
rotate
,
preRotate
,
scale
,
shear
)
def
decompose
(
xform
,
angles
=
True
):
def
decompose
(
xform
,
angles
=
True
,
shears
=
False
):
"""Decomposes the given transformation matrix into separate offsets,
"""Decomposes the given transformation matrix into separate offsets,
scales, and rotations, according to the algorithm described in:
scales, and rotations, according to the algorithm described in:
...
@@ -175,8 +183,7 @@ def decompose(xform, angles=True):
...
@@ -175,8 +183,7 @@ def decompose(xform, angles=True):
320-323 in *Graphics Gems II*, James Arvo (editor), Academic Press, 1991,
320-323 in *Graphics Gems II*, James Arvo (editor), Academic Press, 1991,
ISBN: 0120644819.
ISBN: 0120644819.
It is assumed that the given transform has no perspective components. Any
It is assumed that the given transform has no perspective components.
shears in the affine are discarded.
:arg xform: A ``(3, 3)`` or ``(4, 4)`` affine transformation matrix.
:arg xform: A ``(3, 3)`` or ``(4, 4)`` affine transformation matrix.
...
@@ -184,6 +191,8 @@ def decompose(xform, angles=True):
...
@@ -184,6 +191,8 @@ def decompose(xform, angles=True):
as axis-angles, in radians. Otherwise, the rotation matrix
as axis-angles, in radians. Otherwise, the rotation matrix
is returned.
is returned.
:arg shears: Defaults to ``False``. If ``True``, shears are returned.
:returns: The following:
:returns: The following:
- A sequence of three scales
- A sequence of three scales
...
@@ -191,6 +200,7 @@ def decompose(xform, angles=True):
...
@@ -191,6 +200,7 @@ def decompose(xform, angles=True):
was a ``(3, 3)`` matrix)
was a ``(3, 3)`` matrix)
- A sequence of three rotations, in radians. Or, if
- A sequence of three rotations, in radians. Or, if
``angles is False``, a rotation matrix.
``angles is False``, a rotation matrix.
- If ``shears is True``, a sequence of three shears.
"""
"""
# The inline comments in the code below are taken verbatim from
# The inline comments in the code below are taken verbatim from
...
@@ -199,7 +209,7 @@ def decompose(xform, angles=True):
...
@@ -199,7 +209,7 @@ def decompose(xform, angles=True):
# The next step is to extract the translations. This is trivial;
# The next step is to extract the translations. This is trivial;
# we find t_x = M_{4,1}, t_y = M_{4,2}, and t_z = M_{4,3}. At this
# we find t_x = M_{4,1}, t_y = M_{4,2}, and t_z = M_{4,3}. At this
# point we are left with a 3*3 matrix M' = M_{1..3,1..3}.
# point we are left with a 3*3 matrix M' = M_{1..3,1..3}.
xform
=
xform
.
T
xform
=
np
.
array
(
xform
)
.
T
if
xform
.
shape
==
(
4
,
4
):
if
xform
.
shape
==
(
4
,
4
):
translations
=
xform
[
3
,
:
3
]
translations
=
xform
[
3
,
:
3
]
...
@@ -214,6 +224,7 @@ def decompose(xform, angles=True):
...
@@ -214,6 +224,7 @@ def decompose(xform, angles=True):
# The process of finding the scaling factors and shear parameters
# The process of finding the scaling factors and shear parameters
# is interleaved. First, find s_x = |M'_1|.
# is interleaved. First, find s_x = |M'_1|.
sx
=
np
.
sqrt
(
np
.
dot
(
M1
,
M1
))
sx
=
np
.
sqrt
(
np
.
dot
(
M1
,
M1
))
M1
=
M1
/
sx
# Then, compute an initial value for the xy shear factor,
# Then, compute an initial value for the xy shear factor,
# s_xy = M'_1 * M'_2. (this is too large by the y scaling factor).
# s_xy = M'_1 * M'_2. (this is too large by the y scaling factor).
...
@@ -230,7 +241,7 @@ def decompose(xform, angles=True):
...
@@ -230,7 +241,7 @@ def decompose(xform, angles=True):
# The second row is normalized, and s_xy is divided by s_y to
# The second row is normalized, and s_xy is divided by s_y to
# get its final value.
# get its final value.
M2
=
M2
/
sy
M2
=
M2
/
sy
sxy
=
sxy
/
s
y
sxy
=
sxy
/
s
x
# The xz and yz shear factors are computed as in the preceding,
# The xz and yz shear factors are computed as in the preceding,
sxz
=
np
.
dot
(
M1
,
M3
)
sxz
=
np
.
dot
(
M1
,
M3
)
...
@@ -245,8 +256,8 @@ def decompose(xform, angles=True):
...
@@ -245,8 +256,8 @@ def decompose(xform, angles=True):
# the third row is normalized, and the xz and yz shear factors are
# the third row is normalized, and the xz and yz shear factors are
# rescaled.
# rescaled.
M3
=
M3
/
sz
M3
=
M3
/
sz
sxz
=
sxz
/
s
z
sxz
=
sxz
/
s
x
syz
=
s
x
z
/
s
z
syz
=
s
y
z
/
s
y
# The resulting matrix now is a pure rotation matrix, except that it
# The resulting matrix now is a pure rotation matrix, except that it
# might still include a scale factor of -1. If the determinant of the
# might still include a scale factor of -1. If the determinant of the
...
@@ -266,7 +277,13 @@ def decompose(xform, angles=True):
...
@@ -266,7 +277,13 @@ def decompose(xform, angles=True):
if
angles
:
rotations
=
rotMatToAxisAngles
(
R
.
T
)
if
angles
:
rotations
=
rotMatToAxisAngles
(
R
.
T
)
else
:
rotations
=
R
.
T
else
:
rotations
=
R
.
T
return
np
.
array
([
sx
,
sy
,
sz
]),
translations
,
rotations
retval
=
[
np
.
array
([
sx
,
sy
,
sz
]),
translations
,
rotations
]
if
shears
:
retval
.
append
(
np
.
array
((
sxy
,
sxz
,
syz
)))
return
tuple
(
retval
)
def
rotMatToAffine
(
rotmat
,
origin
=
None
):
def
rotMatToAffine
(
rotmat
,
origin
=
None
):
...
...
fsl/version.py
View file @
3d4a345d
...
@@ -47,7 +47,7 @@ import re
...
@@ -47,7 +47,7 @@ import re
import
string
import
string
__version__
=
'3.3.
0
'
__version__
=
'3.3.
1
'
"""Current version number, as a string. """
"""Current version number, as a string. """
...
...
tests/test_transform/test_affine.py
View file @
3d4a345d
...
@@ -226,8 +226,10 @@ def test_compose_and_decompose():
...
@@ -226,8 +226,10 @@ def test_compose_and_decompose():
xform
=
lines
[
i
*
4
:
i
*
4
+
4
]
xform
=
lines
[
i
*
4
:
i
*
4
+
4
]
xform
=
np
.
genfromtxt
(
xform
)
xform
=
np
.
genfromtxt
(
xform
)
scales
,
offsets
,
rotations
=
affine
.
decompose
(
xform
)
scales
,
offsets
,
rotations
,
shears
=
affine
.
decompose
(
result
=
affine
.
compose
(
scales
,
offsets
,
rotations
)
xform
,
shears
=
True
)
result
=
affine
.
compose
(
scales
,
offsets
,
rotations
,
shears
=
shears
)
assert
np
.
all
(
np
.
isclose
(
xform
,
result
,
atol
=
1e-5
))
assert
np
.
all
(
np
.
isclose
(
xform
,
result
,
atol
=
1e-5
))
...
@@ -235,8 +237,10 @@ def test_compose_and_decompose():
...
@@ -235,8 +237,10 @@ def test_compose_and_decompose():
# different rotation origin, but we test
# different rotation origin, but we test
# explicitly passing the origin for
# explicitly passing the origin for
# completeness
# completeness
scales
,
offsets
,
rotations
=
affine
.
decompose
(
xform
)
scales
,
offsets
,
rotations
,
shears
=
affine
.
decompose
(
result
=
affine
.
compose
(
scales
,
offsets
,
rotations
,
[
0
,
0
,
0
])
xform
,
shears
=
True
)
result
=
affine
.
compose
(
scales
,
offsets
,
rotations
,
origin
=
[
0
,
0
,
0
],
shears
=
shears
)
assert
np
.
all
(
np
.
isclose
(
xform
,
result
,
atol
=
1e-5
))
assert
np
.
all
(
np
.
isclose
(
xform
,
result
,
atol
=
1e-5
))
...
...
tests/test_transform/testdata/test_transform_test_compose.txt
View file @
3d4a345d
...
@@ -23,7 +23,7 @@
...
@@ -23,7 +23,7 @@
1.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 0.0 -1.0 0.0
0.0 0.0 -1.0 0.0
0.0 0.0 0.0 1.0
0.0 0.0 0.0 1.0
0.5 0.0 0.0 0.0
0.5 0.0 0.0 0.0
0.0 2.4 0.0 0.0
0.0 2.4 0.0 0.0
...
@@ -35,22 +35,22 @@
...
@@ -35,22 +35,22 @@
0.0 0.0 1.6 53.0
0.0 0.0 1.6 53.0
0.0 0.0 0.0 1.0
0.0 0.0 0.0 1.0
0.5 0.0 0.0 10.0
0.5 0.0 0.0 10.0
0.0 0.0 -1.6 -6.7877502441
0.0 0.0 -1.6 -6.7877502441
0.0 2.4 0.0 29.7931518555
0.0 2.4 0.0 29.7931518555
0.0 0.0 0.0 1.0
0.0 0.0 0.0 1.0
0.0 -2.4 -0.0 30.6160888672
0.0 -2.4 -0.0 30.6160888672
0.0 0.0 -1.6 -6.7877502441
0.0 0.0 -1.6 -6.7877502441
0.5 0.0 0.0 50.5961608887
0.5 0.0 0.0 50.5961608887
0.0 0.0 0.0 1.0
0.0 0.0 0.0 1.0
-0.0 1.3765834472 1.3106432709 0.0736983719
-0.0 1.3765834472 1.3106432709 0.0736983719
0.0 -1.9659649063 0.9177222982 14.1062102814
0.0 -1.9659649063 0.9177222982 14.1062102814
0.5 0.0 0.0 50.5961608887
0.5 0.0 0.0 50.5961608887
0.0 0.0 0.0 1.0
0.0 0.0 0.0 1.0
0.453766452 -0.7004337463 0.4832135801 16.789591468
0.453766452 -0.7004337463 0.4832135801 16.789591468
-0.0159784155 1.6056689296 1.1880787388 -16.2943532298
-0.0159784155 1.6056689296 1.1880787388 -16.2943532298
-0.2093817023 -1.640493784 0.9565424959 66.1123321137
-0.2093817023 -1.640493784 0.9565424959 66.1123321137
0.0 0.0 0.0 1.0
0.0 0.0 0.0 1.0
...
@@ -109,3 +109,8 @@
...
@@ -109,3 +109,8 @@
0.041017 -0.081580 -6.319922 72.090378
0.041017 -0.081580 -6.319922 72.090378
-0.000000 0.381914 -1.365036 -41.159451
-0.000000 0.381914 -1.365036 -41.159451
0.000000 0.000000 0.000000 1.000000
0.000000 0.000000 0.000000 1.000000
1.1441229 0.833406 3.765340 3
0.8312539 2.459607 -0.804545 4
1.4142136 1.554275 1.724796 5
0.0 0.0 0.0 1
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