procrustes
Procrustes Analysis.
d = procrustes (X, Y)
computes a linear
transformation of the points in the matrix Y to best conform them to
the points in the matrix X by minimizing the sum of squared errors, as
the goodness of fit criterion, which is returned in d as a
dissimilarity measure. d is standardized by a measure of the scale of
X, given by
sum (sum ((X - repmat (mean (X, 1), size (X, 1), 1)) .^ 2, 1))
i.e., the sum of squared elements of a centered version of X. However, if X comprises repetitions of the same point, the sum of squared errors is not standardized.
X and Y must have the same number of points (rows) and
procrustes
matches the -th point in Y to the
-th point in X. Points in Y can have smaller dimensions
(columns) than those in X, but not the opposite. Missing dimensions in
Y are added with padding columns of zeros as necessary to match the
the dimensions in X.
[d, Z] = procrustes (X, Y)
also returns the
transformed values in Y.
[d, Z, transform] = procrustes (X, Y)
also returns the transformation that maps Y to Z.
transform is a structure with fields:
c | the translation component | ||
T | the orthogonal rotation and reflection component | ||
b | the scale component |
So that Z = transform.
b
* Y *
transform.T
+ transform.c
procrustes
can take two optional parameters as Name-Value pairs.
[…] = procrustes (…,
computes a transformation that does not include scaling, that is
transform."Scaling"
, false
)b
= 1. Setting "Scaling"
to true
includes a scaling component, which is the default.
[…] = procrustes (…,
computes a transformation that does not include a reflection component, that
is transform."Reflection"
, false
)T
= 1. Setting "Reflection"
to
true
forces the solution to include a reflection component in the
computed transformation, that is transform.T
= -1.
[…] = procrustes (…,
computes the best fit procrustes solution, which may or may not include a
reflection component, which is the default.
"Reflection"
, "best"
)
See also: cmdscale
Source Code: procrustes
## Create some random points in two dimensions n = 10; randn ("seed", 1); X = normrnd (0, 1, [n, 2]); ## Those same points, rotated, scaled, translated, plus some noise S = [0.5, -sqrt(3)/2; sqrt(3)/2, 0.5]; # rotate 60 degrees Y = normrnd (0.5*X*S + 2, 0.05, n, 2); ## Conform Y to X, plot original X and Y, and transformed Y [d, Z] = procrustes (X, Y); plot (X(:,1), X(:,2), "rx", Y(:,1), Y(:,2), "b.", Z(:,1), Z(:,2), "bx"); |
## Find Procrustes distance and plot superimposed shape X = [40 88; 51 88; 35 78; 36 75; 39 72; 44 71; 48 71; 52 74; 55 77]; Y = [36 43; 48 42; 31 26; 33 28; 37 30; 40 31; 45 30; 48 28; 51 24]; plot (X(:,1),X(:,2),"x"); hold on plot (Y(:,1),Y(:,2),"o"); xlim ([0 100]); ylim ([0 100]); legend ("Target shape (X)", "Source shape (Y)"); [d, Z] = procrustes (X, Y) plot (Z(:,1), Z(:,2), "s"); legend ("Target shape (X)", "Source shape (Y)", "Transformed shape (Z)"); hold off d = 0.2026 Z = 39.769 87.509 50.562 86.801 35.549 72.163 37.313 73.991 40.873 75.850 43.552 76.796 48.058 75.977 50.783 74.229 53.541 70.684 |
## Apply Procrustes transformation to larger set of points ## Create matrices with landmark points for two triangles X = [5, 0; 5, 5; 8, 5]; # target Y = [0, 0; 1, 0; 1, 1]; # source ## Create a matrix with more points on the source triangle Y_mp = [linspace(Y(1,1),Y(2,1),10)', linspace(Y(1,2),Y(2,2),10)'; ... linspace(Y(2,1),Y(3,1),10)', linspace(Y(2,2),Y(3,2),10)'; ... linspace(Y(3,1),Y(1,1),10)', linspace(Y(3,2),Y(1,2),10)']; ## Plot both shapes, including the larger set of points for the source shape plot ([X(:,1); X(1,1)], [X(:,2); X(1,2)], "bx-"); hold on plot ([Y(:,1); Y(1,1)], [Y(:,2); Y(1,2)], "ro-", "MarkerFaceColor", "r"); plot (Y_mp(:,1), Y_mp(:,2), "ro"); xlim ([-1 10]); ylim ([-1 6]); legend ("Target shape (X)", "Source shape (Y)", ... "More points on Y", "Location", "northwest"); hold off ## Obtain the Procrustes transformation [d, Z, transform] = procrustes (X, Y) ## Use the Procrustes transformation to superimpose the more points (Y_mp) ## on the source shape onto the target shape, and then visualize the results. Z_mp = transform.b * Y_mp * transform.T + transform.c(1,:); figure plot ([X(:,1); X(1,1)], [X(:,2); X(1,2)], "bx-"); hold on plot ([Y(:,1); Y(1,1)], [Y(:,2); Y(1,2)], "ro-", "MarkerFaceColor", "r"); plot (Y_mp(:,1), Y_mp(:,2), "ro"); xlim ([-1 10]); ylim ([-1 6]); plot ([Z(:,1); Z(1,1)],[Z(:,2); Z(1,2)],"ks-","MarkerFaceColor","k"); plot (Z_mp(:,1),Z_mp(:,2),"ks"); legend ("Target shape (X)", "Source shape (Y)", ... "More points on Y", "Transformed source shape (Z)", ... "Transformed additional points", "Location", "northwest"); hold off d = 0.044118 Z = 5.0000 0.5000 4.5000 4.5000 8.5000 5.0000 transform = scalar structure containing the fields: T = -0.1240 0.9923 0.9923 0.1240 b = 4.0311 c = 5.0000 0.5000 5.0000 0.5000 5.0000 0.5000 |
## Compare shapes without reflection T = [33, 93; 33, 87; 33, 80; 31, 72; 32, 65; 32, 58; 30, 72; ... 28, 72; 25, 69; 22, 64; 23, 59; 26, 57; 30, 57]; S = [48, 83; 48, 77; 48, 70; 48, 65; 49, 59; 49, 56; 50, 66; ... 52, 66; 56, 65; 58, 61; 57, 57; 54, 56; 51, 55]; plot (T(:,1), T(:,2), "x-"); hold on plot (S(:,1), S(:,2), "o-"); legend ("Target shape (d)", "Source shape (b)"); hold off d_false = procrustes (T, S, "reflection", false); printf ("Procrustes distance without reflection: %f\n", d_false); d_true = procrustes (T, S, "reflection", true); printf ("Procrustes distance with reflection: %f\n", d_true); d_best = procrustes (T, S, "reflection", "best"); printf ("Procrustes distance with best fit: %f\n", d_true); Procrustes distance without reflection: 0.342463 Procrustes distance with reflection: 0.020428 Procrustes distance with best fit: 0.020428 |