22from renderapi .errors import RenderError , EstimationError
33from renderapi .utils import encodeBase64 , decodeBase64
44from .transform import Transform
5+ import scipy .spatial
56__all__ = ['ThinPlateSplineTransform' ]
67
78
@@ -81,47 +82,38 @@ def tform(self, points):
8182 numpy.array
8283 a Nx2 array of x,y points after transformation
8384 """
84- result = []
85- for pt in points :
86- result .append (self .apply (pt ))
85+ return self .apply (points )
8786
88- return np .array (result )
89-
90- def apply (self , pt ):
87+ def apply (self , points ):
9188 if not hasattr (self , 'dMtxDat' ):
92- result = pt
93- return result
89+ return points
9490
95- result = pt + self .computeDeformationContribution (pt )
91+ result = points + self .computeDeformationContribution (points )
9692 if self .aMtx is not None :
97- result += self .aMtx .dot (pt )
93+ result += self .aMtx .dot (points . transpose ()). transpose ( )
9894 if self .bVec is not None :
9995 result += self .bVec
10096
10197 return result
10298
103- def computeDeformationContribution (self , pt ):
104- disp = np .linalg .norm (
105- self .srcPts -
106- pt .reshape (self .ndims , 1 ),
107- axis = 0 )
108- nrm = np .zeros_like (disp )
109- ind = disp > 1e-8
110- nrm [ind ] = disp [ind ] * disp [ind ] * np .log (disp [ind ])
111- result = (nrm * self .dMtxDat ).sum (1 )
112- return result
99+ def computeDeformationContribution (self , points ):
100+ disp = scipy .spatial .distance .cdist (
101+ points ,
102+ self .srcPts .transpose ())
103+ disp = np .power (disp , 2.0 ) * np .ma .log (disp ).filled (0.0 )
104+ return disp .dot (self .dMtxDat .transpose ())
113105
114106 def gradient_descent (
115107 self ,
116- pt ,
108+ pts ,
117109 gamma = 1.0 ,
118110 precision = 0.0001 ,
119111 max_iters = 1000 ):
120112 """based on https://en.wikipedia.org/wiki/Gradient_descent#Python
121113 Parameters
122114 ----------
123- pt : numpy array
124- [x,y] point for estimating inverse
115+ pts : numpy array
116+ a Nx2 array of x,y points
125117 gamma : float
126118 step size is gamma fraction of current gradient
127119 precision : float
@@ -130,23 +122,23 @@ def gradient_descent(
130122 limit for iterations, error if reached
131123 Returns
132124 -------
133- cur_pt : numpy array
134- [ x,y] point , estimated inverse of pt
125+ cur_pts : numpy array
126+ a Nx2 array of x,y points , estimated inverse of pt
135127 """
136- cur_pt = np .copy (pt )
137- prev_pt = np .copy (pt )
128+ cur_pts = np .copy (pts )
129+ prev_pts = np .copy (pts )
138130 step_size = 1
139131 iters = 0
140132 while (step_size > precision ) & (iters < max_iters ):
141- prev_pt [: ] = cur_pt [ :]
142- cur_pt -= gamma * (self .apply (prev_pt ) - pt )
143- step_size = np .linalg .norm (cur_pt - prev_pt )
133+ prev_pts [:, : ] = cur_pts [:, :]
134+ cur_pts -= gamma * (self .apply (prev_pts ) - pts )
135+ step_size = np .linalg .norm (cur_pts - prev_pts , axis = 1 ). max ( )
144136 iters += 1
145137 if iters == max_iters :
146138 raise EstimationError (
147139 'gradient descent for inversion of ThinPlateSpline '
148140 'reached maximum iterations: %d' % max_iters )
149- return cur_pt
141+ return cur_pts
150142
151143 def inverse_tform (
152144 self ,
@@ -170,15 +162,12 @@ def inverse_tform(
170162 numpy.array
171163 a Nx2 array of x,y points after inverse transformation
172164 """
173- newpts = []
174- for p in points :
175- npt = self .gradient_descent (
176- p ,
177- gamma = gamma ,
178- precision = precision ,
179- max_iters = max_iters )
180- newpts .append (npt )
181- return np .array (newpts )
165+ newpts = self .gradient_descent (
166+ points ,
167+ gamma = gamma ,
168+ precision = precision ,
169+ max_iters = max_iters )
170+ return newpts
182171
183172 @staticmethod
184173 def fit (A , B , computeAffine = True ):
0 commit comments