Skip to content

Commit 832b5f3

Browse files
committed
Merge branch 'enh/MeshAlgorithm'
Conflicts: CHANGES
2 parents 4da4516 + d395eb4 commit 832b5f3

File tree

4 files changed

+114
-1
lines changed

4 files changed

+114
-1
lines changed

CHANGES

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Next release
33

44
* ENH: SelectFiles: a streamlined version of DataGrabber
55
* ENH: New interfaces: spm.ResliceToReference, FuzzyOverlap, afni.AFNItoNIFTI
6-
spm.DicomImport
6+
spm.DicomImport, P2PDistance
77
* ENH: W3C PROV support with optional RDF export built into Nipype
88

99
* ENH: Several new interfaces related to Camino were added:

nipype/algorithms/mesh.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
'''
4+
Miscellaneous algorithms for 2D contours and 3D triangularized meshes handling
5+
6+
Change directory to provide relative paths for doctests
7+
>>> import os
8+
>>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
9+
>>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
10+
>>> os.chdir(datadir)
11+
12+
'''
13+
14+
15+
import os
16+
import os.path as op
17+
import warnings
18+
import nibabel as nb
19+
import numpy as np
20+
from scipy.spatial.distance import euclidean
21+
from nipype.utils.misc import package_check
22+
23+
try:
24+
package_check('tvtk')
25+
except Exception, e:
26+
warnings.warn('tvtk or vtk not installed')
27+
else:
28+
from tvtk.api import tvtk
29+
30+
31+
from .. import logging
32+
33+
from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File,
34+
InputMultiPath, OutputMultiPath,
35+
BaseInterfaceInputSpec, isdefined)
36+
from ..utils.filemanip import fname_presuffix, split_filename
37+
iflogger = logging.getLogger('interface')
38+
39+
40+
class P2PDistanceInputSpec(BaseInterfaceInputSpec):
41+
surface1 = File(exists=True, mandatory=True,
42+
desc="Reference surface (vtk format) to which compute distance.")
43+
surface2 = File(exists=True, mandatory=True,
44+
desc="Test surface (vtk format) from which compute distance.")
45+
weighting = traits.Enum("none", "surface", desc='""none": no weighting is performed\
46+
"surface": edge distance is weighted by the corresponding surface area',usedefault=True)
47+
48+
class P2PDistanceOutputSpec(TraitedSpec):
49+
distance = traits.Float(desc="computed distance")
50+
51+
class P2PDistance(BaseInterface):
52+
"""
53+
Calculates a point-to-point (p2p) distance between two corresponding meshes or contours.
54+
Therefore, a point-to-point correspondence between nodes is required
55+
56+
Example
57+
-------
58+
59+
>>> import nipype.algorithms.mesh as mesh
60+
>>> dist = mesh.P2PDistance()
61+
>>> dist.inputs.surface1 = 'surf1.vtk'
62+
>>> dist.inputs.surface2 = 'surf2.vtk'
63+
>>> res = dist.run() # doctest: +SKIP
64+
"""
65+
66+
input_spec = P2PDistanceInputSpec
67+
output_spec = P2PDistanceOutputSpec
68+
69+
def _triangle_area(self, A, B, C):
70+
ABxAC = euclidean(A,B) * euclidean(A,C)
71+
prod = np.dot(np.array(B)-np.array(A),np.array(C)-np.array(A))
72+
angle = np.arccos( prod / ABxAC )
73+
area = 0.5 * ABxAC * np.sin( angle )
74+
return area
75+
76+
def _run_interface(self, runtime):
77+
r1 = tvtk.PolyDataReader( file_name=self.inputs.surface1 )
78+
r2 = tvtk.PolyDataReader( file_name=self.inputs.surface2 )
79+
vtk1 = r1.output
80+
vtk2 = r2.output
81+
r1.update()
82+
r2.update()
83+
assert( len(vtk1.points) == len(vtk2.points) )
84+
d = 0.0
85+
totalWeight = 0.0
86+
87+
points = vtk1.points
88+
faces = vtk1.polys.to_array().reshape(-1,4).astype(int)[:,1:]
89+
90+
for p1,p2 in zip( points, vtk2.points ):
91+
weight = 1.0
92+
if (self.inputs.weighting == 'surface'):
93+
#compute surfaces, set in weight
94+
weight = 0.0
95+
point_faces = faces[ (faces[:,:]==0).any(axis=1) ]
96+
97+
for idset in point_faces:
98+
p1 = points[ int(idset[0]) ]
99+
p2 = points[ int(idset[1]) ]
100+
p3 = points[ int(idset[2]) ]
101+
weight = weight + self._triangle_area(p1, p2, p3)
102+
103+
d+= weight*euclidean( p1, p2 )
104+
totalWeight = totalWeight + weight
105+
106+
self._distance = d / totalWeight
107+
return runtime
108+
109+
def _list_outputs(self):
110+
outputs = self._outputs().get()
111+
outputs['distance'] = self._distance
112+
return outputs
113+

nipype/testing/data/surf1.vtk

Whitespace-only changes.

nipype/testing/data/surf2.vtk

Whitespace-only changes.

0 commit comments

Comments
 (0)