|
|
|
|
|
|
|
|
|
|
|
|
|
import cv2 |
|
import numpy as np |
|
import quaternion |
|
from dust3r.utils.geometry import opencv_to_colmap_intrinsics |
|
from packaging import version |
|
|
|
try: |
|
import poselib |
|
|
|
HAS_POSELIB = True |
|
except Exception as e: |
|
HAS_POSELIB = False |
|
|
|
try: |
|
import pycolmap |
|
|
|
version_number = pycolmap.__version__ |
|
if version.parse(version_number) < version.parse("0.5.0"): |
|
HAS_PYCOLMAP = False |
|
else: |
|
HAS_PYCOLMAP = True |
|
except Exception as e: |
|
HAS_PYCOLMAP = False |
|
|
|
|
|
def run_pnp( |
|
pts2D, pts3D, K, distortion=None, mode="cv2", reprojectionError=5, img_size=None |
|
): |
|
""" |
|
use OPENCV model for distortion (4 values) |
|
""" |
|
assert mode in ["cv2", "poselib", "pycolmap"] |
|
try: |
|
if len(pts2D) > 4 and mode == "cv2": |
|
confidence = 0.9999 |
|
iterationsCount = 10_000 |
|
if distortion is not None: |
|
cv2_pts2ds = np.copy(pts2D) |
|
cv2_pts2ds = cv2.undistortPoints( |
|
cv2_pts2ds, K, np.array(distortion), R=None, P=K |
|
) |
|
pts2D = cv2_pts2ds.reshape((-1, 2)) |
|
|
|
success, r_pose, t_pose, _ = cv2.solvePnPRansac( |
|
pts3D, |
|
pts2D, |
|
K, |
|
None, |
|
flags=cv2.SOLVEPNP_SQPNP, |
|
iterationsCount=iterationsCount, |
|
reprojectionError=reprojectionError, |
|
confidence=confidence, |
|
) |
|
if not success: |
|
return False, None |
|
r_pose = cv2.Rodrigues(r_pose)[0] |
|
RT = np.r_[np.c_[r_pose, t_pose], [(0, 0, 0, 1)]] |
|
return True, np.linalg.inv(RT) |
|
elif len(pts2D) > 4 and mode == "poselib": |
|
assert HAS_POSELIB |
|
confidence = 0.9999 |
|
iterationsCount = 10_000 |
|
|
|
|
|
|
|
colmap_intrinsics = opencv_to_colmap_intrinsics(K) |
|
fx = colmap_intrinsics[0, 0] |
|
fy = colmap_intrinsics[1, 1] |
|
cx = colmap_intrinsics[0, 2] |
|
cy = colmap_intrinsics[1, 2] |
|
width = img_size[0] if img_size is not None else int(cx * 2) |
|
height = img_size[1] if img_size is not None else int(cy * 2) |
|
|
|
if distortion is None: |
|
camera = { |
|
"model": "PINHOLE", |
|
"width": width, |
|
"height": height, |
|
"params": [fx, fy, cx, cy], |
|
} |
|
else: |
|
camera = { |
|
"model": "OPENCV", |
|
"width": width, |
|
"height": height, |
|
"params": [fx, fy, cx, cy] + distortion, |
|
} |
|
|
|
pts2D = np.copy(pts2D) |
|
pts2D[:, 0] += 0.5 |
|
pts2D[:, 1] += 0.5 |
|
pose, _ = poselib.estimate_absolute_pose( |
|
pts2D, |
|
pts3D, |
|
camera, |
|
{ |
|
"max_reproj_error": reprojectionError, |
|
"max_iterations": iterationsCount, |
|
"success_prob": confidence, |
|
}, |
|
{}, |
|
) |
|
if pose is None: |
|
return False, None |
|
RT = pose.Rt |
|
RT = np.r_[RT, [(0, 0, 0, 1)]] |
|
return True, np.linalg.inv(RT) |
|
elif len(pts2D) > 4 and mode == "pycolmap": |
|
assert HAS_PYCOLMAP |
|
assert img_size is not None |
|
|
|
pts2D = np.copy(pts2D) |
|
pts2D[:, 0] += 0.5 |
|
pts2D[:, 1] += 0.5 |
|
colmap_intrinsics = opencv_to_colmap_intrinsics(K) |
|
fx = colmap_intrinsics[0, 0] |
|
fy = colmap_intrinsics[1, 1] |
|
cx = colmap_intrinsics[0, 2] |
|
cy = colmap_intrinsics[1, 2] |
|
width = img_size[0] |
|
height = img_size[1] |
|
if distortion is None: |
|
camera_dict = { |
|
"model": "PINHOLE", |
|
"width": width, |
|
"height": height, |
|
"params": [fx, fy, cx, cy], |
|
} |
|
else: |
|
camera_dict = { |
|
"model": "OPENCV", |
|
"width": width, |
|
"height": height, |
|
"params": [fx, fy, cx, cy] + distortion, |
|
} |
|
|
|
pycolmap_camera = pycolmap.Camera( |
|
model=camera_dict["model"], |
|
width=camera_dict["width"], |
|
height=camera_dict["height"], |
|
params=camera_dict["params"], |
|
) |
|
|
|
pycolmap_estimation_options = dict( |
|
ransac=dict( |
|
max_error=reprojectionError, |
|
min_inlier_ratio=0.01, |
|
min_num_trials=1000, |
|
max_num_trials=100000, |
|
confidence=0.9999, |
|
) |
|
) |
|
pycolmap_refinement_options = dict( |
|
refine_focal_length=False, refine_extra_params=False |
|
) |
|
ret = pycolmap.absolute_pose_estimation( |
|
pts2D, |
|
pts3D, |
|
pycolmap_camera, |
|
estimation_options=pycolmap_estimation_options, |
|
refinement_options=pycolmap_refinement_options, |
|
) |
|
if ret is None: |
|
ret = {"success": False} |
|
else: |
|
ret["success"] = True |
|
if callable(ret["cam_from_world"].matrix): |
|
retmat = ret["cam_from_world"].matrix() |
|
else: |
|
retmat = ret["cam_from_world"].matrix |
|
ret["qvec"] = quaternion.from_rotation_matrix(retmat[:3, :3]) |
|
ret["tvec"] = retmat[:3, 3] |
|
|
|
if not (ret["success"] and ret["num_inliers"] > 0): |
|
success = False |
|
pose = None |
|
else: |
|
success = True |
|
pr_world_to_querycam = np.r_[ |
|
ret["cam_from_world"].matrix(), [(0, 0, 0, 1)] |
|
] |
|
pose = np.linalg.inv(pr_world_to_querycam) |
|
return success, pose |
|
else: |
|
return False, None |
|
except Exception as e: |
|
print(f"error during pnp: {e}") |
|
return False, None |
|
|