Skip to content

ENH WIP: Use geodesic distance in FramewiseDisplacement #2607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
422fbdd
Add geomstats module + geodesic distance for Frame Displacement
ninamiolane Jun 4, 2018
ea9f4dd
Add geomstats module + geodesic distance for Frame Displacement
ninamiolane Jun 4, 2018
9c65c57
Add metric as an option to FramewiseDisplacement
ninamiolane Jun 6, 2018
9a1cb9d
Merge with remote branch
ninamiolane Jun 6, 2018
30098a5
Add geomstats module + geodesic distance for Frame Displacement
ninamiolane Jun 4, 2018
b35fc5e
Add geomstats module + geodesic distance for Frame Displacement
ninamiolane Jun 4, 2018
6199667
Add metric as an option to FramewiseDisplacement
ninamiolane Jun 6, 2018
8e7f2ee
Address Chris CR
ninamiolane Aug 14, 2018
c74a833
Merge with origin nina-geodesic-distance
ninamiolane Aug 14, 2018
308e622
Reverse requirements
ninamiolane Aug 14, 2018
1e7e550
Add conversion function from Euler angles
ninamiolane Aug 14, 2018
741f989
Put only use_default=True and add corresponding line in test auto
ninamiolane Aug 14, 2018
8d1b6f6
Add unit test for FrameDisplacement with Riemannian metric
ninamiolane Aug 14, 2018
8fb79c4
Add geomstats in Dockerfile dependencies
ninamiolane Aug 15, 2018
8f9ce0f
Merge remote-tracking branch 'upstream/master' into nina-geodesic-dis…
effigies Aug 15, 2018
5856288
Address Chris CR
ninamiolane Aug 15, 2018
0565df4
Adapt convention for Euler angles from spm_matrix.m
ninamiolane Aug 28, 2018
6089d27
Address Chris CR
ninamiolane Aug 28, 2018
c82b5fb
Run specs for test_auto_FramewiseDisplacement.py
ninamiolane Aug 28, 2018
c9a896c
Merge branch 'master' into nina-geodesic-distance
effigies Jan 16, 2019
4177f4e
Merge remote-tracking branch 'upstream/master' into nina-geodesic-dis…
effigies Feb 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions nipype/algorithms/confounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,16 @@ class FramewiseDisplacementInputSpec(BaseInterfaceInputSpec):
desc="Source of movement parameters",
mandatory=True,
)
metric = traits.Enum(
"L1",
"riemannian",
usedefault=True,
mandatory=True,
desc="Distance metric to apply: "
"L1 = Manhattan distance (original definition); "
"riemannian = Riemannian distance on the "
"Special Euclidean group in 3D (geodesic)",
)
radius = traits.Float(
50,
usedefault=True,
Expand Down Expand Up @@ -342,9 +352,38 @@ def _run_interface(self, runtime):
arr=mpars,
source=self.inputs.parameter_source,
)
diff = mpars[:-1, :6] - mpars[1:, :6]
diff[:, 3:6] *= self.inputs.radius
fd_res = np.abs(diff).sum(axis=1)

if self.inputs.metric == "L1":
diff = mpars[:-1, :6] - mpars[1:, :6]
diff[:, 3:6] *= self.inputs.radius
fd_res = np.abs(diff).sum(axis=1)

elif self.inputs.metric == "riemannian":
from geomstats.invariant_metric import InvariantMetric
from geomstats.special_euclidean_group import SpecialEuclideanGroup

SE3_GROUP = SpecialEuclideanGroup(n=3)
SO3_GROUP = SE3_GROUP.rotations
DIM_TRANSLATIONS = SE3_GROUP.translations.dimension
DIM_ROTATIONS = SE3_GROUP.rotations.dimension

so3pars = mpars[:, DIM_TRANSLATIONS:]
so3pars = SO3_GROUP.rotation_vector_from_tait_bryan_angles(
so3pars, extrinsic_or_intrinsic="extrinsic", order="zyx"
)

se3pars = np.hstack([so3pars, mpars[:, :DIM_TRANSLATIONS]])

diag_rotations = self.inputs.radius * np.ones(DIM_ROTATIONS)
diag_translations = np.ones(DIM_TRANSLATIONS)
diag = np.concatenate([diag_rotations, diag_translations])
inner_product = np.diag(diag)
metric = InvariantMetric(
group=SE3_GROUP,
inner_product_mat_at_identity=inner_product,
left_or_right="left",
)
fd_res = metric.dist(se3pars[:-1], se3pars[1:])

self._results = {
"out_file": op.abspath(self.inputs.out_file),
Expand Down
1 change: 1 addition & 0 deletions nipype/algorithms/tests/test_auto_FramewiseDisplacement.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def test_FramewiseDisplacement_inputs():
figdpi=dict(usedefault=True,),
figsize=dict(usedefault=True,),
in_file=dict(extensions=None, mandatory=True,),
metric=dict(mandatory=True, usedefault=True,),
normalize=dict(usedefault=True,),
out_figure=dict(extensions=None, usedefault=True,),
out_file=dict(extensions=None, usedefault=True,),
Expand Down
34 changes: 34 additions & 0 deletions nipype/algorithms/tests/test_confounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,48 @@
except ImportError:
pass

nogeomstats = True
try:
import geomstats

nogeomstats = False
except ImportError:
pass


def test_fd(tmpdir):
tempdir = tmpdir.strpath
ground_truth = np.loadtxt(example_data("fsl_motion_outliers_fd.txt"))

fdisplacement = FramewiseDisplacement(
in_file=example_data("fsl_mcflirt_movpar.txt"),
out_file=tempdir + "/fd.txt",
parameter_source="FSL",
)
res = fdisplacement.run()

with open(res.outputs.out_file) as all_lines:
for line in all_lines:
assert "FramewiseDisplacement" in line
break

assert np.allclose(
ground_truth, np.loadtxt(res.outputs.out_file, skiprows=1), atol=0.16
)
assert np.abs(ground_truth.mean() - res.outputs.fd_average) < 1e-2


@pytest.mark.skipif(nogeomstats, reason="geomstats is not installed")
def test_fd_riemannian(tmpdir):
tempdir = tmpdir.strpath
# TODO(nina): Adapt ground_truth w. SPM Euler angles convention
ground_truth = np.loadtxt(example_data("fsl_motion_outliers_fd.txt"))

fdisplacement = FramewiseDisplacement(
in_file=example_data("fsl_mcflirt_movpar.txt"),
out_file=tempdir + "/fd.txt",
parameter_source="FSL",
metric="riemannian",
)
res = fdisplacement.run()

Expand Down
2 changes: 1 addition & 1 deletion nipype/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def get_nipype_gitversion():
"sphinxcontrib-napoleon",
],
"duecredit": ["duecredit"],
"nipy": ["nitime", "nilearn<0.5.0", "dipy", "nipy", "matplotlib"],
"nipy": ["nitime", "nilearn<0.5.0", "dipy", "nipy", "matplotlib", "geomstats>=1.1.0"],
"profiler": ["psutil>=5.0"],
"pybids": ["pybids>=0.7.0"],
"specs": ["black"],
Expand Down