Skip to content

Commit d30ac81

Browse files
committed
even more doc and cleanup
1 parent 750e070 commit d30ac81

File tree

2 files changed

+49
-17
lines changed

2 files changed

+49
-17
lines changed

python/thunder/imgprocessing/regmethods/lucaskanade.py

+40-12
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,52 @@
1-
""" Registration methods based on cross correlation """
1+
""" Registration methods based on Lucas-Kanade registration"""
22

3-
from numpy import ndarray
4-
import numpy as np
3+
from numpy import array, ndarray, inf, zeros
54

65
from thunder.rdds.images import Images
76
from thunder.imgprocessing.registration import RegistrationMethod
8-
#from thunder.imgprocessing.regmethods.utils import computeDisplacement, computeReferenceMean, checkReference
97
from thunder.imgprocessing.transformation import GridTransformer, TRANSFORMATION_TYPES
108
from thunder.imgprocessing.regmethods.utils import volumesToMatrix, imageJacobian, solveLinearized, computeReferenceMean, checkReference
119

1210

1311
class LucasKanadeRegistration(RegistrationMethod):
14-
def __init__(self, maxIter=10, transformationType='Translation', tol=1e-3, robust=False, border=0):
15-
# self.jacobian_args = kwargs
16-
self.maxIter = maxIter
12+
"""Lucas-Kanade registration method.
13+
14+
Lucas-Kanade (LK) is an iterative algorithm for aligning an image to a reference. It aims to minimize the squared
15+
error between the transformed image and the reference. As the relationship between transformation parameters and
16+
transformed pixels is nonlinear, we have to perform a series of linearizations similar to Levenberg-Marquardt.
17+
At each iteration, we compute the Jacobian of the output image with respect to the input parameters, and solve a
18+
least squares problem to identify a change in parameters. We update the parameters and repeat.
19+
20+
To increase robustness, we extend the traditional LK algorithm to use a set of reference images.
21+
We minimize the squared error of the difference of the transformed image and a learned weighting of the references.
22+
"""
23+
24+
def __init__(self, transformationType='Translation', border=0, tol=1e-5, maxIter=10, robust=False):
25+
"""
26+
Parameters
27+
----------
28+
transformationType : one of 'Translation', 'Euclidean', optional, default = 'Translation'
29+
type of transformation to use
30+
border : int or tuple, optional, default = 0
31+
Border to be zeroed out after transformations. For most datasets, it is
32+
critical that this value be larger than the maximum translation to get
33+
good results and avoid boundary artifacts.
34+
maxIter : int, optional, default = 10
35+
maximum number of iterations
36+
tol : float, optional, default = 1e-5
37+
stopping criterion on the L2 norm of the change in parameters
38+
robust : bool, optional, default = False
39+
solve a least absolute deviation problem instead of least squares
40+
"""
1741
self.transformationType = transformationType
42+
self.border = border
43+
self.maxIter = maxIter
1844
self.tol = tol
1945
self.robust = robust
20-
self.border = border
2146

2247
def prepare(self, images, startidx=None, stopidx=None):
2348
"""
24-
Prepare Lucas Kanade registration by computing or specifying a reference image.
49+
Prepare Lucas-Kanade registration by computing or specifying a reference image.
2550
2651
Parameters
2752
----------
@@ -36,6 +61,7 @@ def prepare(self, images, startidx=None, stopidx=None):
3661
self.reference = images
3762
else:
3863
raise Exception('Must provide either an Images object or a reference')
64+
# Convert references to matrix to speed up solving linearized system
3965
self.referenceMat = volumesToMatrix(self.reference)
4066
return self
4167

@@ -52,15 +78,17 @@ def isPrepared(self, images):
5278
checkReference(self.reference, images)
5379

5480
def getTransform(self, vol):
81+
from numpy.linalg import norm
5582
# Create initial transformation
56-
tfm = TRANSFORMATION_TYPES[self.transformationType](shift=np.zeros(vol.ndim))
83+
tfm = TRANSFORMATION_TYPES[self.transformationType](shift=zeros(vol.ndim))
84+
# Grid of coordinates needed to transform images using map_coordinates
5785
grid = GridTransformer(vol.shape)
5886
iter = 0
59-
normDelta = np.inf
87+
normDelta = inf
6088
while iter < self.maxIter and normDelta > self.tol:
6189
iter += 1
6290
volTfm, jacobian = imageJacobian(vol, tfm, grid, border=self.border)
6391
deltaTransformParams, coeff = solveLinearized(volumesToMatrix(volTfm), volumesToMatrix(jacobian), self.referenceMat, self.robust)
6492
tfm.updateParams(-deltaTransformParams)
65-
normDelta = np.linalg.norm(-deltaTransformParams)
93+
normDelta = norm(deltaTransformParams)
6694
return tfm

python/thunder/imgprocessing/regmethods/utils.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ def solveLinearized(vec, jacobian, reference, robust=False):
151151
coeff : array, shape (nbasis,)
152152
optimal weighting of reference volumes
153153
"""
154-
A = np.column_stack((jacobian, reference))
154+
from numpy import column_stack
155+
A = column_stack((jacobian, reference))
155156
if robust:
156157
from statsmodels.regression.quantile_regression import QuantReg
157158
quantile = 0.5
@@ -240,18 +241,21 @@ def imageJacobian(vol, tfm, grid=None, sigma=None, normalize=True, border=1, ord
240241
jacobianVols : list of arrays
241242
list of volume Jacobians, one for each parameter of the transformation
242243
"""
244+
from numpy.linalg import norm
243245
if grid is None:
244246
from thunder.imgprocessing.transformation import GridTransformer
245247
grid = GridTransformer(vol.shape)
246248
grads = imageGradients(vol, sigma)
247249
tvol = zeroBorder(tfm.apply(vol, grid, order=order))
250+
normVol = norm(tvol.ravel())
251+
if normVol == 0.0:
252+
raise ValueError('Transform yields volume of zeroes.')
248253
grads = [zeroBorder(tfm.apply(grad, grid, order=order)) for grad in grads]
249254
if normalize:
250-
norm = np.linalg.norm(tvol.ravel())
251-
if norm == 0.0:
255+
if normVol == 0.0:
252256
raise ValueError('Transform yields volume of zeroes.')
253257
# Update gradients to reflect normalization
254-
grads = [grad / norm - (grad * tvol).sum() / (norm**3) * tvol for grad in grads]
255-
tvol /= norm
258+
grads = [grad / normVol - (grad * tvol).sum() / (normVol**3) * tvol for grad in grads]
259+
tvol /= normVol
256260
jacobianVols = tfm.jacobian(grads, grid.homo_points)
257261
return tvol, jacobianVols

0 commit comments

Comments
 (0)