Skip to content

Commit c581cc1

Browse files
Pau Gargallofacebook-github-bot
authored andcommitted
annotate synthetic data
Reviewed By: DodgySpaniard Differential Revision: D76518688 fbshipit-source-id: 66e8d3ea147b5a500d2e7f51e0a1f1bb61cf21db
1 parent 9b5be94 commit c581cc1

File tree

5 files changed

+79
-77
lines changed

5 files changed

+79
-77
lines changed

opensfm/synthetic_data/synthetic_dataset.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
# pyre-unsafe
1+
# pyre-strict
22
import collections
33
import logging
44
import os
55
import shelve
6-
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
6+
from typing import Any, Dict, Iterator, List, MutableMapping, Optional, Tuple
77

88
import numpy as np
9+
from numpy.typing import NDArray
910
from opensfm import features as oft, geo, io, pygeometry, pymap, tracking, types
1011
from opensfm.dataset import DataSet
1112

1213
logger: logging.Logger = logging.getLogger(__name__)
1314

1415

1516
class SyntheticFeatures(collections.abc.MutableMapping):
16-
database: Union[Dict[str, oft.FeaturesData], shelve.Shelf]
17+
database: MutableMapping[str, oft.FeaturesData]
1718

1819
def __init__(self, on_disk_filename: Optional[str]) -> None:
1920
if on_disk_filename:
@@ -28,16 +29,16 @@ def sync(self) -> None:
2829
database = self.database
2930
if type(database) is dict:
3031
return
31-
else:
32+
elif type(database) is shelve.Shelf:
3233
database.sync()
3334

34-
def __getitem__(self, key) -> oft.FeaturesData:
35+
def __getitem__(self, key: str) -> oft.FeaturesData:
3536
return self.database.__getitem__(key)
3637

37-
def __setitem__(self, key, item) -> None:
38+
def __setitem__(self, key: str, item: oft.FeaturesData) -> None:
3839
return self.database.__setitem__(key, item)
3940

40-
def __delitem__(self, key) -> None:
41+
def __delitem__(self, key: str) -> None:
4142
return self.database.__delitem__(key)
4243

4344
def __iter__(self) -> Iterator[str]:
@@ -73,16 +74,16 @@ def __init__(
7374
self.gcps = gcps
7475
self.features = features
7576
self.tracks_manager = tracks_manager
76-
self.image_list = list(reconstruction.shots.keys())
77+
self.image_list: List[str] = list(reconstruction.shots.keys())
7778
self.reference = reconstruction.reference
78-
self.matches = None
79+
self.matches: Optional[Dict[str, Dict[str, NDArray]]] = None
7980
self.config["use_altitude_tag"] = True
8081
self.config["align_method"] = "naive"
8182

8283
def images(self) -> List[str]:
8384
return self.image_list
8485

85-
def _raise_if_absent_image(self, image: str):
86+
def _raise_if_absent_image(self, image: str) -> None:
8687
if image not in self.image_list:
8788
raise RuntimeError("Image isn't present in the synthetic dataset")
8889

@@ -121,10 +122,8 @@ def features_exist(self, image: str) -> bool:
121122
return False
122123
return image in feat
123124

124-
def load_words(self, image: str):
125-
self._raise_if_absent_image(image)
126-
n_closest = 50
127-
return [image] * n_closest
125+
def load_words(self, image: str) -> NDArray:
126+
raise NotImplementedError
128127

129128
def load_features(self, image: str) -> Optional[oft.FeaturesData]:
130129
self._raise_if_absent_image(image)
@@ -146,7 +145,7 @@ def matches_exists(self, image: str) -> bool:
146145
return False
147146
return True
148147

149-
def load_matches(self, image: str) -> Dict[str, np.ndarray]:
148+
def load_matches(self, image: str) -> Dict[str, NDArray]:
150149
self._raise_if_absent_image(image)
151150
self._check_and_create_matches()
152151
if self.matches is not None:
@@ -161,7 +160,7 @@ def _check_and_create_matches(self) -> None:
161160
if self.matches is None:
162161
self.matches = self._construct_matches()
163162

164-
def _construct_matches(self) -> Dict[str, Any]:
163+
def _construct_matches(self) -> Dict[str, Dict[str, NDArray]]:
165164
matches = {}
166165
tracks_manager = self.load_tracks_manager()
167166
for im1 in self.images():

opensfm/synthetic_data/synthetic_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# pyre-unsafe
1+
# pyre-strict
22
from typing import Optional
33

44
import opensfm.synthetic_data.synthetic_scene as ss

opensfm/synthetic_data/synthetic_generator.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# pyre-unsafe
1+
# pyre-strict
22
import logging
33
import math
44
import time
@@ -10,6 +10,7 @@
1010
import opensfm.synthetic_data.synthetic_dataset as sd
1111
import scipy.signal as signal
1212
import scipy.spatial as spatial
13+
from numpy.typing import NDArray
1314
from opensfm import (
1415
features as oft,
1516
geo,
@@ -25,20 +26,20 @@
2526
logger: logging.Logger = logging.getLogger(__name__)
2627

2728

28-
def derivative(func: Callable, x: np.ndarray) -> np.ndarray:
29+
def derivative(func: Callable[[float], NDArray], x: float) -> NDArray:
2930
eps = 1e-10
3031
d = (func(x + eps) - func(x)) / eps
3132
d /= np.linalg.norm(d)
3233
return d
3334

3435

35-
def samples_generator_random_count(count: int) -> np.ndarray:
36+
def samples_generator_random_count(count: int) -> NDArray:
3637
return np.random.rand(count)
3738

3839

3940
def samples_generator_interval(
4041
length: float, end: float, interval: float, interval_noise: float
41-
) -> np.ndarray:
42+
) -> NDArray:
4243
samples = np.linspace(0, end / length, num=int(end / interval))
4344
samples += np.random.normal(
4445
0.0, float(interval_noise) / float(length), samples.shape
@@ -47,8 +48,8 @@ def samples_generator_interval(
4748

4849

4950
def generate_samples_and_local_frame(
50-
samples: np.ndarray, shape: Callable
51-
) -> Tuple[np.ndarray, np.ndarray]:
51+
samples: NDArray, shape: Callable[[float], NDArray]
52+
) -> Tuple[NDArray, NDArray]:
5253
points = []
5354
tangents = []
5455
for i in samples:
@@ -61,8 +62,8 @@ def generate_samples_and_local_frame(
6162

6263

6364
def generate_samples_shifted(
64-
samples: np.ndarray, shape: Callable, shift: float
65-
) -> np.ndarray:
65+
samples: NDArray, shape: Callable[[float], NDArray], shift: float
66+
) -> NDArray:
6667
plane_points = []
6768
for i in samples:
6869
point = shape(i)
@@ -74,8 +75,8 @@ def generate_samples_shifted(
7475

7576

7677
def generate_z_plane(
77-
samples: np.ndarray, shape: Callable, thickness: float
78-
) -> np.ndarray:
78+
samples: NDArray, shape: Callable[[float], NDArray], thickness: float
79+
) -> NDArray:
7980
plane_points = []
8081
for i in samples:
8182
point = shape(i)
@@ -89,8 +90,8 @@ def generate_z_plane(
8990

9091

9192
def generate_xy_planes(
92-
samples: np.ndarray, shape: Callable, z_size: float, y_size: float
93-
) -> np.ndarray:
93+
samples: NDArray, shape: Callable[[float], NDArray], z_size: float, y_size: float
94+
) -> NDArray:
9495
xy1 = generate_samples_shifted(samples, shape, y_size)
9596
xy2 = generate_samples_shifted(samples, shape, -y_size)
9697
xy1 = np.insert(xy1, 2, values=np.random.rand(xy1.shape[0]) * z_size, axis=1)
@@ -99,16 +100,16 @@ def generate_xy_planes(
99100

100101

101102
def generate_street(
102-
samples: np.ndarray, shape: Callable, height: float, width: float
103-
) -> Tuple[np.ndarray, np.ndarray]:
103+
samples: NDArray, shape: Callable[[float], NDArray], height: float, width: float
104+
) -> Tuple[NDArray, NDArray]:
104105
walls = generate_xy_planes(samples, shape, height, width)
105106
floor = generate_z_plane(samples, shape, width)
106107
return walls, floor
107108

108109

109110
def generate_cameras(
110-
samples: np.ndarray, shape: Callable, height: float
111-
) -> Tuple[np.ndarray, np.ndarray]:
111+
samples: NDArray, shape: Callable[[float], NDArray], height: float
112+
) -> Tuple[NDArray, NDArray]:
112113
positions, rotations = generate_samples_and_local_frame(samples, shape)
113114
positions = np.insert(positions, 2, values=height, axis=1)
114115
rotations = np.insert(rotations, 2, values=0, axis=2)
@@ -117,8 +118,8 @@ def generate_cameras(
117118

118119

119120
def line_generator(
120-
length: float, center_x: float, center_y: float, transpose: bool, point: np.ndarray
121-
) -> np.ndarray:
121+
length: float, center_x: float, center_y: float, transpose: bool, point: float
122+
) -> NDArray:
122123
x = point * length
123124
if transpose:
124125
return np.transpose(
@@ -133,13 +134,13 @@ def line_generator(
133134
return np.transpose(np.array([x + center_x, center_y]))
134135

135136

136-
def ellipse_generator(x_size: float, y_size: float, point: float) -> np.ndarray:
137+
def ellipse_generator(x_size: float, y_size: float, point: float) -> NDArray:
137138
y = np.sin(point * 2 * np.pi) * y_size / 2
138139
x = np.cos(point * 2 * np.pi) * x_size / 2
139140
return np.transpose(np.array([x, y]))
140141

141142

142-
def perturb_points(points: np.ndarray, sigmas: List[float]) -> None:
143+
def perturb_points(points: NDArray, sigmas: List[float]) -> None:
143144
eps = 1e-10
144145
gaussian = np.array([max(s, eps) for s in sigmas])
145146
for point in points:
@@ -148,7 +149,7 @@ def perturb_points(points: np.ndarray, sigmas: List[float]) -> None:
148149

149150
def generate_causal_noise(
150151
dimensions: int, sigma: float, n: int, scale: float
151-
) -> List[np.ndarray]:
152+
) -> List[NDArray]:
152153
dims = [np.arange(-scale, scale) for _ in range(dimensions)]
153154
mesh = np.meshgrid(*dims)
154155
dist = np.linalg.norm(mesh, axis=0)
@@ -254,7 +255,7 @@ def _gps_dop(shot: pymap.Shot) -> float:
254255
return exifs
255256

256257

257-
def perturb_rotations(rotations: np.ndarray, angle_sigma: float) -> None:
258+
def perturb_rotations(rotations: NDArray, angle_sigma: float) -> None:
258259
for i in range(len(rotations)):
259260
rotation = rotations[i]
260261
rodrigues = cv2.Rodrigues(rotation)[0].ravel()
@@ -265,7 +266,7 @@ def perturb_rotations(rotations: np.ndarray, angle_sigma: float) -> None:
265266

266267

267268
def add_points_to_reconstruction(
268-
points: np.ndarray, color: np.ndarray, reconstruction: types.Reconstruction
269+
points: NDArray, color: NDArray, reconstruction: types.Reconstruction
269270
) -> None:
270271
shift = len(reconstruction.points)
271272
for i in range(points.shape[0]):
@@ -275,8 +276,8 @@ def add_points_to_reconstruction(
275276

276277
def add_shots_to_reconstruction(
277278
shots: List[List[str]],
278-
positions: List[np.ndarray],
279-
rotations: List[np.ndarray],
279+
positions: List[NDArray],
280+
rotations: List[NDArray],
280281
rig_cameras: List[pymap.RigCamera],
281282
cameras: List[pygeometry.Camera],
282283
reconstruction: types.Reconstruction,
@@ -308,13 +309,13 @@ def add_shots_to_reconstruction(
308309

309310

310311
def create_reconstruction(
311-
points: List[np.ndarray],
312-
colors: List[np.ndarray],
312+
points: List[NDArray],
313+
colors: List[NDArray],
313314
cameras: List[List[pygeometry.Camera]],
314315
shot_ids: List[List[str]],
315316
rig_shots: List[List[List[Tuple[str, str]]]],
316-
rig_positions: List[np.ndarray],
317-
rig_rotations: List[np.ndarray],
317+
rig_positions: List[NDArray],
318+
rig_rotations: List[NDArray],
318319
rig_cameras: List[List[pymap.RigCamera]],
319320
reference: Optional[geo.TopocentricConverter],
320321
) -> Reconstruction:
@@ -355,7 +356,7 @@ def generate_track_data(
355356
projection_noise: float,
356357
gcp_noise: Tuple[float, float],
357358
gcps_count: Optional[int],
358-
gcp_shift: Optional[np.ndarray],
359+
gcp_shift: Optional[NDArray],
359360
on_disk_features_filename: Optional[str],
360361
) -> Tuple[
361362
sd.SyntheticFeatures, pymap.TracksManager, Dict[str, pymap.GroundControlPoint]
@@ -490,15 +491,15 @@ def generate_track_data(
490491
return features, tracks_manager, gcps
491492

492493

493-
def _is_in_front(point: np.ndarray, center: np.ndarray, z_axis: np.ndarray) -> bool:
494+
def _is_in_front(point: NDArray, center: NDArray, z_axis: NDArray) -> bool:
494495
return (
495496
(point[0] - center[0]) * z_axis[0]
496497
+ (point[1] - center[1]) * z_axis[1]
497498
+ (point[2] - center[2]) * z_axis[2]
498499
) > 0
499500

500501

501-
def _is_inside_camera(projection: np.ndarray, camera: pygeometry.Camera) -> bool:
502+
def _is_inside_camera(projection: NDArray, camera: pygeometry.Camera) -> bool:
502503
w, h = float(camera.width), float(camera.height)
503504
w2 = float(2 * camera.width)
504505
h2 = float(2 * camera.height)

opensfm/synthetic_data/synthetic_metrics.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
# pyre-unsafe
1+
# pyre-strict
22
import copy
33
from typing import Dict, List, Tuple
44

55
import cv2
66
import numpy as np
77
import opensfm.transformations as tf
8+
from numpy.typing import NDArray
89
from opensfm import align, geo, multiview, pymap, types
910

1011

1112
def points_errors(
1213
reference: types.Reconstruction, candidate: types.Reconstruction
13-
) -> np.ndarray:
14+
) -> NDArray:
1415
common_points = set(reference.points.keys()).intersection(
1516
set(candidate.points.keys())
1617
)
@@ -32,7 +33,7 @@ def completeness_errors(
3233
)
3334

3435

35-
def gps_errors(candidate: types.Reconstruction) -> np.ndarray:
36+
def gps_errors(candidate: types.Reconstruction) -> NDArray:
3637
errors = []
3738
for shot in candidate.shots.values():
3839
bias = candidate.biases[shot.camera.id]
@@ -44,7 +45,7 @@ def gps_errors(candidate: types.Reconstruction) -> np.ndarray:
4445

4546
def gcp_errors(
4647
candidate: types.Reconstruction, gcps: Dict[str, pymap.GroundControlPoint]
47-
) -> np.ndarray:
48+
) -> NDArray:
4849
errors = []
4950
for gcp in gcps.values():
5051
if not gcp.lla:
@@ -61,7 +62,7 @@ def gcp_errors(
6162

6263
def position_errors(
6364
reference: types.Reconstruction, candidate: types.Reconstruction
64-
) -> np.ndarray:
65+
) -> NDArray:
6566
common_shots = set(reference.shots.keys()).intersection(set(candidate.shots.keys()))
6667
errors = []
6768
for s in common_shots:
@@ -73,7 +74,7 @@ def position_errors(
7374

7475
def rotation_errors(
7576
reference: types.Reconstruction, candidate: types.Reconstruction
76-
) -> np.ndarray:
77+
) -> NDArray:
7778
common_shots = set(reference.shots.keys()).intersection(set(candidate.shots.keys()))
7879
errors = []
7980
for s in common_shots:
@@ -87,8 +88,8 @@ def rotation_errors(
8788

8889

8990
def find_alignment(
90-
points0: List[np.ndarray], points1: List[np.ndarray]
91-
) -> Tuple[float, np.ndarray, np.ndarray]:
91+
points0: List[NDArray], points1: List[NDArray]
92+
) -> Tuple[float, NDArray, NDArray]:
9293
"""Compute similarity transform between point sets.
9394
9495
Returns (s, A, b) such that ``points1 = s * A * points0 + b``
@@ -156,9 +157,9 @@ def change_geo_reference(
156157
return aligned
157158

158159

159-
def rmse(errors: np.ndarray) -> float:
160+
def rmse(errors: NDArray) -> float:
160161
return np.sqrt(np.mean(errors**2))
161162

162163

163-
def mad(errors: np.ndarray) -> float:
164+
def mad(errors: NDArray) -> float:
164165
return np.median(np.absolute(errors - np.median(errors)))

0 commit comments

Comments
 (0)