import numpy as np
np.set_printoptions(linewidth=500, precision=3)
from menpo.transform import Rotation, Scale, Translation
rotation_m = np.array([[ 0.36, 0.48, -0.80],
[-0.80, 0.60, 0.00],
[ 0.48, 0.64, 0.60]])
scale = np.array([7,3, 15])
translation = np.array([-1,20])
r = Rotation(rotation_m)
nus = Scale([7,3, 15]) # NonUniformScale (Affine)
us = Scale(2, n_dims=3) # UniformScale (Similarity)
t = Translation([-1,20])
All affine transforms are printable. You will see the usual homogeneous matrix, and an English decription of what the affine transform does
print(r)
<menpo.transform.homogeneous.rotation.Rotation object at 0x0000000004C57BE0> [[ 0.36 0.48 -0.8 0. ] [-0.8 0.6 0. 0. ] [ 0.48 0.64 0.6 0. ] [ 0. 0. 0. 1. ]] CCW Rotation of -73 degrees about [-0.333 0.667 0.667]
print(nus)
<menpo.transform.homogeneous.scale.NonUniformScale object at 0x0000000002F907B8> [[ 7. 0. 0. 0.] [ 0. 3. 0. 0.] [ 0. 0. 15. 0.] [ 0. 0. 0. 1.]] NonUniformScale by [ 7. 3. 15.]
print(t)
<menpo.transform.homogeneous.translation.Translation object at 0x0000000002F90AC8> [[ 1. 0. -1.] [ 0. 1. 20.] [ 0. 0. 1.]] Translate by [ -1. 20.]
All these are instances of DiscreteAffine
. That means we can ask any of these to invert themselves
print(us)
print(us.pseudoinverse)
<menpo.transform.homogeneous.scale.UniformScale object at 0x0000000004CDA748> [[ 2. 0. 0. 0.] [ 0. 2. 0. 0.] [ 0. 0. 2. 0.] [ 0. 0. 0. 1.]] UniformScale by 2.000000 <menpo.transform.homogeneous.scale.UniformScale object at 0x00000000042E5BA8> [[ 0.5 0. 0. 0. ] [ 0. 0.5 0. 0. ] [ 0. 0. 0.5 0. ] [ 0. 0. 0. 1. ]] UniformScale by 0.500000
All affine transforms can be chained together using the chain(another_affine_transform)
method. This produces a new Affine
. Note that printing a general affine transform describes an equalivilent set of discrete affine transforms (that is, a sequence of discrete Rotation
, Translation
and Scale
operations) that perform the same transform
rotation_followed_by_scale = r.compose_before(nus)
print(rotation_followed_by_scale)
<menpo.transform.homogeneous.affine.Affine object at 0x0000000004C57B00> [[ 2.52 3.36 -5.6 0. ] [-2.4 1.8 0. 0. ] [ 7.2 9.6 9. 0. ] [ 0. 0. 0. 1. ]] CCW Rotation of 120 degrees about [ 0.115 -0.808 0.577] NonUniformScale by [ 15. 7. 3.] CCW Rotation of 119 degrees about [ 0.577 0.577 -0.577] Translate by [ 0. 0. 0.]
We can request this sequence directly by using the decompose()
method
notice how all affine transforms support eqality checking
decomposed = rotation_followed_by_scale.decompose()
result_of_chain = reduce(lambda x, y: x.compose_before(y), decomposed)
print(result_of_chain)
print('Does chaining the decomposition do '
'the same as the original? %s' % (result_of_chain == rotation_followed_by_scale))
<menpo.transform.homogeneous.affine.Affine object at 0x0000000004C57E48> [[ 2.520e+00 3.360e+00 -5.600e+00 0.000e+00] [ -2.400e+00 1.800e+00 -3.035e-15 0.000e+00] [ 7.200e+00 9.600e+00 9.000e+00 0.000e+00] [ 0.000e+00 0.000e+00 0.000e+00 1.000e+00]] CCW Rotation of 120 degrees about [ 0.115 -0.808 0.577] NonUniformScale by [ 15. 7. 3.] CCW Rotation of 120 degrees about [ 0.577 0.577 -0.577] Translate by [ 0. 0. 0.] Does chaining the decomposition do the same as the original? True
Note that chaining Similarity
objects together yields a Similarity
instead.
print(r.compose_before(us))
<menpo.transform.homogeneous.similarity.Similarity object at 0x0000000004C57CF8> [[ 0.72 0.96 -1.6 0. ] [-1.6 1.2 0. 0. ] [ 0.96 1.28 1.2 0. ] [ 0. 0. 0. 1. ]] CCW Rotation of 90 degrees about [ 0. 1. 0.] UniformScale by 2.000000 CCW Rotation of 116 degrees about [-0.802 -0.535 0.267] Translate by [ 0. 0. 0.]
Finally, note that all instances of Transform
are guaranteed to have an .apply()
method. Novel points can be passed in here, which will then be transformed (the result is returned, and the points left as they are). However, objects passed into apply()
can define a ._transform()
method, signifing that this object knows how to handle it's own transformation. In this case, the trasformation is applied in place. PointCloud
is one such class that defines this method.
%matplotlib inline
from menpo.shape import PointCloud
points = np.array([[0.0, 0.0],
[1.0, 0.0],
[1.0, 1.0],
[0.0, 1.0]])
pc = PointCloud(points)
print(pc)
print(pc.points)
pc.view()
PointCloud: n_points: 4, n_dims: 2 [[ 0. 0.] [ 1. 0.] [ 1. 1.] [ 0. 1.]]
<menpo.visualize.viewmatplotlib.MatplotlibPointCloudViewer2d at 0xb565cf8>
A transform can .apply()
to some raw points to yield new ones...
import matplotlib.pyplot as plt
new_points = t.apply(points)
print(new_points)
plt.scatter(new_points[:, 0], new_points[:, 1])
[[ -1. 20.] [ 0. 20.] [ 0. 21.] [ -1. 21.]]
<matplotlib.collections.PathCollection at 0x13d2b5c0>
..or can apply directly to Transformable
objects.
pc3 = t.apply(pc)
print(pc3.points)
pc3.view()
[[ -1. 20.] [ 0. 20.] [ 0. 21.] [ -1. 21.]]
<menpo.visualize.viewmatplotlib.MatplotlibPointCloudViewer2d at 0x13d17278>