In [1]:
%matplotlib inline
from pathlib import Path
from menpo.transform import GeneralizedProcrustesAnalysis
from menpo.shape import PointCloud
import numpy as np
import matplotlib.pyplot as plt
Let's make three PointCloud
instances
In [2]:
src_1 = PointCloud(np.array([[-1.0, -1.0],
[1.0, -1.0],
[1.0, 1.0],
[-1.0, 1.0]]))
src_2 = PointCloud(np.array([[0.0, 0.0],
[1.5, 0.1],
[1.5, 1.5],
[0.1, 1.5]]))
src_3 = PointCloud(np.array([[-2.0, 0.0],
[0.0, 3.0],
[2.0, 0.0],
[0.0, -3.0]]))
In [3]:
viewer = src_1.view(marker_face_colour='r', marker_size=80);
src_2.view(figure_id=viewer.figure_id, marker_face_colour='g', marker_size=80);
src_3.view(figure_id=viewer.figure_id, marker_face_colour='b', marker_size=80);
plt.legend(['src_1', 'src_2', 'src_3']);
GeneralizedProcrustesAnalysis
accepts a list of Shape
instances upon construction. This causes it to run it's iterative alignment process.
In [4]:
gpa = GeneralizedProcrustesAnalysis([src_1, src_2, src_3])
Printing the object gives you a summary of what happened
In [5]:
print(gpa)
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
In [6]:
print('There are {} transforms'.format(len(gpa.transforms)))
print('Each transform is a {}'.format(type(gpa.transforms[0])))
print('The target: {}'.format(gpa.target))
The optimal transform found to align src_1
is:
In [7]:
src_1_similarity = gpa.transforms[0]
print(src_1_similarity)
and for src_2
we used:
In [8]:
src_2_similarity = gpa.transforms[1]
print(src_2_similarity)
src_1_similarity
has a source
which is the same as src_1
:
In [9]:
print(src_1_similarity.source)
print(np.all(src_1 == src_1_similarity.source))
The target of this object was set in the first iteration of the algorithm to the mean of all the sources:
In [10]:
print(src_1_similarity.target)
Its simple to check how the alignment did
In [11]:
colours_list = ['r' ,'g', 'b']
for i, (t, a_c) in enumerate(zip(gpa.transforms, gpa.sources)):
aligned_src = t.apply(a_c)
aligned_src.view(marker_face_colour=colours_list[i], marker_size=10);
plt.legend(['src_1', 'src_2', 'src_3']);
In this example, we will use one of the most popular and widely used annotated facial databases, the Labeled Faces Parts in the Wild (LFPW) database. Both images and corresponding facial landmark annotations are publicly available and can be downloaded from the following link:
In order to continue with this notebook, the user is required to simply:
Note that the .zip
file containing the whole annotated database is of approximately 350MB.
In [12]:
path_to_lfpw = Path('/vol/atlas/databases/lfpw')
# path_to_lfpw = Path('/Users/pts08/data/lfpw')
Let's load the shapes
In [ ]:
import menpo.io as mio
shapes = [s['all'] for s in mio.import_landmark_files(path_to_lfpw / 'trainset' / '*.pts',
as_generator=True, verbose=True)]
We can visualize them using the visualize_pointclouds
widget.
In [14]:
from menpowidgets import visualize_pointclouds
visualize_pointclouds(shapes)
It is very easy to align the shapes
In [15]:
from menpo.transform import Translation
# centralize shapes
centered_shapes = [Translation(-s.centre()).apply(s) for s in shapes]
# align centralized shape using Procrustes Analysis
gpa = GeneralizedProcrustesAnalysis(centered_shapes)
print(gpa)
# get the aligned shapes
aligned_shapes = [s.aligned_source() for s in gpa.transforms]
Let's see the result!
In [16]:
n_shapes = 101
plt.subplot(121)
plt.title('Before GPA')
for s in shapes[:n_shapes]:
s.view(axes_x_limits=10, axes_y_limits=10)
plt.subplot(122)
plt.title('After GPA')
for s in aligned_shapes[:n_shapes]:
s.view()