mapillary-tools 0.13.3a1__py3-none-any.whl → 0.14.0__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 +287 -22
- mapillary_tools/authenticate.py +326 -64
- mapillary_tools/blackvue_parser.py +195 -0
- mapillary_tools/camm/camm_builder.py +55 -97
- mapillary_tools/camm/camm_parser.py +429 -181
- mapillary_tools/commands/__main__.py +17 -8
- 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 +44 -13
- mapillary_tools/commands/video_process_and_upload.py +19 -5
- mapillary_tools/config.py +65 -26
- mapillary_tools/constants.py +141 -18
- mapillary_tools/exceptions.py +37 -34
- mapillary_tools/exif_read.py +221 -116
- mapillary_tools/exif_write.py +10 -8
- mapillary_tools/exiftool_read.py +33 -42
- mapillary_tools/exiftool_read_video.py +97 -47
- mapillary_tools/exiftool_runner.py +57 -0
- mapillary_tools/ffmpeg.py +417 -242
- mapillary_tools/geo.py +158 -118
- mapillary_tools/geotag/__init__.py +0 -1
- mapillary_tools/geotag/base.py +147 -0
- mapillary_tools/geotag/factory.py +307 -0
- mapillary_tools/geotag/geotag_images_from_exif.py +14 -131
- mapillary_tools/geotag/geotag_images_from_exiftool.py +136 -85
- mapillary_tools/geotag/geotag_images_from_gpx.py +60 -124
- 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 +88 -51
- mapillary_tools/geotag/geotag_videos_from_exiftool.py +123 -0
- mapillary_tools/geotag/geotag_videos_from_gpx.py +52 -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 +182 -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/geotag/video_extractors/gpx.py +116 -0
- mapillary_tools/geotag/video_extractors/native.py +160 -0
- mapillary_tools/{geotag → gpmf}/gpmf_parser.py +205 -182
- mapillary_tools/{geotag → gpmf}/gps_filter.py +5 -3
- mapillary_tools/history.py +134 -20
- mapillary_tools/mp4/construct_mp4_parser.py +17 -10
- 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 +184 -414
- mapillary_tools/process_sequence_properties.py +594 -225
- mapillary_tools/sample_video.py +20 -26
- mapillary_tools/serializer/description.py +587 -0
- mapillary_tools/serializer/gpx.py +132 -0
- mapillary_tools/telemetry.py +26 -13
- mapillary_tools/types.py +98 -611
- mapillary_tools/upload.py +408 -416
- mapillary_tools/upload_api_v4.py +172 -174
- mapillary_tools/uploader.py +804 -284
- mapillary_tools/utils.py +49 -18
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/METADATA +93 -35
- mapillary_tools-0.14.0.dist-info/RECORD +75 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/WHEEL +1 -1
- mapillary_tools/geotag/blackvue_parser.py +0 -118
- 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/gpx_parser.py +0 -108
- 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.0.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info/licenses}/LICENSE +0 -0
- {mapillary_tools-0.13.3a1.dist-info → mapillary_tools-0.14.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import io
|
|
2
4
|
import typing as T
|
|
3
5
|
|
|
4
|
-
from .. import geo
|
|
6
|
+
from .. import geo
|
|
5
7
|
from ..mp4 import (
|
|
6
8
|
construct_mp4_parser as cparser,
|
|
7
9
|
mp4_sample_parser as sample_parser,
|
|
@@ -11,84 +13,35 @@ from ..mp4 import (
|
|
|
11
13
|
from . import camm_parser
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
TelemetryMeasurement
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
]
|
|
16
|
+
def _build_camm_sample(measurement: camm_parser.TelemetryMeasurement) -> bytes:
|
|
17
|
+
if camm_parser.GoProGPSSampleEntry.serializable(measurement):
|
|
18
|
+
return camm_parser.GoProGPSSampleEntry.serialize(measurement)
|
|
18
19
|
|
|
20
|
+
for sample_entry_cls in camm_parser.SAMPLE_ENTRY_CLS_BY_CAMM_TYPE.values():
|
|
21
|
+
if sample_entry_cls.serializable(measurement):
|
|
22
|
+
return sample_entry_cls.serialize(measurement)
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
if isinstance(measurement, geo.Point):
|
|
22
|
-
return camm_parser.CAMMSampleData.build(
|
|
23
|
-
{
|
|
24
|
-
"type": camm_parser.CAMMType.MIN_GPS.value,
|
|
25
|
-
"data": [
|
|
26
|
-
measurement.lat,
|
|
27
|
-
measurement.lon,
|
|
28
|
-
-1.0 if measurement.alt is None else measurement.alt,
|
|
29
|
-
],
|
|
30
|
-
}
|
|
31
|
-
)
|
|
32
|
-
elif isinstance(measurement, telemetry.AccelerationData):
|
|
33
|
-
# Accelerometer reading in meters/second^2 along XYZ axes of the camera.
|
|
34
|
-
return camm_parser.CAMMSampleData.build(
|
|
35
|
-
{
|
|
36
|
-
"type": camm_parser.CAMMType.ACCELERATION.value,
|
|
37
|
-
"data": [
|
|
38
|
-
measurement.x,
|
|
39
|
-
measurement.y,
|
|
40
|
-
measurement.z,
|
|
41
|
-
],
|
|
42
|
-
}
|
|
43
|
-
)
|
|
44
|
-
elif isinstance(measurement, telemetry.GyroscopeData):
|
|
45
|
-
# Gyroscope signal in radians/seconds around XYZ axes of the camera. Rotation is positive in the counterclockwise direction.
|
|
46
|
-
return camm_parser.CAMMSampleData.build(
|
|
47
|
-
{
|
|
48
|
-
"type": camm_parser.CAMMType.GYRO.value,
|
|
49
|
-
"data": [
|
|
50
|
-
measurement.x,
|
|
51
|
-
measurement.y,
|
|
52
|
-
measurement.z,
|
|
53
|
-
],
|
|
54
|
-
}
|
|
55
|
-
)
|
|
56
|
-
elif isinstance(measurement, telemetry.MagnetometerData):
|
|
57
|
-
# Ambient magnetic field.
|
|
58
|
-
return camm_parser.CAMMSampleData.build(
|
|
59
|
-
{
|
|
60
|
-
"type": camm_parser.CAMMType.MAGNETIC_FIELD.value,
|
|
61
|
-
"data": [
|
|
62
|
-
measurement.x,
|
|
63
|
-
measurement.y,
|
|
64
|
-
measurement.z,
|
|
65
|
-
],
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
else:
|
|
69
|
-
raise ValueError(f"unexpected measurement type {type(measurement)}")
|
|
24
|
+
raise ValueError(f"Unsupported measurement type {type(measurement)}")
|
|
70
25
|
|
|
71
26
|
|
|
72
27
|
def _create_edit_list_from_points(
|
|
73
|
-
|
|
28
|
+
tracks: T.Sequence[T.Sequence[geo.Point]],
|
|
74
29
|
movie_timescale: int,
|
|
75
30
|
media_timescale: int,
|
|
76
31
|
) -> builder.BoxDict:
|
|
77
|
-
entries:
|
|
32
|
+
entries: list[dict] = []
|
|
78
33
|
|
|
79
|
-
|
|
34
|
+
non_empty_tracks = [track for track in tracks if track]
|
|
80
35
|
|
|
81
|
-
for idx,
|
|
82
|
-
assert 0 <=
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
assert points[0].time <= points[-1].time, (
|
|
86
|
-
f"expect points to be sorted but got first point {points[0]} and last point {points[-1]}"
|
|
36
|
+
for idx, track in enumerate(non_empty_tracks):
|
|
37
|
+
assert 0 <= track[0].time, f"expect non-negative point time but got {track[0]}"
|
|
38
|
+
assert track[0].time <= track[-1].time, (
|
|
39
|
+
f"expect points to be sorted but got first point {track[0]} and last point {track[-1]}"
|
|
87
40
|
)
|
|
88
41
|
|
|
89
42
|
if idx == 0:
|
|
90
|
-
if 0 <
|
|
91
|
-
segment_duration = int(
|
|
43
|
+
if 0 < track[0].time:
|
|
44
|
+
segment_duration = int(track[0].time * movie_timescale)
|
|
92
45
|
# put an empty edit list entry to skip the initial gap
|
|
93
46
|
entries.append(
|
|
94
47
|
{
|
|
@@ -100,8 +53,8 @@ def _create_edit_list_from_points(
|
|
|
100
53
|
}
|
|
101
54
|
)
|
|
102
55
|
else:
|
|
103
|
-
media_time = int(
|
|
104
|
-
segment_duration = int((
|
|
56
|
+
media_time = int(track[0].time * media_timescale)
|
|
57
|
+
segment_duration = int((track[-1].time - track[0].time) * movie_timescale)
|
|
105
58
|
entries.append(
|
|
106
59
|
{
|
|
107
60
|
"media_time": media_time,
|
|
@@ -119,18 +72,8 @@ def _create_edit_list_from_points(
|
|
|
119
72
|
}
|
|
120
73
|
|
|
121
74
|
|
|
122
|
-
def _multiplex(
|
|
123
|
-
points: T.Sequence[geo.Point],
|
|
124
|
-
measurements: T.Optional[T.List[telemetry.TelemetryMeasurement]] = None,
|
|
125
|
-
) -> T.List[TelemetryMeasurement]:
|
|
126
|
-
mutiplexed: T.List[TelemetryMeasurement] = [*points, *(measurements or [])]
|
|
127
|
-
mutiplexed.sort(key=lambda m: m.time)
|
|
128
|
-
|
|
129
|
-
return mutiplexed
|
|
130
|
-
|
|
131
|
-
|
|
132
75
|
def convert_telemetry_to_raw_samples(
|
|
133
|
-
measurements: T.Sequence[TelemetryMeasurement],
|
|
76
|
+
measurements: T.Sequence[camm_parser.TelemetryMeasurement],
|
|
134
77
|
timescale: int,
|
|
135
78
|
) -> T.Generator[sample_parser.RawSample, None, None]:
|
|
136
79
|
for idx, measurement in enumerate(measurements):
|
|
@@ -281,29 +224,44 @@ def create_camm_trak(
|
|
|
281
224
|
}
|
|
282
225
|
|
|
283
226
|
|
|
284
|
-
def camm_sample_generator2(
|
|
285
|
-
video_metadata: types.VideoMetadata,
|
|
286
|
-
telemetry_measurements: T.Optional[T.List[telemetry.TelemetryMeasurement]] = None,
|
|
287
|
-
):
|
|
227
|
+
def camm_sample_generator2(camm_info: camm_parser.CAMMInfo):
|
|
288
228
|
def _f(
|
|
289
229
|
fp: T.BinaryIO,
|
|
290
|
-
moov_children:
|
|
230
|
+
moov_children: list[builder.BoxDict],
|
|
291
231
|
) -> T.Generator[io.IOBase, None, None]:
|
|
292
232
|
movie_timescale = builder.find_movie_timescale(moov_children)
|
|
293
|
-
#
|
|
233
|
+
# Make sure the precision of timedeltas not lower than 0.001 (1ms)
|
|
294
234
|
media_timescale = max(1000, movie_timescale)
|
|
295
235
|
|
|
296
|
-
# points
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
236
|
+
# Multiplex points for creating elst
|
|
237
|
+
track: list[geo.Point] = [
|
|
238
|
+
*(camm_info.gps or []),
|
|
239
|
+
*(camm_info.mini_gps or []),
|
|
240
|
+
]
|
|
241
|
+
track.sort(key=lambda p: p.time)
|
|
242
|
+
if track and track[0].time < 0:
|
|
243
|
+
track = [p for p in track if p.time >= 0]
|
|
244
|
+
elst = _create_edit_list_from_points([track], movie_timescale, media_timescale)
|
|
245
|
+
|
|
246
|
+
# Multiplex telemetry measurements
|
|
247
|
+
measurements: list[camm_parser.TelemetryMeasurement] = [
|
|
248
|
+
*(camm_info.gps or []),
|
|
249
|
+
*(camm_info.mini_gps or []),
|
|
250
|
+
*(camm_info.accl or []),
|
|
251
|
+
*(camm_info.gyro or []),
|
|
252
|
+
*(camm_info.magn or []),
|
|
253
|
+
]
|
|
254
|
+
measurements.sort(key=lambda m: m.time)
|
|
255
|
+
if measurements and measurements[0].time < 0:
|
|
256
|
+
measurements = [m for m in measurements if m.time >= 0]
|
|
257
|
+
|
|
258
|
+
# Serialize the telemetry measurements into MP4 samples
|
|
302
259
|
camm_samples = list(
|
|
303
260
|
convert_telemetry_to_raw_samples(measurements, media_timescale)
|
|
304
261
|
)
|
|
262
|
+
|
|
305
263
|
camm_trak = create_camm_trak(camm_samples, media_timescale)
|
|
306
|
-
|
|
264
|
+
|
|
307
265
|
if T.cast(T.Dict, elst["data"])["entries"]:
|
|
308
266
|
T.cast(T.List[builder.BoxDict], camm_trak["data"]).append(
|
|
309
267
|
{
|
|
@@ -313,19 +271,19 @@ def camm_sample_generator2(
|
|
|
313
271
|
)
|
|
314
272
|
moov_children.append(camm_trak)
|
|
315
273
|
|
|
316
|
-
udta_data:
|
|
317
|
-
if
|
|
274
|
+
udta_data: list[builder.BoxDict] = []
|
|
275
|
+
if camm_info.make:
|
|
318
276
|
udta_data.append(
|
|
319
277
|
{
|
|
320
278
|
"type": b"@mak",
|
|
321
|
-
"data":
|
|
279
|
+
"data": camm_info.make.encode("utf-8"),
|
|
322
280
|
}
|
|
323
281
|
)
|
|
324
|
-
if
|
|
282
|
+
if camm_info.model:
|
|
325
283
|
udta_data.append(
|
|
326
284
|
{
|
|
327
285
|
"type": b"@mod",
|
|
328
|
-
"data":
|
|
286
|
+
"data": camm_info.model.encode("utf-8"),
|
|
329
287
|
}
|
|
330
288
|
)
|
|
331
289
|
if udta_data:
|