Skip to content

Commit d0f0297

Browse files
committed
Merge branch 'master' into fix/AreaWeighedP2PDistance
Conflicts: CHANGES
2 parents b06e2b8 + 453bcf9 commit d0f0297

File tree

5 files changed

+107
-20
lines changed

5 files changed

+107
-20
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Next release
66
* FIX: Refactor P2PDistance, change name to ComputeMeshWarp, add regression tests,
77
fix bug in area weighted distance, and added optimizations
88
(https://github.com/nipy/nipype/pull/1016)
9+
* ENH: Add the average distance to ErrorMap (https://github.com/nipy/nipype/pull/1039)
910
* ENH: Inputs with name_source can be now chained in cascade (https://github.com/nipy/nipype/pull/938)
1011
* ENH: Improve JSON interfaces: default settings when reading and consistent output creation
1112
when writing (https://github.com/nipy/nipype/pull/1047)

examples/rsfmri_vol_surface_preprocessing.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def build_filter1(motion_params, comp_norm, outliers, detrend_poly=None):
230230
out_params = np.hstack((out_params, outlier_vector))
231231
if detrend_poly:
232232
timepoints = out_params.shape[0]
233-
X = np.ones((timepoints, 1))
233+
X = np.empty((timepoints, 0))
234234
for i in range(detrend_poly):
235235
X = np.hstack((X, legendre(
236236
i + 1)(np.linspace(-1, 1, timepoints))[:, None]))
@@ -542,7 +542,7 @@ def create_reg_workflow(name='registration'):
542542

543543
warpmean = Node(ants.ApplyTransforms(), name='warpmean')
544544
warpmean.inputs.input_image_type = 3
545-
warpmean.inputs.interpolation = 'BSpline'
545+
warpmean.inputs.interpolation = 'Linear'
546546
warpmean.inputs.invert_transform_flags = [False, False]
547547
warpmean.inputs.terminal_output = 'file'
548548
warpmean.inputs.args = '--float'
@@ -771,7 +771,7 @@ def merge_files(in1, in2):
771771
warpall = MapNode(ants.ApplyTransforms(), iterfield=['input_image'],
772772
name='warpall')
773773
warpall.inputs.input_image_type = 3
774-
warpall.inputs.interpolation = 'BSpline'
774+
warpall.inputs.interpolation = 'Linear'
775775
warpall.inputs.invert_transform_flags = [False, False]
776776
warpall.inputs.terminal_output = 'file'
777777
warpall.inputs.reference_image = target_file
@@ -1003,9 +1003,11 @@ def create_resting_workflow(args, name=None):
10031003
parser.add_argument('--surf_fwhm', default=15., dest='surf_fwhm',
10041004
type=float, help="Spatial FWHM" + defstr)
10051005
parser.add_argument("-l", "--lowpass_freq", dest="lowpass_freq",
1006-
default=0.1, help="Low pass frequency (Hz)" + defstr)
1006+
default=0.1, type=float,
1007+
help="Low pass frequency (Hz)" + defstr)
10071008
parser.add_argument("-u", "--highpass_freq", dest="highpass_freq",
1008-
default=0.01, help="High pass frequency (Hz)" + defstr)
1009+
default=0.01, type=float,
1010+
help="High pass frequency (Hz)" + defstr)
10091011
parser.add_argument("-o", "--output_dir", dest="sink",
10101012
help="Output directory base", required=True)
10111013
parser.add_argument("-w", "--work_dir", dest="work_dir",

examples/rsfmri_vol_surface_preprocessing_nipy.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def build_filter1(motion_params, comp_norm, outliers, detrend_poly=None):
217217
out_params = np.hstack((out_params, outlier_vector))
218218
if detrend_poly:
219219
timepoints = out_params.shape[0]
220-
X = np.ones((timepoints, 1))
220+
X = np.empty((timepoints, 0))
221221
for i in range(detrend_poly):
222222
X = np.hstack((X, legendre(
223223
i + 1)(np.linspace(-1, 1, timepoints))[:, None]))
@@ -526,7 +526,7 @@ def create_reg_workflow(name='registration'):
526526
"""
527527
warpmean = Node(ants.ApplyTransforms(), name='warpmean')
528528
warpmean.inputs.input_image_type = 3
529-
warpmean.inputs.interpolation = 'BSpline'
529+
warpmean.inputs.interpolation = 'Linear'
530530
warpmean.inputs.invert_transform_flags = [False, False]
531531
warpmean.inputs.terminal_output = 'file'
532532
warpmean.inputs.args = '--float'
@@ -749,7 +749,7 @@ def merge_files(in1, in2):
749749
warpall = MapNode(ants.ApplyTransforms(), iterfield=['input_image'],
750750
name='warpall')
751751
warpall.inputs.input_image_type = 3
752-
warpall.inputs.interpolation = 'BSpline'
752+
warpall.inputs.interpolation = 'Linear'
753753
warpall.inputs.invert_transform_flags = [False, False]
754754
warpall.inputs.terminal_output = 'file'
755755
warpall.inputs.reference_image = target_file
@@ -996,9 +996,11 @@ def create_resting_workflow(args, name=None):
996996
parser.add_argument('--surf_fwhm', default=15., dest='surf_fwhm',
997997
type=float, help="Spatial FWHM" + defstr)
998998
parser.add_argument("-l", "--lowpass_freq", dest="lowpass_freq",
999-
default=0.1, help="Low pass frequency (Hz)" + defstr)
999+
default=0.1, type=float,
1000+
help="Low pass frequency (Hz)" + defstr)
10001001
parser.add_argument("-u", "--highpass_freq", dest="highpass_freq",
1001-
default=0.01, help="High pass frequency (Hz)" + defstr)
1002+
default=0.01, type=float,
1003+
help="High pass frequency (Hz)" + defstr)
10021004
parser.add_argument("-o", "--output_dir", dest="sink",
10031005
help="Output directory base", required=True)
10041006
parser.add_argument("-w", "--work_dir", dest="work_dir",

nipype/algorithms/metrics.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ class ErrorMapInputSpec(BaseInterfaceInputSpec):
488488

489489
class ErrorMapOutputSpec(TraitedSpec):
490490
out_map = File(exists=True, desc="resulting error map")
491+
distance = traits.Float(desc="Average distance between volume 1 and 2")
491492

492493

493494
class ErrorMap(BaseInterface):
@@ -506,12 +507,13 @@ class ErrorMap(BaseInterface):
506507
_out_file = ''
507508

508509
def _run_interface(self, runtime):
509-
from scipy.spatial.distance import cdist, pdist
510+
# Get two numpy data matrices
510511
nii_ref = nb.load(self.inputs.in_ref)
511512
ref_data = np.squeeze(nii_ref.get_data())
512513
tst_data = np.squeeze(nb.load(self.inputs.in_tst).get_data())
513514
assert(ref_data.ndim == tst_data.ndim)
514515

516+
# Load mask
515517
comps = 1
516518
mapshape = ref_data.shape
517519

@@ -528,26 +530,29 @@ def _run_interface(self, runtime):
528530
else:
529531
msk = np.ones(shape=mapshape)
530532

533+
# Flatten both volumes and make the pixel differennce
531534
mskvector = msk.reshape(-1)
532535
msk_idxs = np.where(mskvector==1)
533536
refvector = ref_data.reshape(-1,comps)[msk_idxs].astype(np.float32)
534537
tstvector = tst_data.reshape(-1,comps)[msk_idxs].astype(np.float32)
535538
diffvector = (refvector-tstvector)
536539

540+
# Scale the difference
537541
if self.inputs.metric == 'sqeuclidean':
538542
errvector = diffvector**2
543+
if (comps > 1):
544+
errvector = np.sum(errvector, axis=1)
545+
else:
546+
errvector = np.squeeze(errvector)
539547
elif self.inputs.metric == 'euclidean':
540-
X = np.hstack((refvector, tstvector))
541-
errvector = np.linalg.norm(X, axis=1)
548+
errvector = np.linalg.norm(diffvector, axis=1)
542549

543-
if (comps > 1):
544-
errvector = np.sum(errvector, axis=1)
545-
else:
546-
errvector = np.squeeze(errvector)
547-
548-
errvectorexp = np.zeros_like(mskvector)
550+
errvectorexp = np.zeros_like(mskvector, dtype=np.float32) # The default type is uint8
549551
errvectorexp[msk_idxs] = errvector
550552

553+
# Get averaged error
554+
self._distance = np.average(errvector) # Only average the masked voxels
555+
551556
errmap = errvectorexp.reshape(mapshape)
552557

553558
hdr = nii_ref.get_header().copy()
@@ -567,11 +572,12 @@ def _run_interface(self, runtime):
567572
nb.Nifti1Image(errmap.astype(np.float32), nii_ref.get_affine(),
568573
hdr).to_filename(self._out_file)
569574

570-
return runtime
575+
return runtime
571576

572577
def _list_outputs(self):
573578
outputs = self.output_spec().get()
574579
outputs['out_map'] = self._out_file
580+
outputs['distance'] = self._distance
575581
return outputs
576582

577583

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
from nipype.testing import (assert_equal, example_data)
5+
from nipype.algorithms.metrics import ErrorMap
6+
import nibabel as nib
7+
import numpy as np
8+
from tempfile import mkdtemp
9+
import os
10+
11+
def test_errormap():
12+
13+
tempdir = mkdtemp()
14+
# Single-Spectual
15+
# Make two fake 2*2*2 voxel volumes
16+
volume1 = np.array([[[2.0, 8.0], [1.0, 2.0]], [[1.0, 9.0], [0.0, 3.0]]]) # John von Neumann's birthday
17+
volume2 = np.array([[[0.0, 7.0], [2.0, 3.0]], [[1.0, 9.0], [1.0, 2.0]]]) # Alan Turing's birthday
18+
mask = np.array([[[1, 0], [0, 1]], [[1, 0], [0, 1]]])
19+
20+
img1 = nib.Nifti1Image(volume1, np.eye(4))
21+
img2 = nib.Nifti1Image(volume2, np.eye(4))
22+
maskimg = nib.Nifti1Image(mask, np.eye(4))
23+
24+
nib.save(img1, os.path.join(tempdir, 'von.nii.gz'))
25+
nib.save(img2, os.path.join(tempdir, 'alan.nii.gz'))
26+
nib.save(maskimg, os.path.join(tempdir, 'mask.nii.gz'))
27+
28+
29+
# Default metric
30+
errmap = ErrorMap()
31+
errmap.inputs.in_tst = os.path.join(tempdir, 'von.nii.gz')
32+
errmap.inputs.in_ref = os.path.join(tempdir, 'alan.nii.gz')
33+
errmap.out_map = os.path.join(tempdir, 'out_map.nii.gz')
34+
result = errmap.run()
35+
yield assert_equal, result.outputs.distance, 1.125
36+
37+
# Square metric
38+
errmap.inputs.metric = 'sqeuclidean'
39+
result = errmap.run()
40+
yield assert_equal, result.outputs.distance, 1.125
41+
42+
# Linear metric
43+
errmap.inputs.metric = 'euclidean'
44+
result = errmap.run()
45+
yield assert_equal, result.outputs.distance, 0.875
46+
47+
# Masked
48+
errmap.inputs.mask = os.path.join(tempdir, 'mask.nii.gz')
49+
result = errmap.run()
50+
yield assert_equal, result.outputs.distance, 1.0
51+
52+
## Multi-Spectual
53+
volume3 = np.array([[[1.0, 6.0], [0.0, 3.0]], [[1.0, 9.0], [3.0, 6.0]]]) # Raymond Vahan Damadian's birthday
54+
55+
msvolume1 = np.zeros(shape=(2,2,2,2))
56+
msvolume1[:,:,:,0] = volume1
57+
msvolume1[:,:,:,1] = volume3
58+
msimg1 = nib.Nifti1Image(msvolume1, np.eye(4))
59+
60+
msvolume2 = np.zeros(shape=(2,2,2,2))
61+
msvolume2[:,:,:,0] = volume3
62+
msvolume2[:,:,:,1] = volume1
63+
msimg2 = nib.Nifti1Image(msvolume2, np.eye(4))
64+
65+
nib.save(msimg1, os.path.join(tempdir, 'von-ray.nii.gz'))
66+
nib.save(msimg2, os.path.join(tempdir, 'alan-ray.nii.gz'))
67+
68+
errmap.inputs.in_tst = os.path.join(tempdir, 'von-ray.nii.gz')
69+
errmap.inputs.in_ref = os.path.join(tempdir, 'alan-ray.nii.gz')
70+
errmap.inputs.metric = 'sqeuclidean'
71+
result = errmap.run()
72+
yield assert_equal, result.outputs.distance, 5.5
73+
74+
errmap.inputs.metric = 'euclidean'
75+
result = errmap.run()
76+
yield assert_equal, result.outputs.distance, np.float32(1.25 * (2**0.5))

0 commit comments

Comments
 (0)