mapillary-tools 0.12.1__py3-none-any.whl → 0.13.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mapillary_tools/__init__.py +1 -1
- mapillary_tools/api_v4.py +94 -4
- mapillary_tools/{geotag → camm}/camm_builder.py +73 -61
- mapillary_tools/camm/camm_parser.py +561 -0
- mapillary_tools/commands/__init__.py +0 -1
- mapillary_tools/commands/__main__.py +0 -6
- mapillary_tools/commands/process.py +0 -50
- mapillary_tools/commands/upload.py +1 -26
- mapillary_tools/constants.py +2 -2
- mapillary_tools/exiftool_read_video.py +13 -11
- mapillary_tools/ffmpeg.py +2 -2
- mapillary_tools/geo.py +0 -54
- mapillary_tools/geotag/blackvue_parser.py +4 -4
- mapillary_tools/geotag/geotag_images_from_exif.py +2 -1
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -1
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +7 -1
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +5 -3
- mapillary_tools/geotag/geotag_videos_from_video.py +13 -14
- mapillary_tools/geotag/gpmf_gps_filter.py +9 -10
- mapillary_tools/geotag/gpmf_parser.py +346 -83
- mapillary_tools/mp4/__init__.py +0 -0
- mapillary_tools/{geotag → mp4}/construct_mp4_parser.py +32 -16
- mapillary_tools/mp4/mp4_sample_parser.py +322 -0
- mapillary_tools/{geotag → mp4}/simple_mp4_builder.py +64 -38
- mapillary_tools/process_geotag_properties.py +25 -19
- mapillary_tools/process_sequence_properties.py +6 -6
- mapillary_tools/sample_video.py +17 -16
- mapillary_tools/telemetry.py +71 -0
- mapillary_tools/types.py +18 -0
- mapillary_tools/upload.py +74 -233
- mapillary_tools/upload_api_v4.py +8 -9
- mapillary_tools/utils.py +9 -16
- mapillary_tools/video_data_extraction/cli_options.py +0 -1
- mapillary_tools/video_data_extraction/extract_video_data.py +13 -31
- mapillary_tools/video_data_extraction/extractors/base_parser.py +13 -11
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +5 -4
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +13 -16
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +4 -9
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +9 -11
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +6 -11
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +11 -4
- mapillary_tools/video_data_extraction/extractors/gpx_parser.py +90 -11
- mapillary_tools/video_data_extraction/extractors/nmea_parser.py +3 -3
- mapillary_tools/video_data_extraction/video_data_parser_factory.py +13 -20
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/METADATA +10 -3
- mapillary_tools-0.13.1.dist-info/RECORD +75 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/WHEEL +1 -1
- mapillary_tools/commands/upload_blackvue.py +0 -33
- mapillary_tools/commands/upload_camm.py +0 -33
- mapillary_tools/commands/upload_zip.py +0 -33
- mapillary_tools/geotag/camm_parser.py +0 -306
- mapillary_tools/geotag/mp4_sample_parser.py +0 -426
- mapillary_tools/process_import_meta_properties.py +0 -76
- mapillary_tools-0.12.1.dist-info/RECORD +0 -77
- /mapillary_tools/{geotag → mp4}/io_utils.py +0 -0
- /mapillary_tools/{geotag → mp4}/simple_mp4_parser.py +0 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/LICENSE +0 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.12.1.dist-info → mapillary_tools-0.13.1.dist-info}/top_level.txt +0 -0
|
@@ -5,6 +5,7 @@ import typing as T
|
|
|
5
5
|
import xml.etree.ElementTree as ET
|
|
6
6
|
|
|
7
7
|
from . import exif_read, exiftool_read, geo
|
|
8
|
+
from .telemetry import GPSFix, GPSPoint
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
MAX_TRACK_ID = 10
|
|
@@ -87,7 +88,7 @@ def _aggregate_gps_track(
|
|
|
87
88
|
alt_tag: T.Optional[str] = None,
|
|
88
89
|
direction_tag: T.Optional[str] = None,
|
|
89
90
|
ground_speed_tag: T.Optional[str] = None,
|
|
90
|
-
) -> T.List[
|
|
91
|
+
) -> T.List[GPSPoint]:
|
|
91
92
|
"""
|
|
92
93
|
Aggregate all GPS data by the tags.
|
|
93
94
|
It requires lat, lon to be present, and their lengths must match.
|
|
@@ -173,15 +174,16 @@ def _aggregate_gps_track(
|
|
|
173
174
|
if timestamp is None or lon is None or lat is None:
|
|
174
175
|
continue
|
|
175
176
|
track.append(
|
|
176
|
-
|
|
177
|
+
GPSPoint(
|
|
177
178
|
time=timestamp,
|
|
178
179
|
lon=lon,
|
|
179
180
|
lat=lat,
|
|
180
181
|
alt=alt,
|
|
181
182
|
angle=direction,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
epoch_time=None,
|
|
184
|
+
fix=None,
|
|
185
|
+
precision=None,
|
|
186
|
+
ground_speed=ground_speed,
|
|
185
187
|
)
|
|
186
188
|
)
|
|
187
189
|
|
|
@@ -230,8 +232,8 @@ def _aggregate_gps_track_by_sample_time(
|
|
|
230
232
|
ground_speed_tag: T.Optional[str] = None,
|
|
231
233
|
gps_fix_tag: T.Optional[str] = None,
|
|
232
234
|
gps_precision_tag: T.Optional[str] = None,
|
|
233
|
-
) -> T.List[
|
|
234
|
-
track: T.List[
|
|
235
|
+
) -> T.List[GPSPoint]:
|
|
236
|
+
track: T.List[GPSPoint] = []
|
|
235
237
|
|
|
236
238
|
expanded_gps_fix_tag = None
|
|
237
239
|
if gps_fix_tag is not None:
|
|
@@ -249,7 +251,7 @@ def _aggregate_gps_track_by_sample_time(
|
|
|
249
251
|
gps_fix_texts = texts_by_tag.get(expanded_gps_fix_tag)
|
|
250
252
|
if gps_fix_texts:
|
|
251
253
|
try:
|
|
252
|
-
gps_fix =
|
|
254
|
+
gps_fix = GPSFix(int(gps_fix_texts[0]))
|
|
253
255
|
except ValueError:
|
|
254
256
|
gps_fix = None
|
|
255
257
|
|
|
@@ -280,7 +282,7 @@ def _aggregate_gps_track_by_sample_time(
|
|
|
280
282
|
for idx, point in enumerate(points):
|
|
281
283
|
point.time = sample_time + idx * avg_timedelta
|
|
282
284
|
track.extend(
|
|
283
|
-
dataclasses.replace(point,
|
|
285
|
+
dataclasses.replace(point, fix=gps_fix, precision=gps_precision)
|
|
284
286
|
for point in points
|
|
285
287
|
)
|
|
286
288
|
|
|
@@ -355,7 +357,7 @@ class ExifToolReadVideo:
|
|
|
355
357
|
_, model = self._extract_make_and_model()
|
|
356
358
|
return model
|
|
357
359
|
|
|
358
|
-
def _extract_gps_track_from_track(self) -> T.List[
|
|
360
|
+
def _extract_gps_track_from_track(self) -> T.List[GPSPoint]:
|
|
359
361
|
for track_id in range(1, MAX_TRACK_ID + 1):
|
|
360
362
|
track_ns = f"Track{track_id}"
|
|
361
363
|
if self._all_tags_exists(
|
|
@@ -397,7 +399,7 @@ class ExifToolReadVideo:
|
|
|
397
399
|
|
|
398
400
|
def _extract_gps_track_from_quicktime(
|
|
399
401
|
self, namespace: str = "QuickTime"
|
|
400
|
-
) -> T.List[
|
|
402
|
+
) -> T.List[GPSPoint]:
|
|
401
403
|
if not self._all_tags_exists(
|
|
402
404
|
{
|
|
403
405
|
expand_tag(f"{namespace}:GPSDateTime"),
|
mapillary_tools/ffmpeg.py
CHANGED
|
@@ -202,10 +202,10 @@ class FFMPEG:
|
|
|
202
202
|
return "0"
|
|
203
203
|
|
|
204
204
|
if length == 1:
|
|
205
|
-
return f"eq(n\\,{
|
|
205
|
+
return f"eq(n\\,{sorted_frame_indices[0]})"
|
|
206
206
|
|
|
207
207
|
middle = length // 2
|
|
208
|
-
return f"if(lt(n\\,{
|
|
208
|
+
return f"if(lt(n\\,{sorted_frame_indices[middle]})\\,{self.generate_binary_search(sorted_frame_indices[:middle])}\\,{self.generate_binary_search(sorted_frame_indices[middle:])})"
|
|
209
209
|
|
|
210
210
|
def extract_specified_frames(
|
|
211
211
|
self,
|
mapillary_tools/geo.py
CHANGED
|
@@ -6,7 +6,6 @@ import datetime
|
|
|
6
6
|
import itertools
|
|
7
7
|
import math
|
|
8
8
|
import typing as T
|
|
9
|
-
from enum import Enum, unique
|
|
10
9
|
|
|
11
10
|
WGS84_a = 6378137.0
|
|
12
11
|
WGS84_a_SQ = WGS84_a**2
|
|
@@ -32,45 +31,6 @@ class Point:
|
|
|
32
31
|
angle: T.Optional[float]
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
@unique
|
|
36
|
-
class GPSFix(Enum):
|
|
37
|
-
NO_FIX = 0
|
|
38
|
-
FIX_2D = 2
|
|
39
|
-
FIX_3D = 3
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@dataclasses.dataclass
|
|
43
|
-
class PointWithFix(Point):
|
|
44
|
-
gps_fix: T.Optional[GPSFix]
|
|
45
|
-
gps_precision: T.Optional[float]
|
|
46
|
-
gps_ground_speed: T.Optional[float]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _ecef_from_lla_DEPRECATED(
|
|
50
|
-
lat: float, lon: float, alt: float
|
|
51
|
-
) -> T.Tuple[float, float, float]:
|
|
52
|
-
"""
|
|
53
|
-
Deprecated because it is slow. Keep here for reference and comparison.
|
|
54
|
-
Use _ecef_from_lla2 instead.
|
|
55
|
-
|
|
56
|
-
Compute ECEF XYZ from latitude, longitude and altitude.
|
|
57
|
-
|
|
58
|
-
All using the WGS94 model.
|
|
59
|
-
Altitude is the distance to the WGS94 ellipsoid.
|
|
60
|
-
Check results here http://www.oc.nps.edu/oc2902w/coord/llhxyz.htm
|
|
61
|
-
|
|
62
|
-
"""
|
|
63
|
-
a2 = WGS84_a**2
|
|
64
|
-
b2 = WGS84_b**2
|
|
65
|
-
lat = math.radians(lat)
|
|
66
|
-
lon = math.radians(lon)
|
|
67
|
-
L = 1.0 / math.sqrt(a2 * math.cos(lat) ** 2 + b2 * math.sin(lat) ** 2)
|
|
68
|
-
x = (a2 * L + alt) * math.cos(lat) * math.cos(lon)
|
|
69
|
-
y = (a2 * L + alt) * math.cos(lat) * math.sin(lon)
|
|
70
|
-
z = (b2 * L + alt) * math.sin(lat)
|
|
71
|
-
return x, y, z
|
|
72
|
-
|
|
73
|
-
|
|
74
34
|
def _ecef_from_lla2(lat: float, lon: float) -> T.Tuple[float, float, float]:
|
|
75
35
|
"""
|
|
76
36
|
Compute ECEF XYZ from latitude, longitude and altitude.
|
|
@@ -172,20 +132,6 @@ def pairwise(iterable: T.Iterable[_IT]) -> T.Iterable[T.Tuple[_IT, _IT]]:
|
|
|
172
132
|
return zip(a, b)
|
|
173
133
|
|
|
174
134
|
|
|
175
|
-
def group_every(
|
|
176
|
-
iterable: T.Iterable[_IT], n: int
|
|
177
|
-
) -> T.Generator[T.Generator[_IT, None, None], None, None]:
|
|
178
|
-
"""
|
|
179
|
-
Return a generator that divides the iterable into groups by N.
|
|
180
|
-
"""
|
|
181
|
-
|
|
182
|
-
if not (0 < n):
|
|
183
|
-
raise ValueError("expect 0 < n but got {0}".format(n))
|
|
184
|
-
|
|
185
|
-
for _, group in itertools.groupby(enumerate(iterable), key=lambda t: t[0] // n):
|
|
186
|
-
yield (item for _, item in group)
|
|
187
|
-
|
|
188
|
-
|
|
189
135
|
def as_unix_time(dt: T.Union[datetime.datetime, int, float]) -> float:
|
|
190
136
|
if isinstance(dt, (int, float)):
|
|
191
137
|
return dt
|
|
@@ -7,7 +7,7 @@ import typing as T
|
|
|
7
7
|
import pynmea2
|
|
8
8
|
|
|
9
9
|
from .. import geo
|
|
10
|
-
from
|
|
10
|
+
from ..mp4 import simple_mp4_parser as sparser
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
LOG = logging.getLogger(__name__)
|
|
@@ -55,8 +55,8 @@ def _parse_gps_box(gps_data: bytes) -> T.Generator[geo.Point, None, None]:
|
|
|
55
55
|
|
|
56
56
|
def extract_camera_model(fp: T.BinaryIO) -> str:
|
|
57
57
|
try:
|
|
58
|
-
cprt_bytes =
|
|
59
|
-
except
|
|
58
|
+
cprt_bytes = sparser.parse_mp4_data_first(fp, [b"free", b"cprt"])
|
|
59
|
+
except sparser.ParsingError:
|
|
60
60
|
return ""
|
|
61
61
|
|
|
62
62
|
if cprt_bytes is None:
|
|
@@ -91,7 +91,7 @@ def extract_camera_model(fp: T.BinaryIO) -> str:
|
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
def extract_points(fp: T.BinaryIO) -> T.Optional[T.List[geo.Point]]:
|
|
94
|
-
gps_data =
|
|
94
|
+
gps_data = sparser.parse_mp4_data_first(fp, [b"free", b"gps "])
|
|
95
95
|
if gps_data is None:
|
|
96
96
|
return None
|
|
97
97
|
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
|
-
from .. import exceptions, exif_write, geo, types
|
|
9
|
+
from .. import exceptions, exif_write, geo, types, utils
|
|
10
10
|
from ..exif_read import ExifRead, ExifReadABC
|
|
11
11
|
from .geotag_from_generic import GeotagImagesFromGeneric
|
|
12
12
|
|
|
@@ -64,6 +64,7 @@ class GeotagImagesFromEXIF(GeotagImagesFromGeneric):
|
|
|
64
64
|
image_metadata = types.ImageMetadata(
|
|
65
65
|
filename=image_path,
|
|
66
66
|
md5sum=None,
|
|
67
|
+
filesize=utils.get_file_size(image_path),
|
|
67
68
|
time=geo.as_unix_time(capture_time),
|
|
68
69
|
lat=lat,
|
|
69
70
|
lon=lon,
|
|
@@ -48,7 +48,6 @@ class GeotagImagesFromExifToolBothImageAndVideo(GeotagImagesFromGeneric):
|
|
|
48
48
|
video_paths = utils.find_videos(
|
|
49
49
|
[Path(pathstr) for pathstr in rdf_description_by_path.keys()],
|
|
50
50
|
skip_subfolders=True,
|
|
51
|
-
check_file_suffix=True,
|
|
52
51
|
)
|
|
53
52
|
|
|
54
53
|
# will try to geotag these error metadatas from video later
|
|
@@ -25,7 +25,13 @@ class GeotagImagesFromGPXFile(GeotagImagesFromGeneric):
|
|
|
25
25
|
num_processes: T.Optional[int] = None,
|
|
26
26
|
):
|
|
27
27
|
super().__init__()
|
|
28
|
-
|
|
28
|
+
try:
|
|
29
|
+
tracks = parse_gpx(source_path)
|
|
30
|
+
except Exception as ex:
|
|
31
|
+
raise RuntimeError(
|
|
32
|
+
f"Error parsing GPX {source_path}: {ex.__class__.__name__}: {ex}"
|
|
33
|
+
)
|
|
34
|
+
|
|
29
35
|
if 1 < len(tracks):
|
|
30
36
|
LOG.warning(
|
|
31
37
|
"Found %s tracks in the GPX file %s. Will merge points in all the tracks as a single track for interpolation",
|
|
@@ -6,8 +6,9 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
|
-
from .. import exceptions, exiftool_read, geo, types
|
|
9
|
+
from .. import exceptions, exiftool_read, geo, types, utils
|
|
10
10
|
from ..exiftool_read_video import ExifToolReadVideo
|
|
11
|
+
from ..telemetry import GPSPoint
|
|
11
12
|
from . import gpmf_gps_filter, utils as video_utils
|
|
12
13
|
from .geotag_from_generic import GeotagVideosFromGeneric
|
|
13
14
|
|
|
@@ -45,11 +46,11 @@ class GeotagVideosFromExifToolVideo(GeotagVideosFromGeneric):
|
|
|
45
46
|
points = geo.extend_deduplicate_points(points)
|
|
46
47
|
assert points, "must have at least one point"
|
|
47
48
|
|
|
48
|
-
if all(isinstance(p,
|
|
49
|
+
if all(isinstance(p, GPSPoint) for p in points):
|
|
49
50
|
points = T.cast(
|
|
50
51
|
T.List[geo.Point],
|
|
51
52
|
gpmf_gps_filter.remove_noisy_points(
|
|
52
|
-
T.cast(T.List[
|
|
53
|
+
T.cast(T.List[GPSPoint], points)
|
|
53
54
|
),
|
|
54
55
|
)
|
|
55
56
|
if not points:
|
|
@@ -65,6 +66,7 @@ class GeotagVideosFromExifToolVideo(GeotagVideosFromGeneric):
|
|
|
65
66
|
video_metadata = types.VideoMetadata(
|
|
66
67
|
video_path,
|
|
67
68
|
md5sum=None,
|
|
69
|
+
filesize=utils.get_file_size(video_path),
|
|
68
70
|
filetype=types.FileType.VIDEO,
|
|
69
71
|
points=points,
|
|
70
72
|
make=exif.extract_make(),
|
|
@@ -6,15 +6,11 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
|
-
from .. import exceptions, geo, types
|
|
10
|
-
from
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
gpmf_parser,
|
|
15
|
-
simple_mp4_parser as parser,
|
|
16
|
-
utils as video_utils,
|
|
17
|
-
)
|
|
9
|
+
from .. import exceptions, geo, types, utils
|
|
10
|
+
from ..camm import camm_parser
|
|
11
|
+
from ..mp4 import simple_mp4_parser as sparser
|
|
12
|
+
from ..telemetry import GPSPoint
|
|
13
|
+
from . import blackvue_parser, gpmf_gps_filter, gpmf_parser, utils as video_utils
|
|
18
14
|
from .geotag_from_generic import GeotagVideosFromGeneric
|
|
19
15
|
|
|
20
16
|
LOG = logging.getLogger(__name__)
|
|
@@ -77,7 +73,7 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
77
73
|
with video_path.open("rb") as fp:
|
|
78
74
|
try:
|
|
79
75
|
points = camm_parser.extract_points(fp)
|
|
80
|
-
except
|
|
76
|
+
except sparser.ParsingError:
|
|
81
77
|
points = None
|
|
82
78
|
|
|
83
79
|
if points is not None:
|
|
@@ -86,6 +82,7 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
86
82
|
return types.VideoMetadata(
|
|
87
83
|
filename=video_path,
|
|
88
84
|
md5sum=None,
|
|
85
|
+
filesize=utils.get_file_size(video_path),
|
|
89
86
|
filetype=types.FileType.CAMM,
|
|
90
87
|
points=points,
|
|
91
88
|
make=make,
|
|
@@ -100,7 +97,7 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
100
97
|
with video_path.open("rb") as fp:
|
|
101
98
|
try:
|
|
102
99
|
points_with_fix = gpmf_parser.extract_points(fp)
|
|
103
|
-
except
|
|
100
|
+
except sparser.ParsingError:
|
|
104
101
|
points_with_fix = None
|
|
105
102
|
|
|
106
103
|
if points_with_fix is not None:
|
|
@@ -109,6 +106,7 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
109
106
|
return types.VideoMetadata(
|
|
110
107
|
filename=video_path,
|
|
111
108
|
md5sum=None,
|
|
109
|
+
filesize=utils.get_file_size(video_path),
|
|
112
110
|
filetype=types.FileType.GOPRO,
|
|
113
111
|
points=T.cast(T.List[geo.Point], points_with_fix),
|
|
114
112
|
make=make,
|
|
@@ -123,7 +121,7 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
123
121
|
with video_path.open("rb") as fp:
|
|
124
122
|
try:
|
|
125
123
|
points = blackvue_parser.extract_points(fp)
|
|
126
|
-
except
|
|
124
|
+
except sparser.ParsingError:
|
|
127
125
|
points = None
|
|
128
126
|
|
|
129
127
|
if points is not None:
|
|
@@ -132,6 +130,7 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
132
130
|
return types.VideoMetadata(
|
|
133
131
|
filename=video_path,
|
|
134
132
|
md5sum=None,
|
|
133
|
+
filesize=utils.get_file_size(video_path),
|
|
135
134
|
filetype=types.FileType.BLACKVUE,
|
|
136
135
|
points=points,
|
|
137
136
|
make=make,
|
|
@@ -160,11 +159,11 @@ class GeotagVideosFromVideo(GeotagVideosFromGeneric):
|
|
|
160
159
|
video_metadata.points = geo.extend_deduplicate_points(video_metadata.points)
|
|
161
160
|
assert video_metadata.points, "must have at least one point"
|
|
162
161
|
|
|
163
|
-
if all(isinstance(p,
|
|
162
|
+
if all(isinstance(p, GPSPoint) for p in video_metadata.points):
|
|
164
163
|
video_metadata.points = T.cast(
|
|
165
164
|
T.List[geo.Point],
|
|
166
165
|
gpmf_gps_filter.remove_noisy_points(
|
|
167
|
-
T.cast(T.List[
|
|
166
|
+
T.cast(T.List[GPSPoint], video_metadata.points)
|
|
168
167
|
),
|
|
169
168
|
)
|
|
170
169
|
if not video_metadata.points:
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import typing as T
|
|
3
3
|
|
|
4
4
|
from .. import constants, geo
|
|
5
|
+
from ..telemetry import GPSPoint
|
|
5
6
|
from . import gps_filter
|
|
6
7
|
|
|
7
8
|
"""
|
|
@@ -13,8 +14,8 @@ LOG = logging.getLogger(__name__)
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def remove_outliers(
|
|
16
|
-
sequence: T.Sequence[
|
|
17
|
-
) -> T.Sequence[
|
|
17
|
+
sequence: T.Sequence[GPSPoint],
|
|
18
|
+
) -> T.Sequence[GPSPoint]:
|
|
18
19
|
distances = [
|
|
19
20
|
geo.gps_distance((left.lat, left.lon), (right.lat, right.lon))
|
|
20
21
|
for left, right in geo.pairwise(sequence)
|
|
@@ -37,9 +38,7 @@ def remove_outliers(
|
|
|
37
38
|
"Split to %d sequences with max distance %f", len(sequences), max_distance
|
|
38
39
|
)
|
|
39
40
|
|
|
40
|
-
ground_speeds = [
|
|
41
|
-
p.gps_ground_speed for p in sequence if p.gps_ground_speed is not None
|
|
42
|
-
]
|
|
41
|
+
ground_speeds = [p.ground_speed for p in sequence if p.ground_speed is not None]
|
|
43
42
|
if len(ground_speeds) < 2:
|
|
44
43
|
return sequence
|
|
45
44
|
|
|
@@ -50,20 +49,20 @@ def remove_outliers(
|
|
|
50
49
|
)
|
|
51
50
|
|
|
52
51
|
return T.cast(
|
|
53
|
-
T.List[
|
|
52
|
+
T.List[GPSPoint],
|
|
54
53
|
gps_filter.find_majority(merged.values()),
|
|
55
54
|
)
|
|
56
55
|
|
|
57
56
|
|
|
58
57
|
def remove_noisy_points(
|
|
59
|
-
sequence: T.Sequence[
|
|
60
|
-
) -> T.Sequence[
|
|
58
|
+
sequence: T.Sequence[GPSPoint],
|
|
59
|
+
) -> T.Sequence[GPSPoint]:
|
|
61
60
|
num_points = len(sequence)
|
|
62
61
|
sequence = [
|
|
63
62
|
p
|
|
64
63
|
for p in sequence
|
|
65
64
|
# include points **without** GPS fix
|
|
66
|
-
if p.
|
|
65
|
+
if p.fix is None or p.fix.value in constants.GOPRO_GPS_FIXES
|
|
67
66
|
]
|
|
68
67
|
if len(sequence) < num_points:
|
|
69
68
|
LOG.debug(
|
|
@@ -77,7 +76,7 @@ def remove_noisy_points(
|
|
|
77
76
|
p
|
|
78
77
|
for p in sequence
|
|
79
78
|
# include points **without** precision
|
|
80
|
-
if p.
|
|
79
|
+
if p.precision is None or p.precision <= constants.GOPRO_MAX_DOP100
|
|
81
80
|
]
|
|
82
81
|
if len(sequence) < num_points:
|
|
83
82
|
LOG.debug(
|