|
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 |
|
|