import numpy as np import torch from utils.utils_poses.ATE.align_utils import alignTrajectory from utils.utils_poses.lie_group_helper import SO3_to_quat, convert3x4_4x4 def pts_dist_max(pts): """ :param pts: (N, 3) torch or np :return: scalar """ if torch.is_tensor(pts): dist = pts.unsqueeze(0) - pts.unsqueeze(1) # (1, N, 3) - (N, 1, 3) -> (N, N, 3) dist = dist[0] # (N, 3) dist = dist.norm(dim=1) # (N, ) max_dist = dist.max() else: dist = pts[None, :, :] - pts[:, None, :] # (1, N, 3) - (N, 1, 3) -> (N, N, 3) dist = dist[0] # (N, 3) dist = np.linalg.norm(dist, axis=1) # (N, ) max_dist = dist.max() return max_dist def align_ate_c2b_use_a2b(traj_a, traj_b, traj_c=None, method='sim3'): """Align c to b using the sim3 from a to b. :param traj_a: (N0, 3/4, 4) torch tensor :param traj_b: (N0, 3/4, 4) torch tensor :param traj_c: None or (N1, 3/4, 4) torch tensor :return: (N1, 4, 4) torch tensor """ device = traj_a.device if traj_c is None: traj_c = traj_a.clone() traj_a = traj_a.float().cpu().numpy() traj_b = traj_b.float().cpu().numpy() traj_c = traj_c.float().cpu().numpy() R_a = traj_a[:, :3, :3] # (N0, 3, 3) t_a = traj_a[:, :3, 3] # (N0, 3) quat_a = SO3_to_quat(R_a) # (N0, 4) R_b = traj_b[:, :3, :3] # (N0, 3, 3) t_b = traj_b[:, :3, 3] # (N0, 3) quat_b = SO3_to_quat(R_b) # (N0, 4) # This function works in quaternion. # scalar, (3, 3), (3, ) gt = R * s * est + t. s, R, t = alignTrajectory(t_a, t_b, quat_a, quat_b, method=method) # reshape tensors R = R[None, :, :].astype(np.float32) # (1, 3, 3) t = t[None, :, None].astype(np.float32) # (1, 3, 1) s = float(s) R_c = traj_c[:, :3, :3] # (N1, 3, 3) t_c = traj_c[:, :3, 3:4] # (N1, 3, 1) R_c_aligned = R @ R_c # (N1, 3, 3) t_c_aligned = s * (R @ t_c) + t # (N1, 3, 1) traj_c_aligned = np.concatenate([R_c_aligned, t_c_aligned], axis=2) # (N1, 3, 4) # append the last row traj_c_aligned = convert3x4_4x4(traj_c_aligned) # (N1, 4, 4) traj_c_aligned = torch.from_numpy(traj_c_aligned).to(device) return traj_c_aligned # (N1, 4, 4) def align_scale_c2b_use_a2b(traj_a, traj_b, traj_c=None): '''Scale c to b using the scale from a to b. :param traj_a: (N0, 3/4, 4) torch tensor :param traj_b: (N0, 3/4, 4) torch tensor :param traj_c: None or (N1, 3/4, 4) torch tensor :return: scaled_traj_c (N1, 4, 4) torch tensor scale scalar ''' if traj_c is None: traj_c = traj_a.clone() t_a = traj_a[:, :3, 3] # (N, 3) t_b = traj_b[:, :3, 3] # (N, 3) # scale estimated poses to colmap scale # s_a2b: a*s ~ b scale_a2b = pts_dist_max(t_b) / pts_dist_max(t_a) traj_c[:, :3, 3] *= scale_a2b if traj_c.shape[1] == 3: traj_c = convert3x4_4x4(traj_c) # (N, 4, 4) return traj_c, scale_a2b # (N, 4, 4)