1313
1414
1515import numpy as np
16+ from numpy .linalg import norm
1617import os .path as op
17- from scipy . spatial . distance import euclidean
18+ from nipype . external import six
1819
1920from .. import logging
2021
@@ -79,14 +80,16 @@ class ComputeMeshWarp(BaseInterface):
7980 output_spec = ComputeMeshWarpOutputSpec
8081
8182 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 )
8488 angle = np .arccos (prod / ABxAC )
8589 area = 0.5 * ABxAC * np .sin (angle )
8690 return area
8791
8892 def _run_interface (self , runtime ):
89- from numpy import linalg as nla
9093 try :
9194 from tvtk .api import tvtk
9295 except ImportError :
@@ -116,7 +119,7 @@ def _run_interface(self, runtime):
116119 diff = points2 - points1
117120 weights = np .ones (len (diff ))
118121
119- errvector = nla . norm (diff , axis = 1 )
122+ errvector = norm (diff , axis = 1 )
120123 if self .inputs .metric == 'sqeuclidean' :
121124 errvector = errvector ** 2
122125
@@ -147,7 +150,6 @@ def _run_interface(self, runtime):
147150 file_name = op .abspath (self .inputs .out_warp ))
148151 writer .set_input_data (out_mesh )
149152 writer .write ()
150- #write_data(out_mesh, op.abspath(self.inputs.out_warp))
151153
152154 self ._distance = np .average (errvector , weights = weights )
153155 return runtime
@@ -160,6 +162,144 @@ def _list_outputs(self):
160162 return outputs
161163
162164
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+
163303class P2PDistance (ComputeMeshWarp ):
164304
165305 """
0 commit comments