Spaces:
Running
on
Zero
Running
on
Zero
| import numpy as np | |
| import torch | |
| import poselib | |
| 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.0, 1.0) # handle numercial errors | |
| R_err = np.rad2deg(np.abs(np.arccos(cos))) | |
| return t_err, R_err | |
| def intrinsics_to_camera(K): | |
| px, py = K[0, 2], K[1, 2] | |
| fx, fy = K[0, 0], K[1, 1] | |
| return { | |
| "model": "PINHOLE", | |
| "width": int(2 * px), | |
| "height": int(2 * py), | |
| "params": [fx, fy, px, py], | |
| } | |
| def estimate_pose(kpts0, kpts1, K0, K1, thresh, conf=0.99999): | |
| M, info = poselib.estimate_relative_pose( | |
| kpts0, kpts1, | |
| intrinsics_to_camera(K0), | |
| intrinsics_to_camera(K1), | |
| {"max_epipolar_error": thresh, | |
| "success_prob": conf, | |
| "min_iterations": 20, | |
| "max_iterations": 1_000}, | |
| ) | |
| R, t, inl = M.R, M.t, info["inliers"] | |
| inl = np.array(inl) | |
| ret = (R, t, inl) | |
| return ret | |
| def tensor2bgr(t): | |
| return (t.cpu()[0].permute(1,2,0).numpy()*255).astype(np.uint8) | |
| def compute_pose_error(match_fn,data): | |
| result = {} | |
| with torch.no_grad(): | |
| mkpts0,mkpts1=match_fn(tensor2bgr(data["image0"]),tensor2bgr(data["image1"])) | |
| mkpts0=mkpts0 * data["scale0"].numpy() | |
| mkpts1=mkpts1 * data["scale1"].numpy() | |
| K0, K1 = data["K0"][0].numpy(), data["K1"][0].numpy() | |
| T_0to1 = data["T_0to1"][0].numpy() | |
| T_1to0 = data["T_1to0"][0].numpy() | |
| result={} | |
| conf = 0.99999 | |
| ret = estimate_pose(mkpts0,mkpts1,K0,K1,4.0,conf) | |
| if ret is not None: | |
| R, t, inliers = ret | |
| t_err, R_err = relative_pose_error(T_0to1, R, t, ignore_gt_t_thr=0.0) | |
| result['R_err'] = R_err | |
| result['t_err'] = t_err | |
| return result | |
| def error_auc(errors, thresholds=[5, 10, 20]): | |
| """ | |
| Args: | |
| errors (list): [N,] | |
| thresholds (list) | |
| """ | |
| errors = [0] + sorted(list(errors)) | |
| recall = list(np.linspace(0, 1, len(errors))) | |
| aucs = [] | |
| 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'auc@{t}': auc for t, auc in zip(thresholds, aucs)} | |
| def compute_maa(pairs, thresholds=[5, 10, 20]): | |
| # print("auc / mAcc on %d pairs" % (len(pairs))) | |
| errors = [] | |
| for p in pairs: | |
| et = p['t_err'] | |
| er = p['R_err'] | |
| errors.append(max(et, er)) | |
| d_err_auc = error_auc(errors) | |
| # for k,v in d_err_auc.items(): | |
| # print(k, ': ', '%.1f'%(v*100)) | |
| errors = np.array(errors) | |
| for t in thresholds: | |
| acc = (errors <= t).sum() / len(errors) | |
| # print("mAcc@%d: %.1f "%(t, acc*100)) | |
| return d_err_auc,errors | |
| def homo_trans(coord, H): | |
| kpt_num = coord.shape[0] | |
| homo_coord = np.concatenate((coord, np.ones((kpt_num, 1))), axis=-1) | |
| proj_coord = np.matmul(H, homo_coord.T).T | |
| proj_coord = proj_coord / proj_coord[:, 2][..., None] | |
| proj_coord = proj_coord[:, 0:2] | |
| return proj_coord | |