mapillary-tools 0.13.3a1__py3-none-any.whl → 0.14.0a2__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 +237 -16
- mapillary_tools/authenticate.py +325 -64
- mapillary_tools/{geotag/blackvue_parser.py → blackvue_parser.py} +74 -54
- mapillary_tools/camm/camm_builder.py +55 -97
- mapillary_tools/camm/camm_parser.py +429 -181
- mapillary_tools/commands/__main__.py +12 -6
- mapillary_tools/commands/authenticate.py +8 -1
- mapillary_tools/commands/process.py +27 -51
- mapillary_tools/commands/process_and_upload.py +19 -5
- mapillary_tools/commands/sample_video.py +2 -3
- mapillary_tools/commands/upload.py +18 -9
- mapillary_tools/commands/video_process_and_upload.py +19 -5
- mapillary_tools/config.py +31 -13
- mapillary_tools/constants.py +47 -6
- mapillary_tools/exceptions.py +34 -35
- mapillary_tools/exif_read.py +221 -116
- mapillary_tools/exif_write.py +7 -7
- mapillary_tools/exiftool_read.py +33 -42
- mapillary_tools/exiftool_read_video.py +46 -33
- mapillary_tools/exiftool_runner.py +77 -0
- mapillary_tools/ffmpeg.py +24 -23
- mapillary_tools/geo.py +144 -120
- mapillary_tools/geotag/base.py +147 -0
- mapillary_tools/geotag/factory.py +291 -0
- mapillary_tools/geotag/geotag_images_from_exif.py +14 -131
- mapillary_tools/geotag/geotag_images_from_exiftool.py +126 -82
- mapillary_tools/geotag/geotag_images_from_gpx.py +53 -118
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +13 -126
- mapillary_tools/geotag/geotag_images_from_nmea_file.py +4 -5
- mapillary_tools/geotag/geotag_images_from_video.py +53 -51
- mapillary_tools/geotag/geotag_videos_from_exiftool.py +97 -0
- mapillary_tools/geotag/geotag_videos_from_gpx.py +39 -0
- mapillary_tools/geotag/geotag_videos_from_video.py +20 -185
- mapillary_tools/geotag/image_extractors/base.py +18 -0
- mapillary_tools/geotag/image_extractors/exif.py +60 -0
- mapillary_tools/geotag/image_extractors/exiftool.py +18 -0
- mapillary_tools/geotag/options.py +160 -0
- mapillary_tools/geotag/utils.py +52 -16
- mapillary_tools/geotag/video_extractors/base.py +18 -0
- mapillary_tools/geotag/video_extractors/exiftool.py +70 -0
- mapillary_tools/{video_data_extraction/extractors/gpx_parser.py → geotag/video_extractors/gpx.py} +57 -39
- mapillary_tools/geotag/video_extractors/native.py +157 -0
- mapillary_tools/{geotag → gpmf}/gpmf_parser.py +205 -182
- mapillary_tools/{geotag → gpmf}/gps_filter.py +5 -3
- mapillary_tools/history.py +7 -13
- mapillary_tools/mp4/construct_mp4_parser.py +9 -8
- mapillary_tools/mp4/io_utils.py +0 -1
- mapillary_tools/mp4/mp4_sample_parser.py +36 -28
- mapillary_tools/mp4/simple_mp4_builder.py +10 -9
- mapillary_tools/mp4/simple_mp4_parser.py +13 -22
- mapillary_tools/process_geotag_properties.py +155 -392
- mapillary_tools/process_sequence_properties.py +562 -208
- mapillary_tools/sample_video.py +13 -20
- mapillary_tools/telemetry.py +26 -13
- mapillary_tools/types.py +111 -58
- mapillary_tools/upload.py +316 -298
- mapillary_tools/upload_api_v4.py +55 -122
- mapillary_tools/uploader.py +396 -254
- mapillary_tools/utils.py +42 -18
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/METADATA +3 -2
- mapillary_tools-0.14.0a2.dist-info/RECORD +72 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/WHEEL +1 -1
- mapillary_tools/geotag/__init__.py +0 -1
- mapillary_tools/geotag/geotag_from_generic.py +0 -22
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -93
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -145
- mapillary_tools/video_data_extraction/cli_options.py +0 -22
- mapillary_tools/video_data_extraction/extract_video_data.py +0 -176
- mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -75
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -34
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -38
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -71
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -53
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -43
- mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -24
- mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -39
- mapillary_tools-0.13.3a1.dist-info/RECORD +0 -75
- /mapillary_tools/{geotag → gpmf}/gpmf_gps_filter.py +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0a2.dist-info/licenses}/LICENSE +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0a2.dist-info}/top_level.txt +0 -0
mapillary_tools/utils.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import hashlib
|
|
2
4
|
import os
|
|
3
5
|
import typing as T
|
|
6
|
+
from multiprocessing import Pool
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
# Use "hashlib._Hash" instead of hashlib._Hash because:
|
|
8
11
|
# AttributeError: module 'hashlib' has no attribute '_Hash'
|
|
9
|
-
def md5sum_fp(
|
|
10
|
-
fp: T.IO[bytes], md5: T.Optional["hashlib._Hash"] = None
|
|
11
|
-
) -> "hashlib._Hash":
|
|
12
|
+
def md5sum_fp(fp: T.IO[bytes], md5: "hashlib._Hash | None" = None) -> "hashlib._Hash":
|
|
12
13
|
if md5 is None:
|
|
13
14
|
md5 = hashlib.md5()
|
|
14
15
|
while True:
|
|
@@ -90,12 +91,12 @@ def filter_video_samples(
|
|
|
90
91
|
|
|
91
92
|
|
|
92
93
|
def find_all_image_samples(
|
|
93
|
-
image_paths: T.
|
|
94
|
-
) ->
|
|
94
|
+
image_paths: T.Iterable[Path], video_paths: T.Iterable[Path]
|
|
95
|
+
) -> dict[Path, list[Path]]:
|
|
95
96
|
# TODO: not work with the same filenames, e.g. foo/hello.mp4 and bar/hello.mp4
|
|
96
97
|
video_basenames = {path.name: path for path in video_paths}
|
|
97
98
|
|
|
98
|
-
image_samples_by_video_path:
|
|
99
|
+
image_samples_by_video_path: dict[Path, list[Path]] = {}
|
|
99
100
|
for image_path in image_paths:
|
|
100
101
|
# If you want to walk an arbitrary filesystem path upwards,
|
|
101
102
|
# it is recommended to first call Path.resolve() so as to resolve symlinks and eliminate “..” components.
|
|
@@ -112,7 +113,7 @@ def find_all_image_samples(
|
|
|
112
113
|
|
|
113
114
|
|
|
114
115
|
def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
115
|
-
resolved_paths:
|
|
116
|
+
resolved_paths: set[Path] = set()
|
|
116
117
|
for p in paths:
|
|
117
118
|
resolved = p.resolve()
|
|
118
119
|
if resolved not in resolved_paths:
|
|
@@ -121,10 +122,10 @@ def deduplicate_paths(paths: T.Iterable[Path]) -> T.Generator[Path, None, None]:
|
|
|
121
122
|
|
|
122
123
|
|
|
123
124
|
def find_images(
|
|
124
|
-
import_paths: T.
|
|
125
|
+
import_paths: T.Iterable[Path],
|
|
125
126
|
skip_subfolders: bool = False,
|
|
126
|
-
) ->
|
|
127
|
-
image_paths:
|
|
127
|
+
) -> list[Path]:
|
|
128
|
+
image_paths: list[Path] = []
|
|
128
129
|
for path in import_paths:
|
|
129
130
|
if path.is_dir():
|
|
130
131
|
image_paths.extend(
|
|
@@ -139,10 +140,10 @@ def find_images(
|
|
|
139
140
|
|
|
140
141
|
|
|
141
142
|
def find_videos(
|
|
142
|
-
import_paths: T.
|
|
143
|
+
import_paths: T.Iterable[Path],
|
|
143
144
|
skip_subfolders: bool = False,
|
|
144
|
-
) ->
|
|
145
|
-
video_paths:
|
|
145
|
+
) -> list[Path]:
|
|
146
|
+
video_paths: list[Path] = []
|
|
146
147
|
for path in import_paths:
|
|
147
148
|
if path.is_dir():
|
|
148
149
|
video_paths.extend(
|
|
@@ -157,10 +158,10 @@ def find_videos(
|
|
|
157
158
|
|
|
158
159
|
|
|
159
160
|
def find_zipfiles(
|
|
160
|
-
import_paths: T.
|
|
161
|
+
import_paths: T.Iterable[Path],
|
|
161
162
|
skip_subfolders: bool = False,
|
|
162
|
-
) ->
|
|
163
|
-
zip_paths:
|
|
163
|
+
) -> list[Path]:
|
|
164
|
+
zip_paths: list[Path] = []
|
|
164
165
|
for path in import_paths:
|
|
165
166
|
if path.is_dir():
|
|
166
167
|
zip_paths.extend(
|
|
@@ -174,8 +175,8 @@ def find_zipfiles(
|
|
|
174
175
|
return list(deduplicate_paths(zip_paths))
|
|
175
176
|
|
|
176
177
|
|
|
177
|
-
def find_xml_files(import_paths: T.
|
|
178
|
-
xml_paths:
|
|
178
|
+
def find_xml_files(import_paths: T.Iterable[Path]) -> list[Path]:
|
|
179
|
+
xml_paths: list[Path] = []
|
|
179
180
|
for path in import_paths:
|
|
180
181
|
if path.is_dir():
|
|
181
182
|
# XML could be hidden in hidden folders
|
|
@@ -194,3 +195,26 @@ def find_xml_files(import_paths: T.Sequence[Path]) -> T.List[Path]:
|
|
|
194
195
|
|
|
195
196
|
def get_file_size(path: Path) -> int:
|
|
196
197
|
return os.path.getsize(path)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
TMapIn = T.TypeVar("TMapIn")
|
|
201
|
+
TMapOut = T.TypeVar("TMapOut")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def mp_map_maybe(
|
|
205
|
+
func: T.Callable[[TMapIn], TMapOut],
|
|
206
|
+
iterable: T.Iterable[TMapIn],
|
|
207
|
+
num_processes: int | None = None,
|
|
208
|
+
) -> T.Generator[TMapOut, None, None]:
|
|
209
|
+
if num_processes is None:
|
|
210
|
+
pool_num_processes = None
|
|
211
|
+
disable_multiprocessing = False
|
|
212
|
+
else:
|
|
213
|
+
pool_num_processes = max(num_processes, 1)
|
|
214
|
+
disable_multiprocessing = num_processes <= 0
|
|
215
|
+
|
|
216
|
+
if disable_multiprocessing:
|
|
217
|
+
yield from map(func, iterable)
|
|
218
|
+
else:
|
|
219
|
+
with Pool(processes=pool_num_processes) as pool:
|
|
220
|
+
yield from pool.imap(func, iterable)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: mapillary_tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0a2
|
|
4
4
|
Summary: Mapillary Image/Video Import Pipeline
|
|
5
5
|
Home-page: https://github.com/mapillary/mapillary_tools
|
|
6
6
|
Author: Mapillary
|
|
@@ -23,6 +23,7 @@ Dynamic: description
|
|
|
23
23
|
Dynamic: description-content-type
|
|
24
24
|
Dynamic: home-page
|
|
25
25
|
Dynamic: license
|
|
26
|
+
Dynamic: license-file
|
|
26
27
|
Dynamic: requires-dist
|
|
27
28
|
Dynamic: requires-python
|
|
28
29
|
Dynamic: summary
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
mapillary_tools/__init__.py,sha256=AdfWnUWJogoEJm_EqofQxlG8NYL78h0B0M3Igr2xQpE,21
|
|
2
|
+
mapillary_tools/api_v4.py,sha256=uvz48tb2HaM1wfomaUR0V2HdId39HoxnQAc1WWgptvo,10091
|
|
3
|
+
mapillary_tools/authenticate.py,sha256=KG3p_7D4D7nUvrGRLGp56_ogLy7JUHzkGSpBw8V70LY,11091
|
|
4
|
+
mapillary_tools/blackvue_parser.py,sha256=Gmz3M1cvBmB2W3Pme6x3w2yuixDpKiTOkQe82iPXr2s,3613
|
|
5
|
+
mapillary_tools/config.py,sha256=uoNW5PnB1a2TW959KmNSYgJ1Y4_trn0xxPHJQ6T-cbI,2633
|
|
6
|
+
mapillary_tools/constants.py,sha256=BVnAlqz2XJfodNXsJfsGIV0L4P_3--oOgw1pR38D-Ls,3770
|
|
7
|
+
mapillary_tools/exceptions.py,sha256=P8vEh3pfOqyzJgOiQiRAcWsZTrcawjic7i4v3k335tM,2411
|
|
8
|
+
mapillary_tools/exif_read.py,sha256=gagyRv06RkZPRHQhyV-5-uQzxHgxOsVqUX2aC8QmCyM,32145
|
|
9
|
+
mapillary_tools/exif_write.py,sha256=Ue0ngLKCfuLQM7X4h5ETjSInUDcyEBWOR6v8ko7DJ1o,8769
|
|
10
|
+
mapillary_tools/exiftool_read.py,sha256=5uatYE9mgbg2d9NAnPfX22nSRBjXhJ9ayMqNMd4QwGM,15779
|
|
11
|
+
mapillary_tools/exiftool_read_video.py,sha256=WXpw7CI7B4daJHRWzzVXAyVgDrFN8nPxQ3UPcVQqRJM,14760
|
|
12
|
+
mapillary_tools/exiftool_runner.py,sha256=J04V0Y70kTiGE2q2m5TKWfORd0hmYNmAsmrZxb34S3s,2316
|
|
13
|
+
mapillary_tools/ffmpeg.py,sha256=tRgdChGHAhHXjZPivDuVImubrJWfSDZDBZ7mSzkeABc,15698
|
|
14
|
+
mapillary_tools/geo.py,sha256=MSfkjSAgTAeuBqncQ8-QXvDuJ7ar7bTt5P_e9HDIgVs,10604
|
|
15
|
+
mapillary_tools/history.py,sha256=8ng2WHXadzbE5gnsoik6gbmRoEXTuGPp997RBiiWy08,1649
|
|
16
|
+
mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
|
|
17
|
+
mapillary_tools/process_geotag_properties.py,sha256=f0Up7yNzgLn32VBFMzoMT6SCsdzFUFYmloOsVHmK5k0,13920
|
|
18
|
+
mapillary_tools/process_sequence_properties.py,sha256=LSYyCH1nmEcYWWZKF1bOYh4hqjwXdz4FY9TqQQfW7AU,23418
|
|
19
|
+
mapillary_tools/sample_video.py,sha256=MwTr2bY5RFnOFThNPg3YiyXCXzWOlTXKq7EOri_W3Vw,13880
|
|
20
|
+
mapillary_tools/telemetry.py,sha256=lL6qQbtOZft4DZZrCNK3njlwHT_30zLyYS_YRN5pgHY,1568
|
|
21
|
+
mapillary_tools/types.py,sha256=0O4fzbwwoT6Znp2Ag1ptjzBKGb3E_RhGyqUUZu6gkPY,23078
|
|
22
|
+
mapillary_tools/upload.py,sha256=exQeEh3jpys94k-3QDT-4xoeJ_lAHbQBoy749hbPfnA,24878
|
|
23
|
+
mapillary_tools/upload_api_v4.py,sha256=dTEpQDnV11TyPf5hefd-FVeEfw7WEVYyZcIUJttUEp0,6417
|
|
24
|
+
mapillary_tools/uploader.py,sha256=xPEszuXlgcgCklGfsgL8qN1ozsFuU4rLT4jocMD2dVI,18694
|
|
25
|
+
mapillary_tools/utils.py,sha256=ft-TrqgwWq1a-HpQqnCWIortZbjjTOywllPi8HBA3FU,6642
|
|
26
|
+
mapillary_tools/camm/camm_builder.py,sha256=ub6Z9ijep8zAo1NOlU51Gxk95kQ2vfN58YgVCLmNMRk,9211
|
|
27
|
+
mapillary_tools/camm/camm_parser.py,sha256=aNHP65hNXYQBWBTfhaj_S5XYzmAHhjwcAfGhbm83__o,18043
|
|
28
|
+
mapillary_tools/commands/__init__.py,sha256=41CFrPLGlG3566uhxssEF3TGAtSpADFPPcDMHbViU0E,171
|
|
29
|
+
mapillary_tools/commands/__main__.py,sha256=5gwngScbvrSNF2S1CA5w_qZgxW_kFevu5NQ89U9u1ZE,5046
|
|
30
|
+
mapillary_tools/commands/authenticate.py,sha256=yqtpHMYzkyBrrchj6MARxB0ywUTfqCEOPkMbkyaO9Ks,1344
|
|
31
|
+
mapillary_tools/commands/process.py,sha256=Japc6_P0B_8HzoM8_82P3_YAiyBBaQZXS9TZF46pbMM,9771
|
|
32
|
+
mapillary_tools/commands/process_and_upload.py,sha256=-RB_86a5xKfQ7Ye79dh6yyJQpZg2xnJZAWOJsUNbUtQ,1041
|
|
33
|
+
mapillary_tools/commands/sample_video.py,sha256=jtnrZrsqqv5eYV1chNTas7RhfbeKBqbAUDUNRFjF01w,3253
|
|
34
|
+
mapillary_tools/commands/upload.py,sha256=YnA1iSvNcFeV7kMs1p4TdaPBUWUFYb7X-vFhQeSz0eI,2005
|
|
35
|
+
mapillary_tools/commands/video_process.py,sha256=-wQeeIwWXPmy81HQHam5A0huMLRHknkEFa_V1OwElU4,890
|
|
36
|
+
mapillary_tools/commands/video_process_and_upload.py,sha256=hOyq9L9TuD0JcqFSOOxdCdgsBA1iJ6fu1TtDbsUr8sI,1088
|
|
37
|
+
mapillary_tools/commands/zip.py,sha256=DVQuMLpbstwiy5o4pU_fBvM6eORuFjLeySd80AhHKU0,991
|
|
38
|
+
mapillary_tools/geotag/base.py,sha256=JdLBXHlC22gqmZkyfAykv1osrzitWoCK8RFHdCd5qlc,4864
|
|
39
|
+
mapillary_tools/geotag/factory.py,sha256=2HIOnUtfxsgWn2GF0PbbgHq3Tzyssrl4T39YDMtN42U,9564
|
|
40
|
+
mapillary_tools/geotag/geotag_images_from_exif.py,sha256=a5fDOp4tlEoVNwrOCJBrvSPzu1LbyxL4RNrNDD0tPP0,608
|
|
41
|
+
mapillary_tools/geotag/geotag_images_from_exiftool.py,sha256=YxMFwv3A35Hh9tcGX1B1Si_m6TNep54SL5XFAQadQh0,5302
|
|
42
|
+
mapillary_tools/geotag/geotag_images_from_gpx.py,sha256=b_4xKVUt0HLIjNN-v7ZVddXDZ9cdxAfecB5Dj2X6XQE,6353
|
|
43
|
+
mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256=HYQkwak32YBDuRrTNiIZOmE3iImCRc22HWWb485WRS4,1121
|
|
44
|
+
mapillary_tools/geotag/geotag_images_from_nmea_file.py,sha256=J8xj6ch9bMPRubJtsRGeb3sb9LyB0ZYy65NPEOVkUe8,1597
|
|
45
|
+
mapillary_tools/geotag/geotag_images_from_video.py,sha256=5_qVgLt_DKl7oQrr9kWxi4iM143_HPAx5Lau-3fB7t4,3136
|
|
46
|
+
mapillary_tools/geotag/geotag_videos_from_exiftool.py,sha256=7chO_tb7ECsBlOkFhiXQ4fhomVPkfBOWtdfzXGpsJes,3168
|
|
47
|
+
mapillary_tools/geotag/geotag_videos_from_gpx.py,sha256=k4a1imUVLg2yDqggCwTSqjvY7jXQd3E-vniBuJj2d4M,1017
|
|
48
|
+
mapillary_tools/geotag/geotag_videos_from_video.py,sha256=T8XS4lVF2Wz4eDXNi5Vt076M5dxjxJXibVrWhqVvErs,863
|
|
49
|
+
mapillary_tools/geotag/options.py,sha256=7Fz0QoCMyZZbhkKvY4HPNWYfnKdQSWE2oRXb7jYZhmA,4336
|
|
50
|
+
mapillary_tools/geotag/utils.py,sha256=0DenLVCLP-GZdk0rCFP0V1tjMGK4XAvYro3I_1RKgCA,1733
|
|
51
|
+
mapillary_tools/geotag/image_extractors/base.py,sha256=XoNrLCbJtd-MN-snbhv6zyr6zBfJRoJkntV0ptrh6qg,358
|
|
52
|
+
mapillary_tools/geotag/image_extractors/exif.py,sha256=cCUegbFqWxjAP4oOP1nZmwoJISWeKgjGO8h_t7nucHs,2079
|
|
53
|
+
mapillary_tools/geotag/image_extractors/exiftool.py,sha256=zokJmf-D2rPvASRJs3dZzEu7j82igpMOr4SE6Z1nsVg,497
|
|
54
|
+
mapillary_tools/geotag/video_extractors/base.py,sha256=_3B37T-m2HGy77avAF8i_kEatIyQqFfBu49qVnx_0PU,357
|
|
55
|
+
mapillary_tools/geotag/video_extractors/exiftool.py,sha256=CB49qZqgmxECma-N5757aEn37xXRbCNRcT4o-Ip7dCw,2312
|
|
56
|
+
mapillary_tools/geotag/video_extractors/gpx.py,sha256=Oycdn74CNiq1j8dAq7O9YiVNTun2mFWRH9VTUn_viRk,4114
|
|
57
|
+
mapillary_tools/geotag/video_extractors/native.py,sha256=XChOyDvrf88Vu8ED-FTT_hzax2FllokanszSW4Q2MEA,5702
|
|
58
|
+
mapillary_tools/gpmf/gpmf_gps_filter.py,sha256=7cg8wEjC1DrujKY76FZguXsaPqTRkG9-t32OeuOJQIc,2755
|
|
59
|
+
mapillary_tools/gpmf/gpmf_parser.py,sha256=uOVXkwJxC3Y2YfTdzUDpt7Bh0pdVqa5u0WUuv7pEJEs,24670
|
|
60
|
+
mapillary_tools/gpmf/gps_filter.py,sha256=zhXkuvr0Dd1bxGTYBwvk6P7xasY_RLuWjHaX7CdBayc,3796
|
|
61
|
+
mapillary_tools/mp4/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
+
mapillary_tools/mp4/construct_mp4_parser.py,sha256=ArJ5C8iWJhHNPFkN048DXh6EwQ8LMfnTMwoNlKV9NoI,17191
|
|
63
|
+
mapillary_tools/mp4/io_utils.py,sha256=KZaJTlgFS27Oh3pcA5MKXYFoCifqgFaEZJyU6lb1jc4,5416
|
|
64
|
+
mapillary_tools/mp4/mp4_sample_parser.py,sha256=0ILTq8M6mXFTI3agKgljpvO9uYa7HXGUGZpdHT8a6ac,11547
|
|
65
|
+
mapillary_tools/mp4/simple_mp4_builder.py,sha256=9TUGk1hzI6mQFN1P30jwHL3dCYz3Zz7rsm8UBvMAqMc,12734
|
|
66
|
+
mapillary_tools/mp4/simple_mp4_parser.py,sha256=g3vvPhBoNu7anhVzC5_XQCV7IwfRWro1vJ6d6GyDkHE,6315
|
|
67
|
+
mapillary_tools-0.14.0a2.dist-info/licenses/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
|
|
68
|
+
mapillary_tools-0.14.0a2.dist-info/METADATA,sha256=UDVbKn5kqizwZmE70erGYDOnSz8sg9CuTd81GHSWtDI,19782
|
|
69
|
+
mapillary_tools-0.14.0a2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
70
|
+
mapillary_tools-0.14.0a2.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
|
|
71
|
+
mapillary_tools-0.14.0a2.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
|
|
72
|
+
mapillary_tools-0.14.0a2.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .. import geo # noqa: F401
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import abc
|
|
2
|
-
import typing as T
|
|
3
|
-
|
|
4
|
-
from .. import types
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class GeotagImagesFromGeneric(abc.ABC):
|
|
8
|
-
def __init__(self) -> None:
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
@abc.abstractmethod
|
|
12
|
-
def to_description(self) -> T.List[types.ImageMetadataOrError]:
|
|
13
|
-
raise NotImplementedError
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class GeotagVideosFromGeneric(abc.ABC):
|
|
17
|
-
def __init__(self) -> None:
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
|
-
@abc.abstractmethod
|
|
21
|
-
def to_description(self) -> T.List[types.VideoMetadataOrError]:
|
|
22
|
-
raise NotImplementedError
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import typing as T
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
from .. import exiftool_read, types, utils
|
|
6
|
-
from . import (
|
|
7
|
-
geotag_images_from_exiftool,
|
|
8
|
-
geotag_images_from_video,
|
|
9
|
-
geotag_videos_from_exiftool_video,
|
|
10
|
-
)
|
|
11
|
-
from .geotag_from_generic import GeotagImagesFromGeneric
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
LOG = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class GeotagImagesFromExifToolBothImageAndVideo(GeotagImagesFromGeneric):
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
image_paths: T.Sequence[Path],
|
|
21
|
-
xml_path: Path,
|
|
22
|
-
offset_time: float = 0.0,
|
|
23
|
-
num_processes: T.Optional[int] = None,
|
|
24
|
-
):
|
|
25
|
-
self.image_paths = image_paths
|
|
26
|
-
self.xml_path = xml_path
|
|
27
|
-
self.offset_time = offset_time
|
|
28
|
-
self.num_processes = num_processes
|
|
29
|
-
super().__init__()
|
|
30
|
-
|
|
31
|
-
def to_description(self) -> T.List[types.ImageMetadataOrError]:
|
|
32
|
-
# will return this list
|
|
33
|
-
final_image_metadatas: T.List[types.ImageMetadataOrError] = []
|
|
34
|
-
|
|
35
|
-
# find the images that can be geotagged from EXIF
|
|
36
|
-
image_metadatas_from_exiftool = (
|
|
37
|
-
geotag_images_from_exiftool.GeotagImagesFromExifTool(
|
|
38
|
-
self.image_paths,
|
|
39
|
-
self.xml_path,
|
|
40
|
-
num_processes=self.num_processes,
|
|
41
|
-
).to_description()
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# find all video paths in self.xml_path
|
|
45
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
46
|
-
[self.xml_path]
|
|
47
|
-
)
|
|
48
|
-
video_paths = utils.find_videos(
|
|
49
|
-
[Path(pathstr) for pathstr in rdf_description_by_path.keys()],
|
|
50
|
-
skip_subfolders=True,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
# will try to geotag these error metadatas from video later
|
|
54
|
-
error_metadata_by_image_path = {}
|
|
55
|
-
for image_metadata in image_metadatas_from_exiftool:
|
|
56
|
-
if isinstance(image_metadata, types.ErrorMetadata):
|
|
57
|
-
error_metadata_by_image_path[image_metadata.filename] = image_metadata
|
|
58
|
-
else:
|
|
59
|
-
final_image_metadatas.append(image_metadata)
|
|
60
|
-
|
|
61
|
-
maybe_image_samples = list(error_metadata_by_image_path.keys())
|
|
62
|
-
|
|
63
|
-
# find all video paths that have sample images
|
|
64
|
-
video_paths_with_image_samples = list(
|
|
65
|
-
utils.find_all_image_samples(maybe_image_samples, video_paths).keys()
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
video_metadatas = (
|
|
69
|
-
geotag_videos_from_exiftool_video.GeotagVideosFromExifToolVideo(
|
|
70
|
-
video_paths_with_image_samples,
|
|
71
|
-
self.xml_path,
|
|
72
|
-
num_processes=self.num_processes,
|
|
73
|
-
).to_description()
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
image_metadatas_from_video = geotag_images_from_video.GeotagImagesFromVideo(
|
|
77
|
-
maybe_image_samples,
|
|
78
|
-
video_metadatas,
|
|
79
|
-
offset_time=self.offset_time,
|
|
80
|
-
num_processes=self.num_processes,
|
|
81
|
-
).to_description()
|
|
82
|
-
final_image_metadatas.extend(image_metadatas_from_video)
|
|
83
|
-
|
|
84
|
-
# add back error metadatas that can not be geotagged at all
|
|
85
|
-
actual_image_sample_paths = set(
|
|
86
|
-
image_metadata.filename for image_metadata in image_metadatas_from_video
|
|
87
|
-
)
|
|
88
|
-
for path, error_metadata in error_metadata_by_image_path.items():
|
|
89
|
-
if path not in actual_image_sample_paths:
|
|
90
|
-
final_image_metadatas.append(error_metadata)
|
|
91
|
-
|
|
92
|
-
assert len(final_image_metadatas) <= len(self.image_paths)
|
|
93
|
-
return final_image_metadatas
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import typing as T
|
|
3
|
-
import xml.etree.ElementTree as ET
|
|
4
|
-
from multiprocessing import Pool
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from tqdm import tqdm
|
|
8
|
-
|
|
9
|
-
from .. import exceptions, exiftool_read, geo, types, utils
|
|
10
|
-
from ..exiftool_read_video import ExifToolReadVideo
|
|
11
|
-
from ..telemetry import GPSPoint
|
|
12
|
-
from . import gpmf_gps_filter, utils as video_utils
|
|
13
|
-
from .geotag_from_generic import GeotagVideosFromGeneric
|
|
14
|
-
|
|
15
|
-
LOG = logging.getLogger(__name__)
|
|
16
|
-
_DESCRIPTION_TAG = "rdf:Description"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class GeotagVideosFromExifToolVideo(GeotagVideosFromGeneric):
|
|
20
|
-
def __init__(
|
|
21
|
-
self,
|
|
22
|
-
video_paths: T.Sequence[Path],
|
|
23
|
-
xml_path: Path,
|
|
24
|
-
num_processes: T.Optional[int] = None,
|
|
25
|
-
):
|
|
26
|
-
self.video_paths = video_paths
|
|
27
|
-
self.xml_path = xml_path
|
|
28
|
-
self.num_processes = num_processes
|
|
29
|
-
super().__init__()
|
|
30
|
-
|
|
31
|
-
@staticmethod
|
|
32
|
-
def geotag_video(element: ET.Element) -> types.VideoMetadataOrError:
|
|
33
|
-
video_path = exiftool_read.find_rdf_description_path(element)
|
|
34
|
-
assert video_path is not None, "must find the path from the element"
|
|
35
|
-
|
|
36
|
-
try:
|
|
37
|
-
exif = ExifToolReadVideo(ET.ElementTree(element))
|
|
38
|
-
|
|
39
|
-
points = exif.extract_gps_track()
|
|
40
|
-
|
|
41
|
-
if not points:
|
|
42
|
-
raise exceptions.MapillaryVideoGPSNotFoundError(
|
|
43
|
-
"No GPS data found from the video"
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
points = geo.extend_deduplicate_points(points)
|
|
47
|
-
assert points, "must have at least one point"
|
|
48
|
-
|
|
49
|
-
if all(isinstance(p, GPSPoint) for p in points):
|
|
50
|
-
points = T.cast(
|
|
51
|
-
T.List[geo.Point],
|
|
52
|
-
gpmf_gps_filter.remove_noisy_points(
|
|
53
|
-
T.cast(T.List[GPSPoint], points)
|
|
54
|
-
),
|
|
55
|
-
)
|
|
56
|
-
if not points:
|
|
57
|
-
raise exceptions.MapillaryGPSNoiseError("GPS is too noisy")
|
|
58
|
-
|
|
59
|
-
stationary = video_utils.is_video_stationary(
|
|
60
|
-
geo.get_max_distance_from_start([(p.lat, p.lon) for p in points])
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
if stationary:
|
|
64
|
-
raise exceptions.MapillaryStationaryVideoError("Stationary video")
|
|
65
|
-
|
|
66
|
-
video_metadata = types.VideoMetadata(
|
|
67
|
-
video_path,
|
|
68
|
-
md5sum=None,
|
|
69
|
-
filesize=utils.get_file_size(video_path),
|
|
70
|
-
filetype=types.FileType.VIDEO,
|
|
71
|
-
points=points,
|
|
72
|
-
make=exif.extract_make(),
|
|
73
|
-
model=exif.extract_model(),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
LOG.debug("Calculating MD5 checksum for %s", str(video_metadata.filename))
|
|
77
|
-
|
|
78
|
-
video_metadata.update_md5sum()
|
|
79
|
-
|
|
80
|
-
except Exception as ex:
|
|
81
|
-
if not isinstance(ex, exceptions.MapillaryDescriptionError):
|
|
82
|
-
LOG.warning(
|
|
83
|
-
"Failed to geotag video %s: %s",
|
|
84
|
-
video_path,
|
|
85
|
-
str(ex),
|
|
86
|
-
exc_info=LOG.getEffectiveLevel() <= logging.DEBUG,
|
|
87
|
-
)
|
|
88
|
-
return types.describe_error_metadata(
|
|
89
|
-
ex, video_path, filetype=types.FileType.VIDEO
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return video_metadata
|
|
93
|
-
|
|
94
|
-
def to_description(self) -> T.List[types.VideoMetadataOrError]:
|
|
95
|
-
rdf_description_by_path = exiftool_read.index_rdf_description_by_path(
|
|
96
|
-
[self.xml_path]
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
error_metadatas: T.List[types.ErrorMetadata] = []
|
|
100
|
-
rdf_descriptions: T.List[ET.Element] = []
|
|
101
|
-
for path in self.video_paths:
|
|
102
|
-
rdf_description = rdf_description_by_path.get(
|
|
103
|
-
exiftool_read.canonical_path(path)
|
|
104
|
-
)
|
|
105
|
-
if rdf_description is None:
|
|
106
|
-
exc = exceptions.MapillaryEXIFNotFoundError(
|
|
107
|
-
f"The {_DESCRIPTION_TAG} XML element for the video not found"
|
|
108
|
-
)
|
|
109
|
-
error_metadatas.append(
|
|
110
|
-
types.describe_error_metadata(
|
|
111
|
-
exc, path, filetype=types.FileType.VIDEO
|
|
112
|
-
)
|
|
113
|
-
)
|
|
114
|
-
else:
|
|
115
|
-
rdf_descriptions.append(rdf_description)
|
|
116
|
-
|
|
117
|
-
if self.num_processes is None:
|
|
118
|
-
num_processes = self.num_processes
|
|
119
|
-
disable_multiprocessing = False
|
|
120
|
-
else:
|
|
121
|
-
num_processes = max(self.num_processes, 1)
|
|
122
|
-
disable_multiprocessing = self.num_processes <= 0
|
|
123
|
-
|
|
124
|
-
with Pool(processes=num_processes) as pool:
|
|
125
|
-
video_metadatas_iter: T.Iterator[types.VideoMetadataOrError]
|
|
126
|
-
if disable_multiprocessing:
|
|
127
|
-
video_metadatas_iter = map(
|
|
128
|
-
GeotagVideosFromExifToolVideo.geotag_video, rdf_descriptions
|
|
129
|
-
)
|
|
130
|
-
else:
|
|
131
|
-
video_metadatas_iter = pool.imap(
|
|
132
|
-
GeotagVideosFromExifToolVideo.geotag_video,
|
|
133
|
-
rdf_descriptions,
|
|
134
|
-
)
|
|
135
|
-
video_metadata_or_errors = list(
|
|
136
|
-
tqdm(
|
|
137
|
-
video_metadatas_iter,
|
|
138
|
-
desc="Extracting GPS tracks from ExifTool XML",
|
|
139
|
-
unit="videos",
|
|
140
|
-
disable=LOG.getEffectiveLevel() <= logging.DEBUG,
|
|
141
|
-
total=len(self.video_paths),
|
|
142
|
-
)
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
return error_metadatas + video_metadata_or_errors
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import typing as T
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
known_parser_options = ["source", "pattern", "exiftool_path"]
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class CliParserOptions(T.TypedDict, total=False):
|
|
9
|
-
source: str
|
|
10
|
-
pattern: T.Optional[str]
|
|
11
|
-
exiftool_path: T.Optional[Path]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class CliOptions(T.TypedDict, total=False):
|
|
15
|
-
paths: T.Sequence[Path]
|
|
16
|
-
recursive: bool
|
|
17
|
-
geotag_sources_options: T.Sequence[CliParserOptions]
|
|
18
|
-
geotag_source_path: Path
|
|
19
|
-
exiftool_path: Path
|
|
20
|
-
num_processes: int
|
|
21
|
-
device_make: T.Optional[str]
|
|
22
|
-
device_model: T.Optional[str]
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import typing as T
|
|
3
|
-
from multiprocessing import Pool
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import tqdm
|
|
7
|
-
|
|
8
|
-
from .. import exceptions, geo, utils
|
|
9
|
-
from ..geotag import gpmf_gps_filter, utils as video_utils
|
|
10
|
-
from ..telemetry import GPSPoint
|
|
11
|
-
from ..types import (
|
|
12
|
-
ErrorMetadata,
|
|
13
|
-
FileType,
|
|
14
|
-
MetadataOrError,
|
|
15
|
-
VideoMetadata,
|
|
16
|
-
VideoMetadataOrError,
|
|
17
|
-
)
|
|
18
|
-
from . import video_data_parser_factory
|
|
19
|
-
from .cli_options import CliOptions
|
|
20
|
-
from .extractors.base_parser import BaseParser
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
LOG = logging.getLogger(__name__)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class VideoDataExtractor:
|
|
27
|
-
options: CliOptions
|
|
28
|
-
|
|
29
|
-
def __init__(self, options: CliOptions) -> None:
|
|
30
|
-
self.options = options
|
|
31
|
-
|
|
32
|
-
def process(self) -> T.List[MetadataOrError]:
|
|
33
|
-
paths = self.options["paths"]
|
|
34
|
-
self._check_paths(paths)
|
|
35
|
-
video_files = utils.find_videos(paths)
|
|
36
|
-
self._check_sources_cardinality(video_files)
|
|
37
|
-
|
|
38
|
-
num_processes = self.options["num_processes"] or None
|
|
39
|
-
with Pool(processes=num_processes) as pool:
|
|
40
|
-
if num_processes == 1:
|
|
41
|
-
iter: T.Iterator[VideoMetadataOrError] = map(
|
|
42
|
-
self.process_file, video_files
|
|
43
|
-
)
|
|
44
|
-
else:
|
|
45
|
-
iter = pool.imap(self.process_file, video_files)
|
|
46
|
-
|
|
47
|
-
video_metadata_or_errors = list(
|
|
48
|
-
tqdm.tqdm(
|
|
49
|
-
iter,
|
|
50
|
-
desc="Extracting GPS tracks",
|
|
51
|
-
unit="videos",
|
|
52
|
-
disable=LOG.getEffectiveLevel() <= logging.DEBUG,
|
|
53
|
-
total=len(video_files),
|
|
54
|
-
)
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
return video_metadata_or_errors
|
|
58
|
-
|
|
59
|
-
def process_file(self, file: Path) -> VideoMetadataOrError:
|
|
60
|
-
parsers = video_data_parser_factory.make_parsers(file, self.options)
|
|
61
|
-
points: T.Sequence[geo.Point] = []
|
|
62
|
-
make = self.options["device_make"]
|
|
63
|
-
model = self.options["device_model"]
|
|
64
|
-
|
|
65
|
-
ex: T.Optional[Exception]
|
|
66
|
-
for parser in parsers:
|
|
67
|
-
log_vars = {
|
|
68
|
-
"filename": file,
|
|
69
|
-
"parser": parser.parser_label,
|
|
70
|
-
"source": parser.geotag_source_path,
|
|
71
|
-
}
|
|
72
|
-
try:
|
|
73
|
-
if not points:
|
|
74
|
-
points = self._extract_points(parser, log_vars)
|
|
75
|
-
if not model:
|
|
76
|
-
model = parser.extract_model()
|
|
77
|
-
if not make:
|
|
78
|
-
make = parser.extract_make()
|
|
79
|
-
except Exception as e:
|
|
80
|
-
ex = e
|
|
81
|
-
LOG.warning(
|
|
82
|
-
'%(filename)s: Exception for parser %(parser)s while processing source %(source)s: "%(e)s"',
|
|
83
|
-
{**log_vars, "e": e},
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
# After trying all parsers, return the points if we found any, otherwise
|
|
87
|
-
# the last exception thrown or a default one.
|
|
88
|
-
# Note that if we have points, we return them, regardless of exceptions
|
|
89
|
-
# with make or model.
|
|
90
|
-
if points:
|
|
91
|
-
video_metadata = VideoMetadata(
|
|
92
|
-
filename=file,
|
|
93
|
-
filetype=FileType.VIDEO,
|
|
94
|
-
md5sum=None,
|
|
95
|
-
filesize=utils.get_file_size(file),
|
|
96
|
-
points=points,
|
|
97
|
-
make=make,
|
|
98
|
-
model=model,
|
|
99
|
-
)
|
|
100
|
-
video_metadata.update_md5sum()
|
|
101
|
-
return video_metadata
|
|
102
|
-
else:
|
|
103
|
-
return ErrorMetadata(
|
|
104
|
-
filename=file,
|
|
105
|
-
error=(
|
|
106
|
-
ex
|
|
107
|
-
if ex
|
|
108
|
-
else exceptions.MapillaryVideoGPSNotFoundError(
|
|
109
|
-
"No GPS data found from the video"
|
|
110
|
-
)
|
|
111
|
-
),
|
|
112
|
-
filetype=FileType.VIDEO,
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
def _extract_points(
|
|
116
|
-
self, parser: BaseParser, log_vars: T.Dict
|
|
117
|
-
) -> T.Sequence[geo.Point]:
|
|
118
|
-
points = parser.extract_points()
|
|
119
|
-
if points:
|
|
120
|
-
LOG.debug(
|
|
121
|
-
"%(filename)s: %(points)d points extracted by parser %(parser)s from file %(source)s}",
|
|
122
|
-
{**log_vars, "points": len(points)},
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
return self._sanitize_points(points)
|
|
126
|
-
|
|
127
|
-
@staticmethod
|
|
128
|
-
def _check_paths(import_paths: T.Sequence[Path]):
|
|
129
|
-
for path in import_paths:
|
|
130
|
-
if not path.is_file() and not path.is_dir():
|
|
131
|
-
raise exceptions.MapillaryFileNotFoundError(
|
|
132
|
-
f"Import file or directory not found: {path}"
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
def _check_sources_cardinality(self, files: T.Sequence[Path]):
|
|
136
|
-
if len(files) > 1:
|
|
137
|
-
for parser_opts in self.options["geotag_sources_options"]:
|
|
138
|
-
pattern = parser_opts.get("pattern")
|
|
139
|
-
if pattern and "%" not in pattern:
|
|
140
|
-
raise exceptions.MapillaryUserError(
|
|
141
|
-
"Multiple video files found: Geotag source pattern for source %s must include filename placeholders",
|
|
142
|
-
parser_opts["source"],
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
@staticmethod
|
|
146
|
-
def _sanitize_points(points: T.Sequence[geo.Point]) -> T.Sequence[geo.Point]:
|
|
147
|
-
"""
|
|
148
|
-
Deduplicates points, when possible removes noisy ones, and checks
|
|
149
|
-
against stationary videos
|
|
150
|
-
"""
|
|
151
|
-
|
|
152
|
-
if not points:
|
|
153
|
-
raise exceptions.MapillaryVideoGPSNotFoundError(
|
|
154
|
-
"No GPS data found in the given sources"
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
points = geo.extend_deduplicate_points(points)
|
|
158
|
-
|
|
159
|
-
if all(isinstance(p, GPSPoint) for p in points):
|
|
160
|
-
points = T.cast(
|
|
161
|
-
T.Sequence[geo.Point],
|
|
162
|
-
gpmf_gps_filter.remove_noisy_points(
|
|
163
|
-
T.cast(T.Sequence[GPSPoint], points)
|
|
164
|
-
),
|
|
165
|
-
)
|
|
166
|
-
if not points:
|
|
167
|
-
raise exceptions.MapillaryGPSNoiseError("GPS is too noisy")
|
|
168
|
-
|
|
169
|
-
stationary = video_utils.is_video_stationary(
|
|
170
|
-
geo.get_max_distance_from_start([(p.lat, p.lon) for p in points])
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
if stationary:
|
|
174
|
-
raise exceptions.MapillaryStationaryVideoError("Stationary video")
|
|
175
|
-
|
|
176
|
-
return points
|