open source pkg v1

This commit is contained in:
Vijay Yadev
2020-08-04 19:12:31 -04:00
parent bef213dba9
commit c389fc2c47
3708 changed files with 1624220 additions and 1 deletions

View File

@@ -0,0 +1,9 @@
function [RotFull] = AddOrthRow(RotSmall)
% We can work out these values from the small version of the rotation matrix Rx * Ry * Rz (if you plug in values you can work it out, just slightly tedious)
RotFull = zeros(3,3);
RotFull(1:2, :) = RotSmall;
RotFull(3,1) = RotSmall(1, 2) * RotSmall(2, 3) - RotSmall(1, 3) * RotSmall(2, 2);
RotFull(3,2) = RotSmall(1, 3) * RotSmall(2, 1) - RotSmall(1, 1) * RotSmall(2, 3);
RotFull(3,3) = RotSmall(1, 1) * RotSmall(2, 2) - RotSmall(1, 2) * RotSmall(2, 1);

View File

@@ -0,0 +1,22 @@
function [ R, T ] = AlignShapesKabsch ( alignFrom, alignTo )
%ALIGN3DSHAPES Summary of this function goes here
% Detailed explanation goes here
dims = size(alignFrom, 2);
alignFromMean = alignFrom - repmat(mean(alignFrom), size(alignFrom,1),1);
alignToMean = alignTo - repmat(mean(alignTo), size(alignTo,1),1);
[U, ~, V] = svd( alignFromMean' * alignToMean);
% make sure no reflection is there
d = sign(det(V*U'));
corr = eye(dims);
corr(end,end) = d;
R = V*corr*U';
T = mean(alignTo) - (R * mean(alignFrom)')';
T = T';
end

View File

@@ -0,0 +1,30 @@
function [ A, T, error, alignedShape ] = AlignShapesWithScale( alignFrom, alignTo )
%ALIGNSHAPESWITHSCALE Summary of this function goes here
% Detailed explanation goes here
numPoints = size(alignFrom,1);
meanFrom = mean(alignFrom);
meanTo = mean(alignTo);
alignFromMeanNormed = bsxfun(@minus, alignFrom, meanFrom);
alignToMeanNormed = bsxfun(@minus, alignTo, meanTo);
% scale now
sFrom = sqrt(sum(alignFromMeanNormed(:).^2)/numPoints);
sTo = sqrt(sum(alignToMeanNormed(:).^2)/numPoints);
s = sTo / sFrom;
alignFromMeanNormed = alignFromMeanNormed/sFrom;
alignToMeanNormed = alignToMeanNormed/sTo;
[R, t] = AlignShapesKabsch(alignFromMeanNormed, alignToMeanNormed);
A = s * R;
aligned = (A * alignFrom')';
T = mean(alignTo - aligned);
alignedShape = bsxfun(@plus, aligned, T);
error = sqrt(mean(sum((alignedShape - alignTo).^2,2)));
end

View File

@@ -0,0 +1,11 @@
function [Rot] = AxisAngle2Rot(axisAngle)
theta = norm(axisAngle, 2);
nx = axisAngle / theta;
nx = [ 0 -nx(3) nx(2);
nx(3) 0 -nx(1);
-nx(2) nx(1) 0 ];
Rot = eye(3) + sin(theta) * nx + (1-cos(theta))*nx^2;

View File

@@ -0,0 +1,11 @@
function [Rot] = Euler2Rot(euler)
rx = euler(1);
ry = euler(2);
rz = euler(3);
Rx = [1 0 0; 0 cos(rx) -sin(rx); 0 sin(rx) cos(rx)];
Ry = [cos(ry) 0 sin(ry); 0 1 0; -sin(ry) 0 cos(ry)];
Rz = [cos(rz) -sin(rz) 0; sin(rz) cos(rz) 0; 0 0 1];
Rot = Rx * Ry * Rz;

View File

@@ -0,0 +1,7 @@
function [shape3D] = GetShape3D(M, V, p)
shape3D = M + V * p;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
end

View File

@@ -0,0 +1,20 @@
function [shape2D] = GetShapeOrtho(M, V, p, global_params)
% M - mean shape vector
% V - eigenvectors
% p - parameters of non-rigid shape
% V_exp
% p_exp
% global_params includes scale, euler rotation, translation,
% R - rotation matrix
% T - translation vector (tx, ty)
R = Euler2Rot(global_params(2:4));
T = [global_params(5:6); 0];
a = global_params(1);
shape3D = GetShape3D(M, V, p);
shape2D = bsxfun(@plus, a * R*shape3D', T);
shape2D = shape2D';
end

View File

@@ -0,0 +1,103 @@
function [normX, normY, meanShape, Transform] = ProcrustesAnalysis(x, y, options)
% Translate all elements to origin and scale to 1
normX = zeros(size(x));
normY = zeros(size(y));
for i = 1:size(x,1)
offsetX = mean(x(i,:));
offsetY = mean(y(i,:));
Transform.offsetX(i) = offsetX;
Transform.offsetY(i) = offsetY;
normX(i,:) = x(i,:) - offsetX;
normY(i,:) = y(i,:) - offsetY;
% Get the Frobenius norm, to scale the shapes to unit size
scale = norm([normX(i,:) normY(i,:)], 'fro');
Transform.scale(i) = scale;
normX(i,:) = normX(i,:)/scale;
normY(i,:) = normY(i,:)/scale;
end
% Rotate elements untill all of them have the same orientation
% the initial estimate of rotation would be the first element
% if change is less than 1% stop (shouldn't take more than 2 steps)
change = 0.1;
meanShape = [ normX(1,:); normY(1,:) ]';
Transform.Rotation = zeros(size(x,1),1);
for i = 1:30
% align all of the shapes to the mean shape
% remember all orientations to get the mean one
orientations = zeros(size(normX,1),1);
for j = 1:size(x,1)
% do SVD of mean * X'
currentShape = [ normX(j,:); normY(j,:) ]';
[U, ~, V] = svd( meanShape' * currentShape);
rot = V*U';
if(asin(rot(2,1)) > 0)
orientations(j) = real(acos(rot(1,1)));
else
orientations(j) = real(-acos(rot(1,1)));
end
Transform.Rotation(j) = Transform.Rotation(j) + orientations(j);
currentShape = currentShape * rot;
normX(j,:) = currentShape(:,1)';
normY(j,:) = currentShape(:,2)';
end
% recalculate the mean shape;
oldMean = meanShape;
meanShape = [mean(normX); mean(normY)]';
% rotate the mean shape to mean rotation
meanOrientation = mean(orientations);
% Do this only the first time
if(i==1)
rotM = [ cos(-meanOrientation) -sin(-meanOrientation); sin(-meanOrientation) cos(-meanOrientation) ];
meanShape = meanShape * rotM;
end
% scale mean shape to unit
meanScale = norm(meanShape, 'fro');
meanShape = meanShape*(1/meanScale);
% find frobenious norm
diff = norm(oldMean - meanShape, 'fro');
if(diff/norm(oldMean,'fro') < change)
break;
end
end
% transform to tangent space to preserve linearities
% get the scaling factors for each shape
if(options.TangentSpaceTransform)
scaling = [ normX normY ] * [ meanShape(:,1)' meanShape(:,2)']';
for i=1:size(x,1)
normX(i,:) = normX(i,:) * (1 / scaling(i));
normY(i,:) = normY(i,:) * (1 / scaling(i));
end
end

View File

@@ -0,0 +1,139 @@
function [ normX, normY, normZ, meanShape, Transform ] = ProcrustesAnalysis3D( x, y, z, tangentSpace, meanShape )
%PROCRUSTESANALYSIS3D Summary of this function goes here
% Detailed explanation goes here
meanProvided = false;
if(nargin > 4)
meanProvided = true;
end
% Translate all elements to origin
normX = zeros(size(x));
normY = zeros(size(y));
normZ = zeros(size(z));
for i = 1:size(x,1)
offsetX = mean(x(i,:));
offsetY = mean(y(i,:));
offsetZ = mean(z(i,:));
Transform.offsetX(i) = offsetX;
Transform.offsetY(i) = offsetY;
Transform.offsetZ(i) = offsetZ;
normX(i,:) = x(i,:) - offsetX;
normY(i,:) = y(i,:) - offsetY;
normZ(i,:) = z(i,:) - offsetZ;
end
% Rotate elements untill all of them have the same orientation
% the initial estimate of rotation would be the first element
% if change is less than 1% stop (shouldn't take more than 2 steps)
change = 0.1;
if(~meanProvided)
meanShape = [ mean(normX); mean(normY); mean(normZ) ]';
end
% scale all the shapes to mean shape
% Get the Frobenius norm, to scale the shapes to mean size (still want to
% retain mm)
meanScale = norm(meanShape, 'fro');
for i = 1:size(x,1)
scale = norm([normX(i,:) normY(i,:) normZ(i,:)], 'fro')/meanScale;
normX(i,:) = normX(i,:)/scale;
normY(i,:) = normY(i,:)/scale;
normZ(i,:) = normZ(i,:)/scale;
end
Transform.RotationX = zeros(size(x,1),1);
Transform.RotationY = zeros(size(x,1),1);
Transform.RotationZ = zeros(size(x,1),1);
for i = 1:30
% align all of the shapes to the mean shape
% remember all orientations to get the mean one (in euler angle form, pitch, yaw roll)
orientationsX = zeros(size(normX,1),1);
orientationsY = zeros(size(normX,1),1);
orientationsZ = zeros(size(normX,1),1);
for j = 1:size(x,1)
currentShape = [normX(j,:); normY(j,:); normZ(j,:)]';
% we want to align the current shape to the mean one
[ R, T ] = AlignShapesKabsch(currentShape, meanShape);
eulers = Rot2Euler(R);
orientationsX(j) = eulers(1);
orientationsY(j) = eulers(2);
orientationsZ(j) = eulers(3);
Transform.RotationX(j) = eulers(1);
Transform.RotationY(j) = eulers(2);
Transform.RotationZ(j) = eulers(3);
currentShape = R * currentShape';
normX(j,:) = currentShape(1,:);
normY(j,:) = currentShape(2,:);
normZ(j,:) = currentShape(3,:);
end
% recalculate the mean shape
% if(~meanProvided)
oldMean = meanShape;
meanShape = [mean(normX); mean(normY); mean(normZ)]';
meanScale = norm(meanShape, 'fro');
% end
for j = 1:size(x,1)
scale = norm([normX(j,:) normY(j,:) normZ(j,:)], 'fro')/meanScale;
normX(j,:) = normX(j,:)/scale;
normY(j,:) = normY(j,:)/scale;
normZ(j,:) = normZ(j,:)/scale;
end
if(i==1 && ~meanProvided)
% rotate the mean shape to mean rotation
meanOrientationX = mean(orientationsX);
meanOrientationY = mean(orientationsY);
meanOrientationZ = mean(orientationsZ);
R = Euler2Rot([meanOrientationX, meanOrientationY, meanOrientationZ]);
meanShape = (R * meanShape')';
end
% find frobenious norm
diff = norm(oldMean - meanShape, 'fro');
if(diff/norm(oldMean,'fro') < change)
break;
end
end
% transform to tangent space to preserve linearities
% get the scaling factors for each shape
if(tangentSpace)
[ normX, normY, normZ] = TangentSpaceTransform(normX, normY, normZ, meanShape);
end
end

View File

@@ -0,0 +1,11 @@
function [ axisAngle ] = Rot2AxisAngle( Rot )
%ROT2AXISANGLE Summary of this function goes here
% Detailed explanation goes here
theta = acos((trace(Rot) - 1) / 2);
vec = 1.0/(2*sin(theta));
vec = vec * [Rot(3,2) - Rot(2,3), Rot(1,3) - Rot(3,1), Rot(2,1) - Rot(1,2)];
axisAngle = vec * theta;
end

View File

@@ -0,0 +1,12 @@
function [euler] = Rot2Euler(R)
q0 = sqrt( 1 + R(1,1) + R(2,2) + R(3,3) ) / 2;
q1 = (R(3,2) - R(2,3)) / (4*q0) ;
q2 = (R(1,3) - R(3,1)) / (4*q0) ;
q3 = (R(2,1) - R(1,2)) / (4*q0) ;
yaw = asin(2*(q0*q2 + q1*q3));
pitch= atan2(2*(q0*q1-q2*q3), q0*q0-q1*q1-q2*q2+q3*q3);
roll = atan2(2*(q0*q3-q1*q2), q0*q0+q1*q1-q2*q2-q3*q3);
euler = [pitch, yaw, roll];

View File

@@ -0,0 +1,17 @@
function [ transformedX, transformedY, transformedZ ] = TangentSpaceTransform( x, y, z, meanShape )
%TANGENTSPACETRANSFORM Summary of this function goes here
% Detailed explanation goes here
scaling = [ x y z] * [ meanShape(:,1)' meanShape(:,2)' meanShape(:,3)']';
for i=1:size(x,1)
x(i,:) = x(i,:) * (1 / scaling(i));
y(i,:) = y(i,:) * (1 / scaling(i));
z(i,:) = z(i,:) * (1 / scaling(i));
end
transformedX = x * mean(scaling);
transformedY = y * mean(scaling);
transformedZ = z * mean(scaling);
end

View File

@@ -0,0 +1,345 @@
function [ a, R, T, T3D, params, error, shapeOrtho ] = fit_PDM_ortho_proj_to_2D( M, E, V, shape2D, f, cx, cy)
%FITPDMTO2DSHAPE Summary of this function goes here
% Detailed explanation goes here
params = zeros(size(E));
hidden = false;
% if some of the points are unavailable modify M, V, and shape2D (can
% later infer the actual shape from this)
if(sum(shape2D(:)==0) > 0)
hidden = true;
% which indices to remove
inds_to_rem = shape2D(:,1) == 0 | shape2D(:,2) == 0;
shape2D = shape2D(~inds_to_rem,:);
inds_to_rem = repmat(inds_to_rem, 3, 1);
M_old = M;
V_old = V;
M = M(~inds_to_rem);
V = V(~inds_to_rem,:);
end
num_points = numel(M) / 3;
m = reshape(M, num_points, 3)';
width_model = max(m(1,:)) - min(m(1,:));
height_model = max(m(2,:)) - min(m(2,:));
bounding_box = [min(shape2D(:,1)), min(shape2D(:,2)),...
max(shape2D(:,1)), max(shape2D(:,2))];
a = (((bounding_box(3) - bounding_box(1)) / width_model) + ((bounding_box(4) - bounding_box(2))/ height_model)) / 2;
tx = (bounding_box(3) + bounding_box(1))/2;
ty = (bounding_box(4) + bounding_box(2))/2;
% correct it so that the bounding box is just around the minimum
% and maximum point in the initialised face
tx = tx - a*(min(m(1,:)) + max(m(1,:)))/2;
ty = ty - a*(min(m(2,:)) + max(m(2,:)))/2;
R = eye(3);
T = [tx; ty];
currShape = getShapeOrtho(M, V, params, R, T, a);
currError = getRMSerror(currShape, shape2D);
reg_rigid = zeros(6,1);
regFactor = 20;
regularisations = [reg_rigid; regFactor ./ E]; % the above version, however, does not perform as well
regularisations = diag(regularisations)*diag(regularisations);
red_in_a_row = 0;
for i=1:1000
shape3D = M + V * params;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
% Now find the current residual error
currShape = a * R(1:2,:)*shape3D' + repmat(T, 1, numel(M)/3);
currShape = currShape';
error_res = shape2D - currShape;
eul = Rot2Euler(R);
p_global = [a; eul'; T];
% get the Jacobians
J = CalcJacobian(M, V, params, p_global);
% RLMS style update
p_delta = (J'*J + regularisations) \ (J'*error_res(:) - regularisations*[p_global;params]);
[params, p_global] = CalcReferenceUpdate(p_delta, params, p_global);
a = p_global(1);
R = Euler2Rot(p_global(2:4));
T = p_global(5:6);
shape3D = M + V * params;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
currShape = a * R(1:2,:)*shape3D' + repmat(T, 1, numel(M)/3);
currShape = currShape';
error = getRMSerror(currShape, shape2D);
if(0.999 * currError < error)
red_in_a_row = red_in_a_row + 1;
if(red_in_a_row == 5)
break;
end
end
currError = error;
end
if(hidden)
shapeOrtho = getShapeOrtho(M_old, V_old, params, R, T, a);
else
shapeOrtho = currShape;
end
if(nargin == 7)
Zavg = f / a;
Xavg = (T(1) - cx) / a;
Yavg = (T(2) - cy) / a;
T3D = [Xavg;Yavg;Zavg];
else
T3D = [0;0;0];
end
end
function [shape2D] = getShapeOrtho(M, V, p, R, T, a)
% M - mean shape vector
% V - eigenvectors
% p - parameters of non-rigid shape
% R - rotation matrix
% T - translation vector (tx, ty)
shape3D = getShape3D(M, V, p);
shape2D = a * R(1:2,:)*shape3D' + repmat(T, 1, numel(M)/3);
shape2D = shape2D';
end
function [shape2D] = getShapeOrthoFull(M, V, p, R, T, a)
% M - mean shape vector
% V - eigenvectors
% p - parameters of non-rigid shape
% R - rotation matrix
% T - translation vector (tx, ty)
T = [T; 0];
shape3D = getShape3D(M, V, p);
shape2D = a * R*shape3D' + repmat(T, 1, numel(M)/3);
shape2D = shape2D';
end
function [shape3D] = getShape3D(M, V, params)
shape3D = M + V * params;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
end
function [error] = getRMSerror(shape2Dv1, shape2Dv2)
error = sqrt(mean(reshape(shape2Dv1 - shape2Dv2, numel(shape2Dv1), 1).^2));
end
% This calculates the combined rigid with non-rigid Jacobian
function J = CalcJacobian(M, V, p, p_global)
n = size(M, 1)/3;
non_rigid_modes = size(V,2);
J = zeros(n*2, 6 + non_rigid_modes);
% now the layour is
% ---------- Rigid part -------------------|----Non rigid part--------|
% dx_1/ds, dx_1/dr1, ... dx_1/dtx, dx_1/dty dx_1/dp_1 ... dx_1/dp_m
% dx_2/ds, dx_2/dr1, ... dx_2/dtx, dx_2/dty dx_2/dp_1 ... dx_2/dp_m
% ...
% dx_n/ds, dx_n/dr1, ... dx_n/dtx, dx_n/dty dx_n/dp_1 ... dx_n/dp_m
% dy_1/ds, dy_1/dr1, ... dy_1/dtx, dy_1/dty dy_1/dp_1 ... dy_1/dp_m
% ...
% dy_n/ds, dy_n/dr1, ... dy_n/dtx, dy_n/dty dy_n/dp_1 ... dy_n/dp_m
% getting the rigid part
J(:,1:6) = CalcRigidJacobian(M, V, p, p_global);
% constructing the non-rigid part
R = Euler2Rot(p_global(2:4));
s = p_global(1);
% 'rotate' and 'scale' the principal components
% First reshape to 3D
V_X = V(1:n,:);
V_Y = V(n+1:2*n,:);
V_Z = V(2*n+1:end,:);
J_x_non_rigid = s*(R(1,1)*V_X + R(1,2)*V_Y + R(1,3)*V_Z);
J_y_non_rigid = s*(R(2,1)*V_X + R(2,2)*V_Y + R(2,3)*V_Z);
J(1:n, 7:end) = J_x_non_rigid;
J(n+1:end, 7:end) = J_y_non_rigid;
end
function J = CalcRigidJacobian(M, V, p, p_global)
n = size(M, 1)/3;
% Get the current 3D shape (not affected by global transform, as this
% is how the Jacobian was derived (for derivation please see
% ../derivations/orthoJacobian
shape3D = GetShape3D(M, V, p);
% Get the rotation matrix corresponding to current global orientation
R = Euler2Rot(p_global(2:4));
s = p_global(1);
% Rigid Jacobian is laid out as follows
% dx_1/ds, dx_1/dr1, dx_1/dr2, dx_1/dr3, dx_1/dtx, dx_1/dty
% dx_2/ds, dx_2/dr1, dx_2/dr2, dx_2/dr3, dx_2/dtx, dx_2/dty
% ...
% dx_n/ds, dx_n/dr1, dx_n/dr2, dx_n/dr3, dx_n/dtx, dx_n/dty
% dy_1/ds, dy_1/dr1, dy_1/dr2, dy_1/dr3, dy_1/dtx, dy_1/dty
% ...
% dy_n/ds, dy_n/dr1, dy_n/dr2, dy_n/dr3, dy_n/dtx, dy_n/dty
J = zeros(n*2, 6);
% dx/ds = X * r11 + Y * r12 + Z * r13
% dx/dr1 = s*(r13 * Y - r12 * Z)
% dx/dr2 = -s*(r13 * X - r11 * Z)
% dx/dr3 = s*(r12 * X - r11 * Y)
% dx/dtx = 1
% dx/dty = 0
% dy/ds = X * r21 + Y * r22 + Z * r23
% dy/dr1 = s * (r23 * Y - r22 * Z)
% dy/dr2 = -s * (r23 * X - r21 * Z)
% dy/dr3 = s * (r22 * X - r21 * Y)
% dy/dtx = 0
% dy/dty = 1
% set the Jacobian for x's
% with respect to scaling factor
J(1:n,1) = shape3D * R(1,:)';
% with respect to angular rotation around x, y, and z axes
% Change of x with respect to change in axis angle rotation
dxdR = [ 0, R(1,3), -R(1,2);
-R(1,3), 0, R(1,1);
R(1,2), -R(1,1), 0];
J(1:n,2:4) = s*(dxdR * shape3D')';
% with respect to translation
J(1:n,5) = 1;
J(1:n,6) = 0;
% set the Jacobian for y's
% with respect to scaling factor
J(n+1:end,1) = shape3D * R(2,:)';
% with respect to angular rotation around x, y, and z axes
% Change of y with respect to change in axis angle rotation
dydR = [ 0, R(2,3), -R(2,2);
-R(2,3), 0, R(2,1);
R(2,2), -R(2,1), 0];
J(n+1:end,2:4) = s*(dydR * shape3D')';
% with respect to translation
J(n+1:end,5) = 0;
J(n+1:end,6) = 1;
end
% This updates the parameters based on the updates from the RLMS
function [non_rigid, rigid] = CalcReferenceUpdate(params_delta, current_non_rigid, current_global)
rigid = zeros(6, 1);
% Same goes for scaling and translation parameters
rigid(1) = current_global(1) + params_delta(1);
rigid(5) = current_global(5) + params_delta(5);
rigid(6) = current_global(6) + params_delta(6);
% for rotation however, we want to make sure that the rotation matrix
% approximation we have
% R' = [1, -wz, wy
% wz, 1, -wx
% -wy, wx, 1]
% is a legal rotation matrix, and then we combine it with current
% rotation (through matrix multiplication) to acquire the new rotation
R = Euler2Rot(current_global(2:4));
wx = params_delta(2);
wy = params_delta(3);
wz = params_delta(4);
R_delta = [1, -wz, wy;
wz, 1, -wx;
-wy, wx, 1];
% Make sure R_delta is orthonormal
R_delta = OrthonormaliseRotation(R_delta);
% Combine rotations
R_final = R * R_delta;
% Extract euler angle
euler = Rot2Euler(R_final);
rigid(2:4) = euler;
if(length(params_delta) > 6)
% non-rigid parameters can just be added together
non_rigid = params_delta(7:end) + current_non_rigid;
else
non_rigid = current_non_rigid;
end
end
function R_ortho = OrthonormaliseRotation(R)
% U * V' is basically what we want, as it's guaranteed to be
% orthonormal
[U, ~, V] = svd(R);
% We also want to make sure no reflection happened
% get the orthogonal matrix from the initial rotation matrix
X = U*V';
% This makes sure that the handedness is preserved and no reflection happened
% by making sure the determinant is 1 and not -1
W = eye(3);
W(3,3) = det(X);
R_ortho = U*W*V';
end

View File

@@ -0,0 +1,340 @@
function [ a, R, T, T3D, params, error, shapeOrtho ] = fit_PDM_ortho_proj_to_2D_no_reg( M, E, V, shape2D)
%FITPDMTO2DSHAPE Summary of this function goes here
% Detailed explanation goes here
hidden = false;
% if some of the points are unavailable modify M, V, and shape2D (can
% later infer the actual shape from this)
if(sum(shape2D(:)==0) > 0)
hidden = true;
% which indices to remove
inds_to_rem = shape2D(:,1) == 0 | shape2D(:,2) == 0;
shape2D = shape2D(~inds_to_rem,:);
inds_to_rem = repmat(inds_to_rem, 3, 1);
M_old = M;
V_old = V;
M = M(~inds_to_rem);
V = V(~inds_to_rem,:);
end
num_points = numel(M) / 3;
m = reshape(M, num_points, 3)';
width_model = max(m(1,:)) - min(m(1,:));
height_model = max(m(2,:)) - min(m(2,:));
bounding_box = [min(shape2D(:,1)), min(shape2D(:,2)),...
max(shape2D(:,1)), max(shape2D(:,2))];
a = (((bounding_box(3) - bounding_box(1)) / width_model) + ((bounding_box(4) - bounding_box(2))/ height_model)) / 2;
tx = (bounding_box(3) + bounding_box(1))/2;
ty = (bounding_box(4) + bounding_box(2))/2;
% correct it so that the bounding box is just around the minimum
% and maximum point in the initialised face
tx = tx - a*(min(m(1,:)) + max(m(1,:)))/2;
ty = ty - a*(min(m(2,:)) + max(m(2,:)))/2;
R = eye(3);
T = [tx; ty];
params = zeros(size(E));
currShape = getShapeOrtho(M, V, params, R, T, a);
currError = getRMSerror(currShape, shape2D);
reg_rigid = zeros(6,1);
regFactor = 0.25;
regularisations = [reg_rigid; regFactor ./ E]; % the above version, however, does not perform as well
regularisations = diag(regularisations)*diag(regularisations);
red_in_a_row = 0;
for i=1:1000
shape3D = M + V * params;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
% Now find the current residual error
currShape = a * R(1:2,:)*shape3D' + repmat(T, 1, numel(M)/3);
currShape = currShape';
error_res = shape2D - currShape;
eul = Rot2Euler(R);
p_global = [a; eul'; T];
% get the Jacobians
J = CalcJacobian(M, V, params, p_global);
% RLMS style update
p_delta = (J'*J + regularisations) \ (J'*error_res(:) - regularisations*[p_global;params]);
% not to overshoot
p_delta = 0.5 * p_delta;
[params, p_global] = CalcReferenceUpdate(p_delta, params, p_global);
a = p_global(1);
R = Euler2Rot(p_global(2:4));
T = p_global(5:6);
shape3D = M + V * params;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
currShape = a * R(1:2,:)*shape3D' + repmat(T, 1, numel(M)/3);
currShape = currShape';
error = getRMSerror(currShape, shape2D);
if(0.999 * currError < error)
red_in_a_row = red_in_a_row + 1;
if(red_in_a_row == 5)
break;
end
end
currError = error;
end
if(hidden)
shapeOrtho = getShapeOrtho(M_old, V_old, params, R, T, a);
else
shapeOrtho = currShape;
end
T3D = [0;0;0];
end
function [shape2D] = getShapeOrtho(M, V, p, R, T, a)
% M - mean shape vector
% V - eigenvectors
% p - parameters of non-rigid shape
% R - rotation matrix
% T - translation vector (tx, ty)
shape3D = getShape3D(M, V, p);
shape2D = a * R(1:2,:)*shape3D' + repmat(T, 1, numel(M)/3);
shape2D = shape2D';
end
function [shape2D] = getShapeOrthoFull(M, V, p, R, T, a)
% M - mean shape vector
% V - eigenvectors
% p - parameters of non-rigid shape
% R - rotation matrix
% T - translation vector (tx, ty)
T = [T; 0];
shape3D = getShape3D(M, V, p);
shape2D = a * R*shape3D' + repmat(T, 1, numel(M)/3);
shape2D = shape2D';
end
function [shape3D] = getShape3D(M, V, params)
shape3D = M + V * params;
shape3D = reshape(shape3D, numel(shape3D) / 3, 3);
end
function [error] = getRMSerror(shape2Dv1, shape2Dv2)
error = sqrt(mean(reshape(shape2Dv1 - shape2Dv2, numel(shape2Dv1), 1).^2));
end
% This calculates the combined rigid with non-rigid Jacobian
function J = CalcJacobian(M, V, p, p_global)
n = size(M, 1)/3;
non_rigid_modes = size(V,2);
J = zeros(n*2, 6 + non_rigid_modes);
% now the layour is
% ---------- Rigid part -------------------|----Non rigid part--------|
% dx_1/ds, dx_1/dr1, ... dx_1/dtx, dx_1/dty dx_1/dp_1 ... dx_1/dp_m
% dx_2/ds, dx_2/dr1, ... dx_2/dtx, dx_2/dty dx_2/dp_1 ... dx_2/dp_m
% ...
% dx_n/ds, dx_n/dr1, ... dx_n/dtx, dx_n/dty dx_n/dp_1 ... dx_n/dp_m
% dy_1/ds, dy_1/dr1, ... dy_1/dtx, dy_1/dty dy_1/dp_1 ... dy_1/dp_m
% ...
% dy_n/ds, dy_n/dr1, ... dy_n/dtx, dy_n/dty dy_n/dp_1 ... dy_n/dp_m
% getting the rigid part
J(:,1:6) = CalcRigidJacobian(M, V, p, p_global);
% constructing the non-rigid part
R = Euler2Rot(p_global(2:4));
s = p_global(1);
% 'rotate' and 'scale' the principal components
% First reshape to 3D
V_X = V(1:n,:);
V_Y = V(n+1:2*n,:);
V_Z = V(2*n+1:end,:);
J_x_non_rigid = s*(R(1,1)*V_X + R(1,2)*V_Y + R(1,3)*V_Z);
J_y_non_rigid = s*(R(2,1)*V_X + R(2,2)*V_Y + R(2,3)*V_Z);
J(1:n, 7:end) = J_x_non_rigid;
J(n+1:end, 7:end) = J_y_non_rigid;
end
function J = CalcRigidJacobian(M, V, p, p_global)
n = size(M, 1)/3;
% Get the current 3D shape (not affected by global transform, as this
% is how the Jacobian was derived (for derivation please see
% ../derivations/orthoJacobian
shape3D = GetShape3D(M, V, p);
% Get the rotation matrix corresponding to current global orientation
R = Euler2Rot(p_global(2:4));
s = p_global(1);
% Rigid Jacobian is laid out as follows
% dx_1/ds, dx_1/dr1, dx_1/dr2, dx_1/dr3, dx_1/dtx, dx_1/dty
% dx_2/ds, dx_2/dr1, dx_2/dr2, dx_2/dr3, dx_2/dtx, dx_2/dty
% ...
% dx_n/ds, dx_n/dr1, dx_n/dr2, dx_n/dr3, dx_n/dtx, dx_n/dty
% dy_1/ds, dy_1/dr1, dy_1/dr2, dy_1/dr3, dy_1/dtx, dy_1/dty
% ...
% dy_n/ds, dy_n/dr1, dy_n/dr2, dy_n/dr3, dy_n/dtx, dy_n/dty
J = zeros(n*2, 6);
% dx/ds = X * r11 + Y * r12 + Z * r13
% dx/dr1 = s*(r13 * Y - r12 * Z)
% dx/dr2 = -s*(r13 * X - r11 * Z)
% dx/dr3 = s*(r12 * X - r11 * Y)
% dx/dtx = 1
% dx/dty = 0
% dy/ds = X * r21 + Y * r22 + Z * r23
% dy/dr1 = s * (r23 * Y - r22 * Z)
% dy/dr2 = -s * (r23 * X - r21 * Z)
% dy/dr3 = s * (r22 * X - r21 * Y)
% dy/dtx = 0
% dy/dty = 1
% set the Jacobian for x's
% with respect to scaling factor
J(1:n,1) = shape3D * R(1,:)';
% with respect to angular rotation around x, y, and z axes
% Change of x with respect to change in axis angle rotation
dxdR = [ 0, R(1,3), -R(1,2);
-R(1,3), 0, R(1,1);
R(1,2), -R(1,1), 0];
J(1:n,2:4) = s*(dxdR * shape3D')';
% with respect to translation
J(1:n,5) = 1;
J(1:n,6) = 0;
% set the Jacobian for y's
% with respect to scaling factor
J(n+1:end,1) = shape3D * R(2,:)';
% with respect to angular rotation around x, y, and z axes
% Change of y with respect to change in axis angle rotation
dydR = [ 0, R(2,3), -R(2,2);
-R(2,3), 0, R(2,1);
R(2,2), -R(2,1), 0];
J(n+1:end,2:4) = s*(dydR * shape3D')';
% with respect to translation
J(n+1:end,5) = 0;
J(n+1:end,6) = 1;
end
% This updates the parameters based on the updates from the RLMS
function [non_rigid, rigid] = CalcReferenceUpdate(params_delta, current_non_rigid, current_global)
rigid = zeros(6, 1);
% Same goes for scaling and translation parameters
rigid(1) = current_global(1) + params_delta(1);
rigid(5) = current_global(5) + params_delta(5);
rigid(6) = current_global(6) + params_delta(6);
% for rotation however, we want to make sure that the rotation matrix
% approximation we have
% R' = [1, -wz, wy
% wz, 1, -wx
% -wy, wx, 1]
% is a legal rotation matrix, and then we combine it with current
% rotation (through matrix multiplication) to acquire the new rotation
R = Euler2Rot(current_global(2:4));
wx = params_delta(2);
wy = params_delta(3);
wz = params_delta(4);
R_delta = [1, -wz, wy;
wz, 1, -wx;
-wy, wx, 1];
% Make sure R_delta is orthonormal
R_delta = OrthonormaliseRotation(R_delta);
% Combine rotations
R_final = R * R_delta;
% Extract euler angle
euler = Rot2Euler(R_final);
rigid(2:4) = euler;
if(length(params_delta) > 6)
% non-rigid parameters can just be added together
non_rigid = params_delta(7:end) + current_non_rigid;
else
non_rigid = current_non_rigid;
end
end
function R_ortho = OrthonormaliseRotation(R)
% U * V' is basically what we want, as it's guaranteed to be
% orthonormal
[U, ~, V] = svd(R);
% We also want to make sure no reflection happened
% get the orthogonal matrix from the initial rotation matrix
X = U*V';
% This makes sure that the handedness is preserved and no reflection happened
% by making sure the determinant is 1 and not -1
W = eye(3);
W(3,3) = det(X);
R_ortho = U*W*V';
end

View File

@@ -0,0 +1,32 @@
function [ pts_new ] = iterate_piece_wise( pts_orig, new_num_points )
%ITERATE_PIECE_WISE Summary of this function goes here
% Detailed explanation goes here
% Reinterpolate the new points, but make sure they are still on the
% same lines
num_orig = size(pts_orig,1);
% Divide the number of original segments by number of new segments
step_size = (num_orig - 1) / (new_num_points-1);
pts_new = zeros(new_num_points,2);
% Clamp the beginning and end, as they will be the same
pts_new(1,:) = pts_orig(1,:);
pts_new(end,:) = pts_orig(end,:);
for i=1:new_num_points-2
low_point = floor(1 + i * step_size);
high_point = ceil(1 + i * step_size);
coeff_1 = floor(1 + i * step_size) - i * step_size;
coeff_2 = 1 - coeff_1;
new_pt = coeff_1 * pts_orig(low_point,:) + coeff_2 * pts_orig(high_point,:);
pts_new(i+1,:) = new_pt;
end
end

View File

@@ -0,0 +1,16 @@
% for easier readibility write them row by row
function writeMatrix(fileID, M, type)
fprintf(fileID, '%d\r\n', size(M,1));
fprintf(fileID, '%d\r\n', size(M,2));
fprintf(fileID, '%d\r\n', type);
for i=1:size(M,1)
if(type == 4 || type == 0)
fprintf(fileID, '%d ', M(i,:));
else
fprintf(fileID, '%.9f ', M(i,:));
end
fprintf(fileID, '\r\n');
end
end

View File

@@ -0,0 +1,37 @@
% for easier readibility write them row by row
function writeMatrixBin(fileID, M, type)
% 4 bytes each for the description
fwrite(fileID, size(M,1), 'uint');
fwrite(fileID, size(M,2), 'uint');
fwrite(fileID, type, 'uint');
% Convert the matrix to OpenCV format (row minor as opposed to column
% minor)
M = M';
% type 0 - uint8, 1 - int8, 2 - uint16, 3 - int16, 4 - int, 5 -
% float32, 6 - float64
% Write out the matrix itself
switch type
case 0
type = 'uint8';
case 1
type = 'int8';
case 2
type = 'uint16';
case 3
type = 'int16';
case 4
type = 'int';
case 5
type = 'float32';
case 6
type = 'float64';
otherwise
type = 'float32';
end
fwrite(fileID, M, type);
end