| import pickle |
| from pathlib import Path |
|
|
| import numpy as np |
| import poselib |
| import pycolmap |
|
|
| from siclib.models.extractor import VP |
|
|
| from .models.extractor import GeoCalib |
| from .utils.image import load_image |
|
|
| |
| |
|
|
|
|
| class AbsolutePoseEstimator: |
| default_opts = { |
| "ransac": "poselib_gravity", |
| "refinement": "pycolmap_gravity", |
| "gravity_weight": 50000, |
| "max_reproj_error": 48.0, |
| "loss_function_scale": 1.0, |
| "use_vp": False, |
| "max_uncertainty": 10.0 / 180.0 * 3.1415, |
| "cache_path": "../../outputs/inloc/calib.pickle", |
| } |
|
|
| def __init__(self, pose_opts=None): |
| pose_opts = {} if pose_opts is None else pose_opts |
| self.opts = {**self.default_opts, **pose_opts} |
| self.device = "cuda" |
|
|
| if self.opts["use_vp"]: |
| self.calib = VP().to(self.device) |
| self.cache_path = str(self.opts["cache_path"]).replace(".pickle", "_vp.pickle") |
| else: |
| self.calib = GeoCalib().to(self.device) |
| self.cache_path = str(self.opts["cache_path"]) |
|
|
| |
| self.cache = {} |
|
|
| def read_cache(self): |
| print(f"Reading cache from {self.cache_path} ({Path(self.cache_path).exists()})") |
| if not Path(self.cache_path).exists(): |
| self.cache = {} |
| return |
| with open(self.cache_path, "rb") as handle: |
| self.cache = pickle.load(handle) |
|
|
| def write_cache(self): |
| with open(self.cache_path, "wb") as handle: |
| pickle.dump(self.cache, handle, protocol=pickle.HIGHEST_PROTOCOL) |
|
|
| def __call__(self, query_path, p2d, p3d, camera_dict): |
| focal_length = pycolmap.Camera(camera_dict).mean_focal_length() |
|
|
| if query_path in self.cache: |
| calib = self.cache[query_path] |
| else: |
| calib = self.calib.calibrate( |
| load_image(query_path).to(self.device), priors={"f": focal_length} |
| ) |
| calib = {k: v[0].detach().cpu().numpy() for k, v in calib.items()} |
| self.cache[query_path] = calib |
| |
|
|
| if self.opts["ransac"] == "pycolmap": |
| ret = pycolmap.absolute_pose_estimation( |
| p2d, p3d, camera_dict, self.opts["max_reproj_error"] |
| ) |
| elif self.opts["ransac"] == "poselib": |
| M, ret = poselib.estimate_absolute_pose( |
| p2d, |
| p3d, |
| camera_dict, |
| ransac_opt={"max_reproj_error": self.opts["max_reproj_error"]}, |
| ) |
| ret["success"] = M is not None |
| ret["qvec"] = M.q |
| ret["tvec"] = M.t |
| elif self.opts["ransac"] == "poselib_gravity": |
| g_q = calib["gravity"].vec3d |
| g_qu = calib.get("gravity_uncertainty", self.opts["max_uncertainty"]) |
| M, ret = poselib.estimate_absolute_pose_gravity( |
| p2d, |
| p3d, |
| camera_dict, |
| g_q, |
| g_qu * 2 * 180 / 3.1415, |
| ransac_opt={"max_reproj_error": self.opts["max_reproj_error"]}, |
| ) |
| ret["success"] = M is not None |
| ret["qvec"] = M.q |
| ret["tvec"] = M.t |
| else: |
| raise NotImplementedError(self.opts["ransac"]) |
| r_opts = { |
| "refine_focal_length": False, |
| "refine_extra_params": False, |
| "print_summary": False, |
| "loss_function_scale": self.opts["loss_function_scale"], |
| } |
| if self.opts["refinement"] == "pycolmap_gravity": |
| g_q = calib["gravity"].vec3d |
| g_qu = calib.get("gravity_uncertainty", self.opts["max_uncertainty"]) |
| if g_qu <= self.opts["max_uncertainty"]: |
| g_gt = np.array([0, 0, 1]) |
| ret_ref = pycolmap.pose_refinement_gravity( |
| ret["tvec"], |
| ret["qvec"], |
| p2d, |
| p3d, |
| ret["inliers"], |
| camera_dict, |
| g_q, |
| g_gt, |
| self.opts["gravity_weight"], |
| r_opts, |
| ) |
| else: |
| ret_ref = pycolmap.pose_refinement( |
| ret["tvec"], |
| ret["qvec"], |
| p2d, |
| p3d, |
| ret["inliers"], |
| camera_dict, |
| r_opts, |
| ) |
| elif self.opts["refinement"] == "pycolmap": |
| ret_ref = pycolmap.pose_refinement( |
| ret["tvec"], |
| ret["qvec"], |
| p2d, |
| p3d, |
| ret["inliers"], |
| camera_dict, |
| r_opts, |
| ) |
| elif self.opts["refinement"] == "none": |
| ret_ref = {} |
| else: |
| raise NotImplementedError(self.opts["refinement"]) |
| ret = {**ret, **ret_ref} |
| ret["camera_dict"] = camera_dict |
| return ret, calib |
|
|