import numpy as np
from menpo.shape import PointCloud
from menpo.groupalign import GeneralizedProcrustesAnalysis
Generalized Procrustes Analysis is an iterative technique for optimally rigidly aligning a set of shapes. To see how this works, let's make three PointCloud
instances
src_1 = PointCloud(np.array([[0.0, 0.0],
[1.0, 0.0],
[1.0, 1.0],
[0.0, 1.0]]))
src_2 = PointCloud(np.array([[0.0, 0.0],
[2.0, 0.1],
[2.0, 2.0],
[0.1, 2.0]]))
src_3 = PointCloud(np.array([[-1.0, 0.0],
[0.0, 0.0],
[0.0, 1.0],
[-1.0, 1.0]]))
GeneralizedProcrustesAnalysis
accepts a list of Shape
instances upon construction. This causes it to run it's iterative alignment process.
gpa = GeneralizedProcrustesAnalysis([src_1, src_2, src_3])
Printing the object gives you a summary of what happened
print(gpa)
Converged after 2 iterations with av. error 0.030000
GeneralizedProcrustesAnalysis provides a property called .transforms
, which gives one transform per source that aligns it to the target frame. The target frame is also accessable at .target
print('There are {} transforms'.format(len(gpa.transforms)))
print('Each transform is a {}'.format(type(gpa.transforms[0])))
print('The target: {}'.format(gpa.target))
There are 3 transforms Each transform is a <class 'menpo.transform.homogeneous.similarity.AlignmentSimilarity'> The target: PointCloud: n_points: 4, n_dims: 2
the optimal transform found to align src_1
is:
src_1_similarity = gpa.transforms[0]
print(src_1_similarity)
<menpo.transform.homogeneous.similarity.AlignmentSimilarity object at 0x0000000012EF1908> [[ 1.31687762e+00 1.46202785e-16 -3.16772143e-01] [ 0.00000000e+00 1.31687762e+00 1.65611899e-02] [ 0.00000000e+00 0.00000000e+00 1.00000000e+00]] CCW Rotation of 90 degrees about [0 0 1] UniformScale by 1.316878 CCW Rotation of 90 degrees about [0 0 1] Translate by [-0.31677214 0.01656119]
and for src_2
we used:
src_2_similarity = gpa.transforms[1]
print(src_2_similarity)
<menpo.transform.homogeneous.similarity.AlignmentSimilarity object at 0x0000000012EF1C18> [[ 6.74878295e-01 7.49265422e-17 -3.50083586e-01] [ -7.49265422e-17 6.74878295e-01 -1.67502527e-02] [ 0.00000000e+00 0.00000000e+00 1.00000000e+00]] CCW Rotation of 180 degrees about [0 0 1] UniformScale by 0.674878 CCW Rotation of 180 degrees about [0 0 1] Translate by [-0.35008359 -0.01675025]
src_1_similarity
has a source
which is the same as src_1
:
print(src_1_similarity.source)
print(np.all(src_1 == src_1_similarity.source))
PointCloud: n_points: 4, n_dims: 2 True
the target of this object was set in the first iteration of the algorithm to the mean of all the sources:
print(src_1_similarity.target)
PointCloud: n_points: 4, n_dims: 2
Its simple to check how the alignment did
%matplotlib inline
for t, a_c in zip(gpa.transforms, gpa.sources):
aligned_src = t.apply(a_c)
aligned_src.view()