-
Notifications
You must be signed in to change notification settings - Fork 0
/
pnp.py
101 lines (71 loc) · 2.8 KB
/
pnp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import numpy as np
from spatialmath.pose3d import SE3
from spatialmath.base import trexp
def pnp(points_3d, points_2d, K):
iterations = 10
pose = SE3()
cost, last_cost = 0, 0
fx = K[0, 0]
fy = K[1, 1]
cx = K[0, 2]
cy = K[1, 2]
for iter in range(iterations):
H = np.zeros(shape=(6, 6))
b = np.zeros(shape=6)
cost = 0
for i in range(len(points_3d)):
pc = (pose * points_3d[i]).flatten()
inv_z = 1.0 / pc[2]
inv_z2 = inv_z * inv_z
proj = np.array([fx * pc[0] / pc[2] + cx, fy * pc[1] / pc[2] + cy])
e = points_2d[i] - proj
cost += np.linalg.norm(e) ** 2
J = np.array([
[- fx * inv_z, 0, fx * pc[0] * inv_z2, fx * pc[0] * pc[1] * inv_z2, - fx - fx * pc[0] * pc[0] * inv_z2, fx * pc[1] * inv_z],
[0, - fy * inv_z, fy * pc[1] * inv_z, fy + fy * pc[1] * pc[1] * inv_z2, - fy * pc[0] * pc[1] * inv_z2, - fy * pc[0] * inv_z]
])
H += J.T @ J
b += - J.T @ e
try:
dx = np.linalg.solve(H, b)
except np.linalg.LinAlgError:
break
if iter > 0 and cost >= last_cost:
break
pose = SE3.Delta(dx) * pose
last_cost = cost
# print(f"Iter {iter}, cost = {cost}")
if np.linalg.norm(dx) < 1e-6:
break
# print(f"Pose:\n{pose}")
return pose.A
def homogenize(a: np.ndarray) -> np.ndarray:
if a.ndim == 1:
return np.hstack((a, 1))
return np.hstack((a, np.ones((len(a), 1))))
def world_to_camera(points: np.ndarray, pose) -> np.ndarray:
return homogenize(points) @ pose[:-1].T
def camera_to_pixel(points: np.ndarray, intrinsics: np.ndarray) -> np.ndarray:
points_pixel = points @ intrinsics.T
points_pixel /= points_pixel[:, 2:3]
return points_pixel[:, :-1]
def world_to_pixel(points: np.ndarray, pose, intrinsics: np.ndarray) -> np.ndarray:
return camera_to_pixel(world_to_camera(points, pose), intrinsics)
def pnp_ransac(points_3d, points_2d, K, threshold, iterations=100, sample_size=3):
best_pose = None
best_inliers = []
num_points = len(points_3d)
for _ in range(iterations):
# Randomly sample a subset of points
sample_indices = np.random.choice(num_points, sample_size, replace=False)
sampled_points_3d = points_3d[sample_indices]
sampled_points_2d = points_2d[sample_indices]
pose = pnp(sampled_points_3d, sampled_points_2d, K)
# inliers = []
proj = world_to_pixel(points_3d, pose, K)
error = np.linalg.norm(points_2d - proj, axis=1)
inliers = np.where(error <= threshold)[0]
if len(inliers) > len(best_inliers):
best_pose = pose
best_inliers = inliers
return best_pose