13
13
14
14
15
15
import numpy as np
16
+ from numpy .linalg import norm
16
17
import os .path as op
17
- from scipy . spatial . distance import euclidean
18
+ from nipype . external import six
18
19
19
20
from .. import logging
20
21
@@ -79,14 +80,16 @@ class ComputeMeshWarp(BaseInterface):
79
80
output_spec = ComputeMeshWarpOutputSpec
80
81
81
82
def _triangle_area (self , A , B , C ):
82
- ABxAC = euclidean (A , B ) * euclidean (A , C )
83
- prod = np .dot (np .array (B ) - np .array (A ), np .array (C ) - np .array (A ))
83
+ A = np .array (A )
84
+ B = np .array (B )
85
+ C = np .array (C )
86
+ ABxAC = norm (A - B ) * norm (A - C )
87
+ prod = np .dot (B - A , C - A )
84
88
angle = np .arccos (prod / ABxAC )
85
89
area = 0.5 * ABxAC * np .sin (angle )
86
90
return area
87
91
88
92
def _run_interface (self , runtime ):
89
- from numpy import linalg as nla
90
93
try :
91
94
from tvtk .api import tvtk
92
95
except ImportError :
@@ -116,7 +119,7 @@ def _run_interface(self, runtime):
116
119
diff = points2 - points1
117
120
weights = np .ones (len (diff ))
118
121
119
- errvector = nla . norm (diff , axis = 1 )
122
+ errvector = norm (diff , axis = 1 )
120
123
if self .inputs .metric == 'sqeuclidean' :
121
124
errvector = errvector ** 2
122
125
@@ -147,7 +150,6 @@ def _run_interface(self, runtime):
147
150
file_name = op .abspath (self .inputs .out_warp ))
148
151
writer .set_input_data (out_mesh )
149
152
writer .write ()
150
- #write_data(out_mesh, op.abspath(self.inputs.out_warp))
151
153
152
154
self ._distance = np .average (errvector , weights = weights )
153
155
return runtime
@@ -160,6 +162,144 @@ def _list_outputs(self):
160
162
return outputs
161
163
162
164
165
+ class MeshWarpMathsInputSpec (BaseInterfaceInputSpec ):
166
+ in_surf = File (exists = True , mandatory = True ,
167
+ desc = ('Input surface in vtk format, with associated warp '
168
+ 'field as point data (ie. from ComputeMeshWarp' ))
169
+ float_trait = traits .Either (traits .Float (1.0 ), traits .Tuple (
170
+ traits .Float (1.0 ), traits .Float (1.0 ), traits .Float (1.0 )))
171
+
172
+ operator = traits .Either (
173
+ float_trait , File (exists = True ), default = 1.0 , mandatory = True ,
174
+ desc = ('image, float or tuple of floats to act as operator' ))
175
+
176
+ operation = traits .Enum ('sum' , 'sub' , 'mul' , 'div' , usedefault = True ,
177
+ desc = ('operation to be performed' ))
178
+
179
+ out_warp = File ('warp_maths.vtk' , usedefault = True ,
180
+ desc = 'vtk file based on in_surf and warpings mapping it '
181
+ 'to out_file' )
182
+ out_file = File ('warped_surf.vtk' , usedefault = True ,
183
+ desc = 'vtk with surface warped' )
184
+
185
+
186
+ class MeshWarpMathsOutputSpec (TraitedSpec ):
187
+ out_warp = File (exists = True , desc = ('vtk file with the vertex-wise '
188
+ 'mapping of surface1 to surface2' ))
189
+ out_file = File (exists = True ,
190
+ desc = 'vtk with surface warped' )
191
+
192
+
193
+ class MeshWarpMaths (BaseInterface ):
194
+
195
+ """
196
+ Performs the most basic mathematical operations on the warping field
197
+ defined at each vertex of the input surface. A surface with scalar
198
+ or vector data can be used as operator for non-uniform operations.
199
+
200
+ .. warning:
201
+
202
+ A point-to-point correspondence between surfaces is required
203
+
204
+
205
+ Example
206
+ -------
207
+
208
+ >>> import nipype.algorithms.mesh as m
209
+ >>> mmath = m.MeshWarpMaths()
210
+ >>> mmath.inputs.in_surf = 'surf1.vtk'
211
+ >>> mmath.inputs.operator = 'surf2.vtk'
212
+ >>> mmath.inputs.operation = 'mul'
213
+ >>> res = mmath.run() # doctest: +SKIP
214
+
215
+ """
216
+
217
+ input_spec = MeshWarpMathsInputSpec
218
+ output_spec = MeshWarpMathsOutputSpec
219
+
220
+ def _run_interface (self , runtime ):
221
+ try :
222
+ from tvtk .api import tvtk
223
+ except ImportError :
224
+ raise ImportError ('Interface ComputeMeshWarp requires tvtk' )
225
+
226
+ try :
227
+ from enthought .etsconfig .api import ETSConfig
228
+ ETSConfig .toolkit = 'null'
229
+ except ImportError :
230
+ iflogger .warn (('ETS toolkit could not be imported' ))
231
+ pass
232
+ except ValueError :
233
+ iflogger .warn (('ETS toolkit is already set' ))
234
+ pass
235
+
236
+ r1 = tvtk .PolyDataReader (file_name = self .inputs .in_surf )
237
+ vtk1 = r1 .output
238
+ r1 .update ()
239
+ points1 = np .array (vtk1 .points )
240
+
241
+ if vtk1 .point_data .vectors is None :
242
+ raise RuntimeError (('No warping field was found in in_surf' ))
243
+
244
+ operator = self .inputs .operator
245
+ opfield = np .ones_like (points1 )
246
+
247
+ if isinstance (operator , six .string_types ):
248
+ r2 = tvtk .PolyDataReader (file_name = self .inputs .surface2 )
249
+ vtk2 = r2 .output
250
+ r2 .update ()
251
+ assert (len (points1 ) == len (vtk2 .points ))
252
+
253
+ opfield = vtk2 .point_data .vectors
254
+
255
+ if opfield is None :
256
+ opfield = vtk2 .point_data .scalars
257
+
258
+ if opfield is None :
259
+ raise RuntimeError (
260
+ ('No operator values found in operator file' ))
261
+
262
+ opfield = np .array (opfield )
263
+
264
+ if opfield .shape [1 ] < points1 .shape [1 ]:
265
+ opfield = np .array ([opfield .tolist ()] * points1 .shape [1 ]).T
266
+ else :
267
+ operator = np .atleast_1d (operator )
268
+ opfield *= operator
269
+
270
+ warping = np .array (vtk1 .point_data .vectors )
271
+
272
+ if self .inputs .operation == 'sum' :
273
+ warping += opfield
274
+ elif self .inputs .operation == 'sub' :
275
+ warping -= opfield
276
+ elif self .inputs .operation == 'mul' :
277
+ warping *= opfield
278
+ elif self .inputs .operation == 'div' :
279
+ warping /= opfield
280
+
281
+ vtk1 .point_data .vectors = warping
282
+ writer = tvtk .PolyDataWriter (
283
+ file_name = op .abspath (self .inputs .out_warp ))
284
+ writer .set_input_data (vtk1 )
285
+ writer .write ()
286
+
287
+ vtk1 .point_data .vectors = None
288
+ vtk1 .points = points1 + warping
289
+ writer = tvtk .PolyDataWriter (
290
+ file_name = op .abspath (self .inputs .out_file ))
291
+ writer .set_input_data (vtk1 )
292
+ writer .write ()
293
+
294
+ return runtime
295
+
296
+ def _list_outputs (self ):
297
+ outputs = self ._outputs ().get ()
298
+ outputs ['out_file' ] = op .abspath (self .inputs .out_file )
299
+ outputs ['out_warp' ] = op .abspath (self .inputs .out_warp )
300
+ return outputs
301
+
302
+
163
303
class P2PDistance (ComputeMeshWarp ):
164
304
165
305
"""
0 commit comments