11""" Shared utilities for registration methods """
22
3- import numpy as np
43from numpy import ndarray
54
65from thunder .rdds .images import Images
@@ -110,6 +109,7 @@ def zeroBorder(vol, border=1, cval=0.0):
110109 border: scalar or tuple containing borders for each dimension
111110 cval: constant value to replace boundary voxels with, default is 0.0
112111 """
112+ import numpy as np # sorry
113113 vol = vol .copy () # create new copy so we don't overwrite vol
114114 dims = np .array (vol .shape )
115115 if np .size (border ) == 1 :
@@ -130,62 +130,6 @@ def zeroBorder(vol, border=1, cval=0.0):
130130 return vol
131131
132132
133- def imageGradients (im , sigma = None ):
134- """Compute gradients of volume in each dimension using a Sobel filter.
135-
136- Parameters
137- ----------
138- im : ndarray
139- single volume
140- sigma : float or tuple, optional, default = None
141- smoothing amount to apply to volume before computing gradients
142- """
143- from scipy .ndimage .filters import gaussian_filter , sobel
144- if sigma is not None :
145- im = gaussian_filter (im , sigma )
146- grads = [sobel (im , axis = dim , mode = 'constant' ) / 8.0 for dim in xrange (im .ndim )]
147- return grads
148-
149-
150- def imageJacobian (vol , tfm , grid = None , sigma = None , normalize = True , border = 1 , order = 1 ):
151- """Compute Jacobian of volume w.r.t. transformation parameters
152-
153- Args:
154- I: volume
155- tfm: Transform object
156- sigma: smoothing bandwidth for gradients (None for no smoothing)
157- normalize: Whether to normalize images before aligning.
158- border: Number or tuple of border sizes to zero after transforming.
159- Returns:
160- It: vectorized version of transformed volume
161- J: nvoxels x nparameters Jacobian matrix
162- """
163- # Compute gradients
164- # Transform image and gradients
165- # Note: does not work if we compute gradients after transforming (borders?)
166- #It = zeroBorder(tfm.apply(I), border)
167- #TODO(ben): check that tfm then grad works
168- # It = transform_vol(I, tfm, grid, order=order)
169- # grads = [transform_vol(grad, tfm, grid, order=order) for grad in grads]
170- if grid is None :
171- grid = GridTransformer (vol .shape )
172- grads = imageGradients (vol , sigma )
173- It = tfm .apply (vol , grid , order = order )
174- It = zeroBorder (It , border )
175- grads = [tfm .apply (grad , grid , order = order ) for grad in grads ]
176- grads = [zeroBorder (grad , border ) for grad in grads ]
177-
178- if normalize :
179- normI = np .linalg .norm (It .ravel ())
180- if normI == 0.0 :
181- raise ValueError ('Transform yields volume of zeroes.' )
182- grads = [(1. / normI ) * I - (I * It ).sum () / (normI ** 3 ) * It for I in grads ]
183- It /= normI
184-
185- jacobianVols = tfm .jacobian (grads , grid .homo_points )
186- return It , jacobianVols
187-
188-
189133def solveLinearized (vec , jacobian , reference , robust = False ):
190134 """Solve linearized registration problem for change in transformation parameters and weighting on reference basis.
191135
@@ -214,22 +158,100 @@ def solveLinearized(vec, jacobian, reference, robust=False):
214158 model = QuantReg (vec , A ).fit (q = quantile )
215159 params = model .params
216160 else :
217- model = np .linalg .lstsq (A , vec )
161+ from numpy .linalg import lstsq
162+ model = lstsq (A , vec )
218163 params = model [0 ]
219- deltaTransform , coeff = np .split (params , [jacobian .shape [1 ]])
164+ from numpy import split
165+ deltaTransform , coeff = split (params , [jacobian .shape [1 ]])
220166 return deltaTransform , coeff
221167
222168
223- def volumesToMatrix (vols ):
224- if not isinstance (vols , list ):
225- return v2v (vols )
226- else :
227- return np .column_stack ([v2v (v ) for v in vols ]).squeeze ()
228-
229169def v2v (v , dims = None ):
170+ """Convert vector to volume or volume to vector.
171+
172+ Parameters
173+ ----------
174+ v : array
175+ volume or vectorized version of volume
176+ dims : tuple, optional
177+ shape of volume, must be set to reshape vectors into volumes
178+
179+ Returns
180+ -------
181+ volume if input is a vector, and vector if input is a volume
182+ """
230183 if v .ndim == 1 :
184+ from numpy import prod
231185 assert dims
232186 assert v .size == np .prod (dims )
233187 return v .reshape (dims )
234188 else :
235- return v .ravel ()
189+ return v .ravel ()
190+
191+
192+ def volumesToMatrix (vols ):
193+ """Convert list of volumes to a matrix.
194+
195+ Parameters
196+ ----------
197+ vols : list of arrays
198+
199+ Returns
200+ -------
201+ array with size nvoxels by number of volumes
202+ """
203+ from numpy import column_stack
204+ if not isinstance (vols , list ):
205+ return v2v (vols )
206+ else :
207+ return column_stack ([v2v (v ) for v in vols ]).squeeze ()
208+
209+
210+ def imageGradients (im , sigma = None ):
211+ """Compute gradients of volume in each dimension using a Sobel filter.
212+
213+ Parameters
214+ ----------
215+ im : ndarray
216+ single volume
217+ sigma : float or tuple, optional, default = None
218+ smoothing amount to apply to volume before computing gradients
219+ """
220+ from scipy .ndimage .filters import gaussian_filter , sobel
221+ if sigma is not None :
222+ im = gaussian_filter (im , sigma )
223+ grads = [sobel (im , axis = dim , mode = 'constant' ) / 8.0 for dim in xrange (im .ndim )]
224+ return grads
225+
226+
227+ def imageJacobian (vol , tfm , grid = None , sigma = None , normalize = True , border = 1 , order = 1 ):
228+ """Compute Jacobian of volume w.r.t. transformation parameters
229+
230+ Args:
231+ vol: volume
232+ tfm: Transform object
233+ sigma: smoothing bandwidth for gradients (None for no smoothing)
234+ normalize: Whether to normalize images before aligning.
235+ border: Number or tuple of border sizes to zero after transforming.
236+ order: interpolation order used by map_coordinates
237+ Returns:
238+ tvol : array
239+ transformed volume
240+ jacobianVols : list of arrays
241+ list of volume Jacobians, one for each parameter of the transformation
242+ """
243+ if grid is None :
244+ from thunder .imgprocessing .transformation import GridTransformer
245+ grid = GridTransformer (vol .shape )
246+ grads = imageGradients (vol , sigma )
247+ tvol = zeroBorder (tfm .apply (vol , grid , order = order ))
248+ grads = [zeroBorder (tfm .apply (grad , grid , order = order )) for grad in grads ]
249+ if normalize :
250+ norm = np .linalg .norm (tvol .ravel ())
251+ if norm == 0.0 :
252+ raise ValueError ('Transform yields volume of zeroes.' )
253+ # Update gradients to reflect normalization
254+ grads = [grad / norm - (grad * tvol ).sum () / (norm ** 3 ) * tvol for grad in grads ]
255+ tvol /= norm
256+ jacobianVols = tfm .jacobian (grads , grid .homo_points )
257+ return tvol , jacobianVols
0 commit comments