forked from mapillary/OpenSfM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypes.py
359 lines (280 loc) · 12.2 KB
/
types.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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
"""Basic types for building a reconstruction."""
from typing import Dict, Optional
import numpy as np
from opensfm import pygeometry, pymap
from opensfm.geo import TopocentricConverter
PANOSHOT_RIG_PREFIX = "panoshot_"
class ShotMesh(object):
"""Triangular mesh of points visible in a shot
Attributes:
vertices: (list of vectors) mesh vertices
faces: (list of triplets) triangles' topology
"""
def __init__(self):
self.vertices = None
self.faces = None
class Reconstruction(object):
"""Defines the reconstructed scene.
Attributes:
cameras (Dict(Camera)): List of cameras.
shots (Dict(Shot)): List of reconstructed shots.
points (Dict(Point)): List of reconstructed points.
reference (TopocentricConverter): Topocentric reference converter.
"""
def __init__(self) -> None:
"""Defaut constructor"""
self._setup_from_map(pymap.Map())
def _setup_from_map(self, map_obj: pymap.Map):
self.map = map_obj
self.camera_view = pymap.CameraView(self.map)
self.bias_view = pymap.BiasView(self.map)
self.rig_cameras_view = pymap.RigCameraView(self.map)
self.rig_instances_view = pymap.RigInstanceView(self.map)
self.shot_view = pymap.ShotView(self.map)
self.pano_shot_view = pymap.PanoShotView(self.map)
self.landmark_view = pymap.LandmarkView(self.map)
def __repr__(self):
return (
"<Reconstruction"
f" cameras={len(self.cameras)}"
f" shots={len(self.shots)}"
f" points={len(self.points)}"
f" rig_cameras={len(self.rig_cameras)}"
f" rig_instances={len(self.rig_instances)}"
">"
)
def get_cameras(self) -> pymap.CameraView:
return self.camera_view
def set_cameras(self, value: Dict[str, pygeometry.Camera]) -> None:
for cam in value.values():
self.map.create_camera(cam)
cameras = property(get_cameras, set_cameras)
def get_biases(self) -> pymap.BiasView:
return self.bias_view
def set_biases(self, value: Dict[str, pygeometry.Similarity]) -> None:
for cam_id, bias in value.items():
self.map.set_bias(cam_id, bias)
def set_bias(self, cam_id: str, bias: pygeometry.Similarity) -> None:
self.map.set_bias(cam_id, bias)
biases = property(get_biases, set_biases)
def get_rig_cameras(self) -> pymap.RigCameraView:
return self.rig_cameras_view
def set_rig_cameras(self, values: Dict[str, pymap.RigCamera]) -> None:
for rig_camera in values.values():
self.map.create_rig_camera(rig_camera)
rig_cameras = property(get_rig_cameras, set_rig_cameras)
def get_rig_instances(self) -> pymap.RigInstanceView:
return self.rig_instances_view
def set_rig_instances(self, values: Dict[str, pymap.RigInstance]) -> None:
for rig_instance in values.values():
self.add_rig_instance(rig_instance)
def remove_rig_instance(self, rig_instance_id: str) -> None:
self.map.remove_rig_instance(rig_instance_id)
rig_instances = property(get_rig_instances, set_rig_instances)
def get_shots(self) -> pymap.ShotView:
return self.shot_view
def set_shots(self, value: Dict[str, pymap.Shot]) -> None:
for shot in value.values():
self.add_shot(shot)
shots = property(get_shots, set_shots)
def get_pano_shots(self) -> pymap.PanoShotView:
return self.pano_shot_view
def set_pano_shots(self, value: Dict[str, pymap.Shot]) -> None:
for shot in value.values():
self.add_pano_shot(shot)
pano_shots = property(get_pano_shots, set_pano_shots)
def get_points(self) -> pymap.LandmarkView:
return self.landmark_view
def set_points(self, value: Dict[str, pymap.Landmark]) -> None:
self.map.clear_observations_and_landmarks()
for point in value.values():
self.add_point(point)
def remove_point(self, point_id: str) -> None:
self.map.remove_landmark(point_id)
points = property(get_points, set_points)
def get_reference(self) -> TopocentricConverter:
ref = self.map.get_reference()
return TopocentricConverter(ref.lat, ref.lon, ref.alt)
def set_reference(self, value: TopocentricConverter) -> None:
self.map.set_reference(value.lat, value.lon, value.alt)
reference = property(get_reference, set_reference)
# Cameras
def add_camera(self, camera: pygeometry.Camera) -> pygeometry.Camera:
"""Add a camera in the list
:param camera: The camera.
"""
if camera.id not in self.cameras:
return self.map.create_camera(camera)
else:
return self.get_camera(camera.id)
def get_camera(self, id: str) -> pygeometry.Camera:
"""Return a camera by id.
:return: If exists returns the camera, otherwise None.
"""
return self.cameras.get(id)
# Rigs
def add_rig_camera(self, rig_camera: pymap.RigCamera) -> pymap.RigCamera:
"""Add a rig camera in the list
:param rig_camera: The rig camera.
"""
if rig_camera.id not in self.rig_cameras:
return self.map.create_rig_camera(rig_camera)
else:
return self.rig_cameras.get(rig_camera.id)
def add_rig_instance(self, rig_instance: pymap.RigInstance) -> pymap.RigInstance:
"""Creates a copy of the passed rig instance
in the current reconstruction"""
for camera in rig_instance.rig_cameras.values():
if camera.id not in self.rig_cameras:
self.map.create_rig_camera(camera)
in_any_instance = any(
(set(rig_instance.shots) & set(ri.shots))
for ri in self.rig_instances.values()
)
if in_any_instance:
raise RuntimeError("Shots already exist in another instance")
if rig_instance.id not in self.rig_instances:
self.map.create_rig_instance(rig_instance.id)
return self.map.update_rig_instance(rig_instance)
# Shot
def create_shot(
self,
shot_id: str,
camera_id: str,
pose: Optional[pygeometry.Pose] = None,
rig_camera_id: Optional[str] = None,
rig_instance_id: Optional[str] = None,
) -> pymap.Shot:
passed_rig_camera_id = rig_camera_id if rig_camera_id else camera_id
passed_rig_instance_id = rig_instance_id if rig_instance_id else shot_id
if (not rig_camera_id) and (camera_id not in self.rig_cameras):
self.add_rig_camera(pymap.RigCamera(pygeometry.Pose(), camera_id))
if passed_rig_camera_id not in self.rig_cameras:
raise RuntimeError(
f"Rig Camera {passed_rig_camera_id} doesn't exist in reconstruction"
)
if (not rig_instance_id) and (shot_id not in self.rig_instances):
self.add_rig_instance(pymap.RigInstance(shot_id))
if passed_rig_instance_id not in self.rig_instances:
raise RuntimeError(
f"Rig Instance {passed_rig_instance_id} doesn't exist in reconstruction"
)
if pose is None:
created_shot = self.map.create_shot(
shot_id, camera_id, passed_rig_camera_id, passed_rig_instance_id
)
else:
created_shot = self.map.create_shot(
shot_id, camera_id, passed_rig_camera_id, passed_rig_instance_id, pose
)
return created_shot
def add_shot(self, shot: pymap.Shot) -> pymap.Shot:
"""Creates a copy of the passed shot in the current reconstruction
If the shot belong to a Rig, we recursively copy the entire rig
instance, so rigs stay consistents.
"""
if shot.camera.id not in self.cameras:
self.add_camera(shot.camera)
if shot.rig_instance_id not in self.rig_instances:
self.map.create_rig_instance(shot.rig_instance_id)
if shot.rig_camera_id not in self.rig_cameras:
self.map.create_rig_camera(shot.rig_camera)
if shot.id not in self.shots:
self.map.create_shot(
shot.id,
shot.camera.id,
shot.rig_camera_id,
shot.rig_instance_id,
shot.pose,
)
ret = self.map.update_shot(shot)
return ret
def get_shot(self, id: str) -> pymap.Shot:
"""Return a shot by id.
:return: If exists returns the shot, otherwise None.
"""
return self.shots.get(id)
def remove_shot(self, shot_id: str) -> None:
self.map.remove_shot(shot_id)
# PanoShot
def create_pano_shot(
self, shot_id: str, camera_id: str, pose: Optional[pygeometry.Pose] = None
) -> pymap.Shot:
if pose is None:
pose = pygeometry.Pose()
rig_camera_id = f"{PANOSHOT_RIG_PREFIX}{camera_id}"
if rig_camera_id not in self.rig_cameras:
self.add_rig_camera(pymap.RigCamera(pygeometry.Pose(), rig_camera_id))
rig_instance_id = f"{PANOSHOT_RIG_PREFIX}{shot_id}"
if rig_instance_id not in self.rig_instances:
self.add_rig_instance(pymap.RigInstance(rig_instance_id))
created_shot = self.map.create_pano_shot(
shot_id, camera_id, rig_camera_id, rig_instance_id, pose
)
return created_shot
def add_pano_shot(self, pshot: pymap.Shot) -> pymap.Shot:
if pshot.camera.id not in self.cameras:
self.add_camera(pshot.camera)
if pshot.id not in self.pano_shots:
self.create_pano_shot(pshot.id, pshot.camera.id, pshot.pose)
return self.map.update_pano_shot(pshot)
def get_pano_shot(self, id: str) -> pymap.Shot:
"""Return a shot by id.
:return: If exists returns the shot, otherwise None.
"""
return self.pano_shots.get(id)
def remove_pano_shot(self, shot_id: str) -> None:
self.map.remove_pano_shot(shot_id)
def create_point(
self, point_id: str, coord: Optional[np.ndarray] = None
) -> pymap.Landmark:
if coord is None:
return self.map.create_landmark(point_id, np.array([0, 0, 0]))
return self.map.create_landmark(point_id, coord)
def add_point(self, point: pymap.Landmark) -> pymap.Landmark:
"""Add a point in the list
:param point: The point.
"""
if point.coordinates is None:
new_pt = self.map.create_landmark(point.id, np.array([0, 0, 0]))
else:
new_pt = self.map.create_landmark(point.id, point.coordinates)
if point.color is not None:
new_pt.color = point.color
return new_pt
def get_point(self, id: str) -> pymap.Landmark:
"""Return a point by id.
:return: If exists returns the point, otherwise None.
"""
return self.points.get(id)
def add_observation(
self, shot_id: str, lm_id: str, observation: pymap.Observation
) -> None:
"""Adds an observation between a shot and a landmark
:param shot_id: The id of the shot
:param lm_id: The id of the landmark
:param observation: The observation
"""
self.map.add_observation(shot_id, lm_id, observation)
def remove_observation(self, shot_id: str, lm_id: str) -> None:
self.map.remove_observation(shot_id, lm_id)
def __deepcopy__(self, d):
rec_cpy = Reconstruction()
copy_observations = False
# Check if we also need the observations
if "copy_observations" in d:
copy_observations = d["copy_observations"]
map_copy = pymap.Map.deep_copy(self.map, copy_observations)
rec_cpy._setup_from_map(map_copy)
return rec_cpy
def add_correspondences_from_tracks_manager(
self, tracks_manager: pymap.TracksManager
) -> None:
for track_id in tracks_manager.get_track_ids():
if track_id not in self.points:
continue
track_obs = tracks_manager.get_track_observations(track_id)
for shot_id in track_obs.keys():
if shot_id in self.shots:
observation = tracks_manager.get_observation(shot_id, track_id)
self.add_observation(shot_id, track_id, observation)