-
Notifications
You must be signed in to change notification settings - Fork 1
/
common_lab_utils.py
171 lines (128 loc) · 5.27 KB
/
common_lab_utils.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import numpy as np
from dataclasses import dataclass
import cv2
from pylie import SE3
class Size:
"""Represents image size"""
def __init__(self, width: float, height: float):
self._width = width
self._height = height
def __str__(self):
return f"w: {self._width}, h: {self._height}"
@classmethod
def from_numpy_shape(cls, shape):
return cls(*shape[1::-1])
@property
def width(self):
return self._width
@property
def height(self):
return self._height
@property
def as_cv_size(self):
return np.array((self._width, self._height), dtype=int)
@dataclass
class StereoPair:
left: np.ndarray
right: np.ndarray
def __iter__(self):
print("iter")
return iter((self.left, self.right))
class StereoMatchingResult:
def __init__(self, keypoints_left=(), keypoints_right=(), matches=()):
self._keypoints_left = keypoints_left
self._keypoints_right = keypoints_right
self._matches = matches
@property
def keypoints_left(self):
return self._keypoints_left
@property
def keypoints_right(self):
return self._keypoints_right
@property
def matches(self):
return self._matches
class CvStereoMatcherWrap:
def __init__(self, matcher):
self._matcher = matcher
def compute(self, stereo_rectified: StereoPair):
num_disparities = self._matcher.getNumDisparities()
padded_l = cv2.copyMakeBorder(stereo_rectified.left, 0, 0, num_disparities, 0, cv2.BORDER_CONSTANT,
value=(0, 0, 0))
padded_r = cv2.copyMakeBorder(stereo_rectified.right, 0, 0, num_disparities, 0, cv2.BORDER_CONSTANT,
value=(0, 0, 0))
disparity_padded = self._matcher.compute(padded_l, padded_r)
# Unpad.
pixels_to_unpad_left = num_disparities
pixels_to_unpad_right = padded_l.shape[1]
disparity_16bit_fixed = disparity_padded[:, pixels_to_unpad_left:pixels_to_unpad_right]
# Convert from 16 bit fixed point.
ratio_1_over_16 = 1.0 / 16.0
disparity = (disparity_16bit_fixed * ratio_1_over_16).astype(np.float32)
return disparity
def homogeneous(x):
"""Transforms Cartesian column vectors to homogeneous column vectors"""
return np.r_[x, [np.ones(x.shape[1])]]
def hnormalized(x):
"""Transforms homogeneous column vector to Cartesian column vectors"""
return x[:-1] / x[-1]
class PerspectiveCamera:
"""Camera model for the perspective camera"""
def __init__(self,
calibration_matrix: np.ndarray,
distortion_coeffs: np.ndarray,
pose_world_camera: SE3):
"""Constructs the camera model.
:param calibration_matrix: The intrinsic calibration matrix.
:param distortion_coeffs: Distortion coefficients on the form [k1, k2, p1, p2, k3].
:param pose_world_camera: The pose of the camera in the world coordinate system.
"""
self._calibration_matrix = calibration_matrix
self._calibration_matrix_inv = np.linalg.inv(calibration_matrix)
self._distortion_coeffs = distortion_coeffs
self._pose_world_camera = pose_world_camera
self._camera_projection_matrix = self._compute_camera_projection_matrix()
def _compute_camera_projection_matrix(self):
return self._calibration_matrix @ self._pose_world_camera.inverse().to_matrix()[:3, :]
def project_world_point(self, point_world):
"""Projects a world point into pixel coordinates.
:param point_world: A 3D point in world coordinates.
"""
if point_world.ndim == 1:
# Convert to column vector.
point_world = point_world[:, np.newaxis]
return hnormalized(self._camera_projection_matrix @ homogeneous(point_world))
def undistort_image(self, distorted_image):
"""Undistorts an image corresponding to the camera model.
:param distorted_image: The original, distorted image.
:returns: The undistorted image.
"""
return cv2.undistort(distorted_image, self._calibration_matrix, self._distortion_coeffs)
def pixel_to_normalised(self, point_pixel):
"""Transform a pixel coordinate to normalised coordinates
:param point_pixel: The 2D point in the image given in pixels.
"""
if point_pixel.ndim == 1:
# Convert to column vector.
point_pixel = point_pixel[:, np.newaxis]
return self._calibration_matrix_inv @ homogeneous(point_pixel)
@property
def pose_world_camera(self):
"""The pose of the camera in world coordinates."""
return self._pose_world_camera
@property
def calibration_matrix(self):
"""The intrinsic calibration matrix K."""
return self._calibration_matrix
@property
def calibration_matrix_inv(self):
"""The inverse calibration matrix K^{-1}."""
return self._calibration_matrix_inv
@property
def distortion_coeffs(self):
"""The distortion coefficients on the form [k1, k2, p1, p2, k3]."""
return self._distortion_coeffs
@property
def projection_matrix(self):
"""The projection matrix P."""
return self._camera_projection_matrix