Source code for menpofit.transform.homogeneous

import numpy as np

from menpo.transform import (Affine, AlignmentAffine, Similarity,
                             AlignmentSimilarity)

from menpofit.differentiable import DP, DX


[docs]class DifferentiableAffine(Affine, DP, DX): r""" Base class for an affine transformation that can compute its own derivative with respect to spatial changes, as well as its parametrisation. """
[docs] def d_dp(self, points): r""" The derivative with respect to the parametrisation changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dp : ``(n_points, n_parameters, n_dims)`` `ndarray` The Jacobian with respect to the parametrisation. ``d_dp[i, j, k]`` is the scalar differential change that the ``k``'th dimension of the ``i``'th point experiences due to a first order change in the ``j``'th scalar in the parametrisation vector. """ return affine_d_dp(self, points)
[docs] def d_dx(self, points): r""" The first order derivative with respect to spatial changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dx : ``(n_points, n_dims, n_dims)`` `ndarray` The Jacobian wrt spatial changes. ``d_dx[i, j, k]`` is the scalar differential change that the ``j``'th dimension of the ``i``'th point experiences due to a first order change in the ``k``'th dimension. It may be the case that the Jacobian is constant across space - in this case axis zero may have length ``1`` to allow for broadcasting. """ return affine_d_dx(self)
[docs]class DifferentiableAlignmentAffine(AlignmentAffine, DP, DX): r""" Base class that constructs an affine transformation that is the optimal transform to align the `source` to the `target`. It can compute its own derivative with respect to spatial changes, as well as its parametrisation. """
[docs] def as_non_alignment(self): r""" Returns the non-alignment version of the transform. :type: :map:`DifferentiableAffine` """ return DifferentiableAffine(self.h_matrix, skip_checks=True)
[docs] def d_dp(self, points): r""" The derivative with respect to the parametrisation changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dp : ``(n_points, n_parameters, n_dims)`` `ndarray` The Jacobian with respect to the parametrisation. ``d_dp[i, j, k]`` is the scalar differential change that the ``k``'th dimension of the ``i``'th point experiences due to a first order change in the ``j``'th scalar in the parametrisation vector. """ return affine_d_dp(self, points)
[docs] def d_dx(self, points): r""" The first order derivative with respect to spatial changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dx : ``(n_points, n_dims, n_dims)`` `ndarray` The Jacobian wrt spatial changes. ``d_dx[i, j, k]`` is the scalar differential change that the ``j``'th dimension of the ``i``'th point experiences due to a first order change in the ``k``'th dimension. It may be the case that the Jacobian is constant across space - in this case axis zero may have length ``1`` to allow for broadcasting. """ return affine_d_dx(self)
[docs]class DifferentiableSimilarity(Similarity, DP, DX): r""" Base class for a similarity transformation that can compute its own derivative with respect to spatial changes, as well as its parametrisation. """
[docs] def d_dp(self, points): r""" The derivative with respect to the parametrisation changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dp : ``(n_points, n_parameters, n_dims)`` `ndarray` The Jacobian with respect to the parametrisation. ``d_dp[i, j, k]`` is the scalar differential change that the ``k``'th dimension of the ``i``'th point experiences due to a first order change in the ``j``'th scalar in the parametrisation vector. """ return similarity_d_dp(self, points)
[docs] def d_dx(self, points): r""" The first order derivative with respect to spatial changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dx : ``(n_points, n_dims, n_dims)`` `ndarray` The Jacobian wrt spatial changes. ``d_dx[i, j, k]`` is the scalar differential change that the ``j``'th dimension of the ``i``'th point experiences due to a first order change in the ``k``'th dimension. It may be the case that the Jacobian is constant across space - in this case axis zero may have length ``1`` to allow for broadcasting. """ return affine_d_dx(self)
[docs]class DifferentiableAlignmentSimilarity(AlignmentSimilarity, DP, DX): r""" Base class that constructs a similarity transformation that is the optimal transform to align the `source` to the `target`. It can compute its own derivative with respect to spatial changes, as well as its parametrisation. """
[docs] def as_non_alignment(self): r""" Returns the non-alignment version of the transform. :type: :map:`DifferentiableSimilarity` """ return DifferentiableSimilarity(self.h_matrix, skip_checks=True)
[docs] def d_dp(self, points): r""" The derivative with respect to the parametrisation changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dp : ``(n_points, n_parameters, n_dims)`` `ndarray` The Jacobian with respect to the parametrisation. ``d_dp[i, j, k]`` is the scalar differential change that the ``k``'th dimension of the ``i``'th point experiences due to a first order change in the ``j``'th scalar in the parametrisation vector. """ return similarity_d_dp(self, points)
[docs] def d_dx(self, points): r""" The first order derivative with respect to spatial changes evaluated at points. Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dx : ``(n_points, n_dims, n_dims)`` `ndarray` The Jacobian wrt spatial changes. ``d_dx[i, j, k]`` is the scalar differential change that the ``j``'th dimension of the ``i``'th point experiences due to a first order change in the ``k``'th dimension. It may be the case that the Jacobian is constant across space - in this case axis zero may have length ``1`` to allow for broadcasting. """ return affine_d_dx(self)
def affine_d_dx(affine): r""" The first order derivative of an Affine transform with respect to spatial changes evaluated at points. The Jacobian for a given point (for 2D) is of the form:: Jx = [(1 + a), -b ] Jy = [ b, (1 + a)] J = [Jx, Jy] = [[(1 + a), -b], [b, (1 + a)]] where a and b come from: W(x;p) = [1 + a -b tx] [x] [b 1 + a ty] [y] [1] Hence it is simply the linear component of the transform. Returns ------- d_dx : ``(1, n_dims, n_dims)`` `ndarray` The Jacobian with respect to spatial changes. ``d_dx[0, j, k]`` is the scalar differential change that the ``j``'th dimension of the ``i``'th point experiences due to a first order change in the ``k``'th dimension. Note that because the Jacobian is constant across space the first axis is length ``1`` to allow for broadcasting. """ return affine.linear_component[None, ...] def affine_d_dp(self, points): r""" The first order derivative of this Affine transform wrt parameter changes evaluated at points. The Jacobian generated (for 2D) is of the form:: x 0 y 0 1 0 0 x 0 y 0 1 This maintains a parameter order of:: W(x;p) = [1 + p1 p3 p5] [x] [p2 1 + p4 p6] [y] [1] Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dp : ``(n_points, n_parameters, n_dims)`` `ndarray` The Jacobian with respect to the parametrisation. ``d_dp[i, j, k]`` is the scalar differential change that the ``k``'th dimension of the ``i``'th point experiences due to a first order change in the ``j``'th scalar in the parametrisation vector. """ n_points, points_n_dim = points.shape if points_n_dim != self.n_dims: raise ValueError( "Trying to sample jacobian in incorrect dimensions " "(transform is {0}D, sampling at {1}D)".format( self.n_dims, points_n_dim)) # prealloc the jacobian jac = np.zeros((n_points, self.n_parameters, self.n_dims)) # a mask that we can apply at each iteration dim_mask = np.eye(self.n_dims, dtype=np.bool) for i, s in enumerate( range(0, self.n_dims * self.n_dims, self.n_dims)): # i is current axis # s is slicing offset # make a mask for a single points jacobian full_mask = np.zeros((self.n_parameters, self.n_dims), dtype=bool) # fill the mask in for the ith axis full_mask[slice(s, s + self.n_dims)] = dim_mask # assign the ith axis points to this mask, broadcasting over all # points jac[:, full_mask] = points[:, i][..., None] # finally, just repeat the same but for the ones at the end full_mask = np.zeros((self.n_parameters, self.n_dims), dtype=bool) full_mask[slice(s + self.n_dims, s + 2 * self.n_dims)] = dim_mask jac[:, full_mask] = 1 return jac def similarity_d_dp(sim, points): r""" Computes the Jacobian of the transform w.r.t the parameters. The Jacobian generated (for 2D) is of the form:: x -y 1 0 y x 0 1 This maintains a parameter order of:: W(x;p) = [1 + a -b ] [x] + tx [b 1 + a] [y] + ty Parameters ---------- points : ``(n_points, n_dims)`` `ndarray` The spatial points at which the derivative should be evaluated. Returns ------- d_dp : ``(n_points, n_parameters, n_dims)`` `ndarray` The Jacobian with respect to the parametrisation. ``d_dp[i, j, k]`` is the scalar differential change that the ``k``'th dimension of the ``i``'th point experiences due to a first order change in the ``j``'th scalar in the parametrisation vector. Raises ------ DimensionalityError 'points.n_dims != self.n_dim' or transform is not 2D """ n_points, points_n_dim = points.shape if points_n_dim != sim.n_dims: raise ValueError('Trying to sample jacobian in incorrect ' 'dimensions (transform is {0}D, sampling ' 'at {1}D)'.format(sim.n_dims, points_n_dim)) elif sim.n_dims != 2: # TODO: implement 3D Jacobian raise ValueError("Only the Jacobian of a 2D similarity " "transform is currently supported.") # prealloc the jacobian jac = np.zeros((n_points, sim.n_parameters, sim.n_dims)) ones = np.ones_like(points) # Build a mask and apply it to the points to build the jacobian # Do this for each parameter - [a, b, tx, ty] respectively _apply_jacobian_mask(sim, jac, np.array([1, 1]), 0, points) _apply_jacobian_mask(sim, jac, np.array([-1, 1]), 1, points[:, ::-1]) _apply_jacobian_mask(sim, jac, np.array([1, 0]), 2, ones) _apply_jacobian_mask(sim, jac, np.array([0, 1]), 3, ones) return jac def _apply_jacobian_mask(sim, jac, param_mask, row_index, points): # make a mask for a single points jacobian full_mask = np.zeros((sim.n_parameters, sim.n_dims), dtype=np.bool) # fill the mask in for the ith axis full_mask[row_index] = [True, True] # assign the ith axis points to this mask, broadcasting over all # points jac[:, full_mask] = points * param_mask