SRPose / utils /metrics.py
FrickYinn's picture
Upload 53 files
e170a8e verified
import numpy as np
import torch
import sklearn
from scipy import spatial
def quat_degree_error(q1, q2):
return 2 * torch.acos((q1 * q2).sum(1).abs()) * 180 / torch.pi
def rotation_angular_error(R, Rgt):
"""
Computes rotation degree error of residual rotation angle [radians]
Input:
R - estimated rotation matrix [B, 3, 3]
Rgt - groundtruth rotation matrix [B, 3, 3]
Output: degree error
"""
residual = R.transpose(1, 2) @ Rgt
trace = torch.diagonal(residual, dim1=-2, dim2=-1).sum(-1)
cosine = (trace - 1) / 2
cosine = torch.clip(cosine, -0.99999, 0.99999) # handle numerical errors and NaNs
R_err = torch.acos(cosine)
# loss = F.l1_loss(R_err, torch.zeros_like(R_err))
return R_err
def translation_angular_error(t, tgt):
cosine = torch.cosine_similarity(t, tgt)
cosine = torch.clip(cosine, -0.99999, 0.99999) # handle numerical errors and NaNs
t_err = torch.acos(cosine)
return t_err
def error_auc(errors, thresholds, mode):
"""
Args:
errors (list): [N,]
thresholds (list)
"""
errors = [0] + sorted(list(errors))
recall = list(np.linspace(0, 1, len(errors)))
aucs = []
# thresholds = [5, 10, 20]
for thr in thresholds:
last_index = np.searchsorted(errors, thr)
y = recall[:last_index] + [recall[last_index-1]]
x = errors[:last_index] + [thr]
aucs.append(np.trapz(y, x) / thr)
return {f'{mode}/auc@{t}': auc for t, auc in zip(thresholds, aucs)}
def relative_pose_error(T_0to1, R, t, ignore_gt_t_thr=0.0):
# angle error between 2 vectors
t_gt = T_0to1[:3, 3]
n = np.linalg.norm(t) * np.linalg.norm(t_gt)
t_err = np.rad2deg(np.arccos(np.clip(np.dot(t, t_gt) / n, -1.0, 1.0)))
t_err = np.minimum(t_err, 180 - t_err) # handle E ambiguity
if np.linalg.norm(t_gt) < ignore_gt_t_thr: # pure rotation is challenging
t_err = 0
# angle error between 2 rotation matrices
R_gt = T_0to1[:3, :3]
cos = (np.trace(np.dot(R.T, R_gt)) - 1) / 2
cos = np.clip(cos, -1., 1.) # handle numercial errors
R_err = np.rad2deg(np.abs(np.arccos(cos)))
return t_err, R_err
def transform_pts_Rt(pts, R, t):
"""
Applies a rigid transformation to 3D points.
:param pts: nx3 ndarray with 3D points.
:param R: 3x3 rotation matrix.
:param t: 3x1 translation vector.
:return: nx3 ndarray with transformed 3D points.
"""
assert(pts.shape[1] == 3)
pts_t = R.dot(pts.T) + t.reshape((3, 1))
return pts_t.T
def reproj(K, R_est, t_est, R_gt, t_gt, pts):
"""
reprojection error.
:param K intrinsic matrix
:param R_est, t_est: Estimated pose (3x3 rot. matrix and 3x1 trans. vector).
:param R_gt, t_gt: GT pose (3x3 rot. matrix and 3x1 trans. vector).
:param model: Object model given by a dictionary where item 'pts'
is nx3 ndarray with 3D model points.
:return: Error of pose_est w.r.t. pose_gt.
"""
pts_est = transform_pts_Rt(pts, R_est, t_est)
pts_gt = transform_pts_Rt(pts, R_gt, t_gt)
pixels_est = K.dot(pts_est.T)
pixels_est = pixels_est.T
pixels_gt = K.dot(pts_gt.T)
pixels_gt = pixels_gt.T
n = pts.shape[0]
est = np.zeros((n, 2), dtype=np.float32);
est[:, 0] = np.divide(pixels_est[:, 0], pixels_est[:, 2])
est[:, 1] = np.divide(pixels_est[:, 1], pixels_est[:, 2])
gt = np.zeros((n, 2), dtype=np.float32);
gt[:, 0] = np.divide(pixels_gt[:, 0], pixels_gt[:, 2])
gt[:, 1] = np.divide(pixels_gt[:, 1], pixels_gt[:, 2])
e = np.linalg.norm(est - gt, axis=1).mean()
return e
def add(R_est, t_est, R_gt, t_gt, pts):
"""
Average Distance of Model Points for objects with no indistinguishable views
- by Hinterstoisser et al. (ACCV 2012).
:param R_est, t_est: Estimated pose (3x3 rot. matrix and 3x1 trans. vector).
:param R_gt, t_gt: GT pose (3x3 rot. matrix and 3x1 trans. vector).
:param model: Object model given by a dictionary where item 'pts'
is nx3 ndarray with 3D model points.
:return: Error of pose_est w.r.t. pose_gt.
"""
pts_est = transform_pts_Rt(pts, R_est, t_est)
pts_gt = transform_pts_Rt(pts, R_gt, t_gt)
e = np.linalg.norm(pts_est - pts_gt, axis=1).mean()
return e
def adi(R_est, t_est, R_gt, t_gt, pts):
"""
Average Distance of Model Points for objects with indistinguishable views
- by Hinterstoisser et al. (ACCV 2012).
:param R_est, t_est: Estimated pose (3x3 rot. matrix and 3x1 trans. vector).
:param R_gt, t_gt: GT pose (3x3 rot. matrix and 3x1 trans. vector).
:param model: Object model given by a dictionary where item 'pts'
is nx3 ndarray with 3D model points.
:return: Error of pose_est w.r.t. pose_gt.
"""
pts_est = transform_pts_Rt(pts, R_est, t_est)
pts_gt = transform_pts_Rt(pts, R_gt, t_gt)
# Calculate distances to the nearest neighbors from pts_gt to pts_est
nn_index = spatial.cKDTree(pts_est)
nn_dists, _ = nn_index.query(pts_gt, k=1)
e = nn_dists.mean()
return e
def compute_continuous_auc(metrics, thresholds):
x_range = thresholds.max() - thresholds.min()
results = np.array(metrics)
accuracies = [(results <= t).sum() / len(results) for t in thresholds]
auc = sklearn.metrics.auc(thresholds, accuracies) / x_range
return auc