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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
import typing as T
|
|
5
|
+
|
|
6
|
+
if sys.version_info >= (3, 12):
|
|
7
|
+
from typing import override
|
|
8
|
+
else:
|
|
9
|
+
from typing_extensions import override
|
|
10
|
+
|
|
11
|
+
import gpxpy
|
|
12
|
+
import gpxpy.gpx
|
|
13
|
+
|
|
14
|
+
from .. import geo, types
|
|
15
|
+
|
|
16
|
+
from ..telemetry import CAMMGPSPoint, GPSPoint
|
|
17
|
+
from ..types import (
|
|
18
|
+
BaseSerializer,
|
|
19
|
+
ErrorMetadata,
|
|
20
|
+
ImageMetadata,
|
|
21
|
+
MetadataOrError,
|
|
22
|
+
VideoMetadata,
|
|
23
|
+
)
|
|
24
|
+
from .description import DescriptionJSONSerializer
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GPXSerializer(BaseSerializer):
|
|
28
|
+
@override
|
|
29
|
+
@classmethod
|
|
30
|
+
def serialize(cls, metadatas: T.Sequence[MetadataOrError]) -> bytes:
|
|
31
|
+
gpx = cls.as_gpx(metadatas)
|
|
32
|
+
return gpx.to_xml().encode("utf-8")
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def as_gpx(cls, metadatas: T.Sequence[MetadataOrError]) -> gpxpy.gpx.GPX:
|
|
36
|
+
gpx = gpxpy.gpx.GPX()
|
|
37
|
+
|
|
38
|
+
error_metadatas = []
|
|
39
|
+
image_metadatas = []
|
|
40
|
+
video_metadatas = []
|
|
41
|
+
|
|
42
|
+
for metadata in metadatas:
|
|
43
|
+
if isinstance(metadata, ErrorMetadata):
|
|
44
|
+
error_metadatas.append(metadata)
|
|
45
|
+
elif isinstance(metadata, ImageMetadata):
|
|
46
|
+
image_metadatas.append(metadata)
|
|
47
|
+
elif isinstance(metadata, VideoMetadata):
|
|
48
|
+
video_metadatas.append(metadata)
|
|
49
|
+
|
|
50
|
+
for metadata in error_metadatas:
|
|
51
|
+
gpx_track = gpxpy.gpx.GPXTrack()
|
|
52
|
+
gpx_track.name = str(metadata.filename)
|
|
53
|
+
gpx_track.description = cls._build_gpx_description(metadata, ["filename"])
|
|
54
|
+
gpx.tracks.append(gpx_track)
|
|
55
|
+
|
|
56
|
+
sequences = types.group_and_sort_images(image_metadatas)
|
|
57
|
+
for sequence_uuid, sequence in sequences.items():
|
|
58
|
+
gpx.tracks.append(cls.image_sequence_as_gpx_track(sequence_uuid, sequence))
|
|
59
|
+
|
|
60
|
+
for metadata in video_metadatas:
|
|
61
|
+
gpx.tracks.append(cls.as_gpx_track(metadata))
|
|
62
|
+
|
|
63
|
+
return gpx
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def as_gpx_point(cls, point: geo.Point) -> gpxpy.gpx.GPXTrackPoint:
|
|
67
|
+
gpx_point = gpxpy.gpx.GPXTrackPoint(
|
|
68
|
+
latitude=point.lat,
|
|
69
|
+
longitude=point.lon,
|
|
70
|
+
elevation=point.alt,
|
|
71
|
+
time=datetime.datetime.fromtimestamp(point.time, datetime.timezone.utc),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if isinstance(point, types.ImageMetadata):
|
|
75
|
+
gpx_point.name = point.filename.name
|
|
76
|
+
elif isinstance(point, CAMMGPSPoint):
|
|
77
|
+
gpx_point.time = datetime.datetime.fromtimestamp(
|
|
78
|
+
point.time_gps_epoch, datetime.timezone.utc
|
|
79
|
+
)
|
|
80
|
+
elif isinstance(point, GPSPoint):
|
|
81
|
+
if point.epoch_time is not None:
|
|
82
|
+
gpx_point.time = datetime.datetime.fromtimestamp(
|
|
83
|
+
point.epoch_time, datetime.timezone.utc
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return gpx_point
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def as_gpx_track(cls, metadata: VideoMetadata) -> gpxpy.gpx.GPXTrack:
|
|
90
|
+
gpx_segment = gpxpy.gpx.GPXTrackSegment()
|
|
91
|
+
for point in metadata.points:
|
|
92
|
+
gpx_point = cls.as_gpx_point(point)
|
|
93
|
+
gpx_segment.points.append(gpx_point)
|
|
94
|
+
gpx_track = gpxpy.gpx.GPXTrack()
|
|
95
|
+
gpx_track.name = str(metadata.filename)
|
|
96
|
+
gpx_track.description = cls._build_gpx_description(
|
|
97
|
+
metadata, ["filename", "MAPGPSTrack"]
|
|
98
|
+
)
|
|
99
|
+
gpx_track.segments.append(gpx_segment)
|
|
100
|
+
return gpx_track
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def image_sequence_as_gpx_track(
|
|
104
|
+
cls, sequence_uuid: str, sequence: T.Sequence[ImageMetadata]
|
|
105
|
+
) -> gpxpy.gpx.GPXTrack:
|
|
106
|
+
gpx_segment = gpxpy.gpx.GPXTrackSegment()
|
|
107
|
+
for metadata in sequence:
|
|
108
|
+
gpx_point = cls.as_gpx_point(metadata)
|
|
109
|
+
gpx_segment.points.append(gpx_point)
|
|
110
|
+
gpx_track = gpxpy.gpx.GPXTrack()
|
|
111
|
+
gpx_track.name = sequence_uuid
|
|
112
|
+
gpx_track.description = cls._build_gpx_description(
|
|
113
|
+
metadata,
|
|
114
|
+
[
|
|
115
|
+
"filename",
|
|
116
|
+
"MAPLongitude",
|
|
117
|
+
"MAPLatitude",
|
|
118
|
+
"MAPCaptureTime",
|
|
119
|
+
"MAPAltitude",
|
|
120
|
+
],
|
|
121
|
+
)
|
|
122
|
+
gpx_track.segments.append(gpx_segment)
|
|
123
|
+
return gpx_track
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def _build_gpx_description(
|
|
127
|
+
cls, metadata: MetadataOrError, excluded_properties: T.Sequence[str]
|
|
128
|
+
) -> str:
|
|
129
|
+
desc = T.cast(T.Dict, DescriptionJSONSerializer.as_desc(metadata))
|
|
130
|
+
for prop in excluded_properties:
|
|
131
|
+
desc.pop(prop, None)
|
|
132
|
+
return json.dumps(desc, sort_keys=True, separators=(",", ":"))
|
mapillary_tools/telemetry.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import dataclasses
|
|
2
|
-
import typing as T
|
|
3
4
|
from enum import Enum, unique
|
|
4
5
|
|
|
5
6
|
from .geo import Point
|
|
@@ -12,16 +13,8 @@ class GPSFix(Enum):
|
|
|
12
13
|
FIX_3D = 3
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
@dataclasses.dataclass
|
|
16
|
-
class GPSPoint(Point):
|
|
17
|
-
epoch_time: T.Optional[float]
|
|
18
|
-
fix: T.Optional[GPSFix]
|
|
19
|
-
precision: T.Optional[float]
|
|
20
|
-
ground_speed: T.Optional[float]
|
|
21
|
-
|
|
22
|
-
|
|
23
16
|
@dataclasses.dataclass(order=True)
|
|
24
|
-
class
|
|
17
|
+
class TimestampedMeasurement:
|
|
25
18
|
"""Base class for all telemetry measurements.
|
|
26
19
|
|
|
27
20
|
All telemetry measurements must have a timestamp in seconds.
|
|
@@ -32,8 +25,28 @@ class TelemetryMeasurement:
|
|
|
32
25
|
time: float
|
|
33
26
|
|
|
34
27
|
|
|
28
|
+
@dataclasses.dataclass
|
|
29
|
+
class GPSPoint(TimestampedMeasurement, Point):
|
|
30
|
+
epoch_time: float | None
|
|
31
|
+
fix: GPSFix | None
|
|
32
|
+
precision: float | None
|
|
33
|
+
ground_speed: float | None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclasses.dataclass
|
|
37
|
+
class CAMMGPSPoint(TimestampedMeasurement, Point):
|
|
38
|
+
time_gps_epoch: float
|
|
39
|
+
gps_fix_type: int
|
|
40
|
+
horizontal_accuracy: float
|
|
41
|
+
vertical_accuracy: float
|
|
42
|
+
velocity_east: float
|
|
43
|
+
velocity_north: float
|
|
44
|
+
velocity_up: float
|
|
45
|
+
speed_accuracy: float
|
|
46
|
+
|
|
47
|
+
|
|
35
48
|
@dataclasses.dataclass(order=True)
|
|
36
|
-
class GyroscopeData(
|
|
49
|
+
class GyroscopeData(TimestampedMeasurement):
|
|
37
50
|
"""Gyroscope signal in radians/seconds around XYZ axes of the camera."""
|
|
38
51
|
|
|
39
52
|
x: float
|
|
@@ -42,7 +55,7 @@ class GyroscopeData(TelemetryMeasurement):
|
|
|
42
55
|
|
|
43
56
|
|
|
44
57
|
@dataclasses.dataclass(order=True)
|
|
45
|
-
class AccelerationData(
|
|
58
|
+
class AccelerationData(TimestampedMeasurement):
|
|
46
59
|
"""Accelerometer reading in meters/second^2 along XYZ axes of the camera."""
|
|
47
60
|
|
|
48
61
|
x: float
|
|
@@ -51,7 +64,7 @@ class AccelerationData(TelemetryMeasurement):
|
|
|
51
64
|
|
|
52
65
|
|
|
53
66
|
@dataclasses.dataclass(order=True)
|
|
54
|
-
class MagnetometerData(
|
|
67
|
+
class MagnetometerData(TimestampedMeasurement):
|
|
55
68
|
"""Ambient magnetic field."""
|
|
56
69
|
|
|
57
70
|
x: float
|