Spaces:
Running
on
L40S
Running
on
L40S
# ------------------------------------------------------------------------------ | |
# Adapted from https://github.com/akanazawa/hmr | |
# Original licence: Copyright (c) 2018 akanazawa, under the MIT License. | |
# ------------------------------------------------------------------------------ | |
import numpy as np | |
def compute_similarity_transform(source_points, | |
target_points, | |
return_tform=False): | |
"""Computes a similarity transform (sR, t) that takes a set of 3D points | |
source_points (N x 3) closest to a set of 3D points target_points, where R | |
is an 3x3 rotation matrix, t 3x1 translation, s scale. | |
And return the | |
transformed 3D points source_points_hat (N x 3). i.e. solves the orthogonal | |
Procrutes problem. | |
Notes: | |
Points number: N | |
Args: | |
source_points (np.ndarray([N, 3])): Source point set. | |
target_points (np.ndarray([N, 3])): Target point set. | |
return_tform (bool) : Whether return transform | |
Returns: | |
source_points_hat (np.ndarray([N, 3])): Transformed source point set. | |
transform (dict): Returns if return_tform is True. | |
Returns rotation: r, 'scale': s, 'translation':t. | |
""" | |
assert target_points.shape[0] == source_points.shape[0] | |
assert target_points.shape[1] == 3 and source_points.shape[1] == 3 | |
source_points = source_points.T | |
target_points = target_points.T | |
# 1. Remove mean. | |
mu1 = source_points.mean(axis=1, keepdims=True) | |
mu2 = target_points.mean(axis=1, keepdims=True) | |
X1 = source_points - mu1 | |
X2 = target_points - mu2 | |
# 2. Compute variance of X1 used for scale. | |
var1 = np.sum(X1**2) | |
# 3. The outer product of X1 and X2. | |
K = X1.dot(X2.T) | |
# 4. Solution that Maximizes trace(R'K) is R=U*V', where U, V are | |
# singular vectors of K. | |
U, _, Vh = np.linalg.svd(K) | |
V = Vh.T | |
# Construct Z that fixes the orientation of R to get det(R)=1. | |
Z = np.eye(U.shape[0]) | |
Z[-1, -1] *= np.sign(np.linalg.det(U.dot(V.T))) | |
# Construct R. | |
R = V.dot(Z.dot(U.T)) | |
# 5. Recover scale. | |
scale = np.trace(R.dot(K)) / var1 | |
# 6. Recover translation. | |
t = mu2 - scale * (R.dot(mu1)) | |
# 7. Transform the source points: | |
source_points_hat = scale * R.dot(source_points) + t | |
source_points_hat = source_points_hat.T | |
if return_tform: | |
return source_points_hat, { | |
'rotation': R, | |
'scale': scale, | |
'translation': t | |
} | |
return source_points_hat | |