Source code for pygem.utils

"""
Utilities for the affine transformations of the bounding box of the Free Form
Deformation.
"""
import math
from functools import reduce
import numpy as np


[docs] def angles2matrix(rot_z=0, rot_y=0, rot_x=0): """ This method returns the rotation matrix for given rotations around z, y and x axes. The output rotation matrix is equal to the composition of the individual rotations. Rotations are counter-clockwise. The default value of the three rotations is zero. :param float rot_z: rotation angle (in radians) around z-axis. :param float rot_y: rotation angle (in radians) around y-axis. :param float rot_x: rotation angle (in radians) around x-axis. :return: rot_matrix: rotation matrix for the given angles. The matrix shape is always (3, 3). :rtype: numpy.ndarray :Example: >>> import pygem.affine as at >>> import numpy as np >>> from math import radians >>> # Example of a rotation around x, y, z axis >>> rotz = radians(10) >>> roty = radians(20) >>> rotx = radians(30) >>> rot_matrix = at.angles2matrix(rotz, roty, rotx) .. note:: - The direction of rotation is given by the right-hand rule. - When applying the rotation to a vector, the vector should be column vector to the right of the rotation matrix. """ rot_matrix = [] if rot_z: cos = math.cos(rot_z) sin = math.sin(rot_z) rot_matrix.append( np.array([cos, -sin, 0, sin, cos, 0, 0, 0, 1]).reshape((3, 3))) if rot_y: cos = math.cos(rot_y) sin = math.sin(rot_y) rot_matrix.append( np.array([cos, 0, sin, 0, 1, 0, -sin, 0, cos]).reshape((3, 3))) if rot_x: cos = math.cos(rot_x) sin = math.sin(rot_x) rot_matrix.append( np.array([1, 0, 0, 0, cos, -sin, 0, sin, cos]).reshape((3, 3))) if rot_matrix: return reduce(np.dot, rot_matrix[::-1]) return np.eye(3)
[docs] def fit_affine_transformation(points_start, points_end): """ Fit an affine transformation from starting points to ending points through a least square procedure. :param numpy.ndarray points_start: set of starting points. :param numpy.ndarray points_end: set of ending points. :return: transform_vector: function that transforms a vector according to the affine map. It takes a source vector and return a vector transformed by the reduced row echelon form of the map. :rtype: function :Example: >>> import pygem.affine as at >>> # Example of a rotation (affine transformation) >>> p_start = np.array([[1,0,0], [0,1,0], [0,0,1], [0,0,0]]) >>> p_end = np.array([[0,1,0], [-1,0,0], [0,0,1], [0,0,0]]) >>> v_test = np.array([1., 2., 3.]) >>> transformation = at.affine_points_fit(p_start, p_end) >>> v_trans = transformation(v_test) """ if len(points_start) != len(points_end): raise RuntimeError("points_start and points_end must be of same size.") dim = len(points_start[0]) if len(points_start) < dim: raise RuntimeError( "Too few starting points => under-determined system.") def pad_column_ones(x): """ Add right column of 1.0 to the given 2D numpy array """ return np.hstack([x, np.ones((x.shape[0], 1))]) def unpad_column(x): """ Remove last column to the given 2D numpy array """ return x[:, :-1] def transform(src): shape = src.shape X = pad_column_ones(points_start) Y = pad_column_ones(points_end) A, res, rank, _ = np.linalg.lstsq(X, Y, rcond=None) # TODO add check condition number #if np.linalg.cond(A) >= 1 / sys.float_info.epsilon: # raise RuntimeError( # "Error: singular matrix. Points are probably coplanar.") return unpad_column( np.dot( pad_column_ones(np.atleast_2d(src)), A) ).reshape(shape) return transform