|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import numpy as np |
|
|
|
|
|
|
|
|
def compute_similarity_transform(source_points, target_points): |
|
|
"""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. |
|
|
|
|
|
Note: |
|
|
Points number: N |
|
|
|
|
|
Args: |
|
|
source_points (np.ndarray): Source point set with shape [N, 3]. |
|
|
target_points (np.ndarray): Target point set with shape [N, 3]. |
|
|
|
|
|
Returns: |
|
|
np.ndarray: Transformed source point set with shape [N, 3]. |
|
|
""" |
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
mu1 = source_points.mean(axis=1, keepdims=True) |
|
|
mu2 = target_points.mean(axis=1, keepdims=True) |
|
|
X1 = source_points - mu1 |
|
|
X2 = target_points - mu2 |
|
|
|
|
|
|
|
|
var1 = np.sum(X1**2) |
|
|
|
|
|
|
|
|
K = X1.dot(X2.T) |
|
|
|
|
|
|
|
|
|
|
|
U, _, Vh = np.linalg.svd(K) |
|
|
V = Vh.T |
|
|
|
|
|
Z = np.eye(U.shape[0]) |
|
|
Z[-1, -1] *= np.sign(np.linalg.det(U.dot(V.T))) |
|
|
|
|
|
R = V.dot(Z.dot(U.T)) |
|
|
|
|
|
|
|
|
scale = np.trace(R.dot(K)) / var1 |
|
|
|
|
|
|
|
|
t = mu2 - scale * (R.dot(mu1)) |
|
|
|
|
|
|
|
|
source_points_hat = scale * R.dot(source_points) + t |
|
|
|
|
|
source_points_hat = source_points_hat.T |
|
|
|
|
|
return source_points_hat |
|
|
|