mapillary-tools 0.13.0__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/camm/camm_builder.py +14 -62
- mapillary_tools/camm/camm_parser.py +292 -73
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +7 -1
- mapillary_tools/geotag/gpmf_parser.py +28 -5
- mapillary_tools/telemetry.py +20 -8
- mapillary_tools/upload.py +2 -3
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +8 -12
- mapillary_tools/video_data_extraction/extractors/gpx_parser.py +60 -23
- {mapillary_tools-0.13.0.dist-info → mapillary_tools-0.13.1.dist-info}/METADATA +1 -1
- {mapillary_tools-0.13.0.dist-info → mapillary_tools-0.13.1.dist-info}/RECORD +15 -15
- {mapillary_tools-0.13.0.dist-info → mapillary_tools-0.13.1.dist-info}/LICENSE +0 -0
- {mapillary_tools-0.13.0.dist-info → mapillary_tools-0.13.1.dist-info}/WHEEL +0 -0
- {mapillary_tools-0.13.0.dist-info → mapillary_tools-0.13.1.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.13.0.dist-info → mapillary_tools-0.13.1.dist-info}/top_level.txt +0 -0
mapillary_tools/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "0.13.
|
|
1
|
+
VERSION = "0.13.1"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import typing as T
|
|
3
3
|
|
|
4
|
-
from .. import geo,
|
|
4
|
+
from .. import geo, types
|
|
5
5
|
from ..mp4 import (
|
|
6
6
|
construct_mp4_parser as cparser,
|
|
7
7
|
mp4_sample_parser as sample_parser,
|
|
@@ -11,62 +11,11 @@ from ..mp4 import (
|
|
|
11
11
|
from . import camm_parser
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
TelemetryMeasurement
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _build_camm_sample(measurement: TelemetryMeasurement) -> bytes:
|
|
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)}")
|
|
14
|
+
def _build_camm_sample(measurement: camm_parser.TelemetryMeasurement) -> bytes:
|
|
15
|
+
for sample_entry_cls in camm_parser.SAMPLE_ENTRY_CLS_BY_CAMM_TYPE.values():
|
|
16
|
+
if sample_entry_cls.serializable(measurement):
|
|
17
|
+
return sample_entry_cls.serialize(measurement)
|
|
18
|
+
raise ValueError(f"Unsupported measurement type {type(measurement)}")
|
|
70
19
|
|
|
71
20
|
|
|
72
21
|
def _create_edit_list_from_points(
|
|
@@ -121,16 +70,19 @@ def _create_edit_list_from_points(
|
|
|
121
70
|
|
|
122
71
|
def _multiplex(
|
|
123
72
|
points: T.Sequence[geo.Point],
|
|
124
|
-
measurements: T.Optional[T.List[
|
|
125
|
-
) -> T.List[TelemetryMeasurement]:
|
|
126
|
-
mutiplexed: T.List[TelemetryMeasurement] = [
|
|
73
|
+
measurements: T.Optional[T.List[camm_parser.TelemetryMeasurement]] = None,
|
|
74
|
+
) -> T.List[camm_parser.TelemetryMeasurement]:
|
|
75
|
+
mutiplexed: T.List[camm_parser.TelemetryMeasurement] = [
|
|
76
|
+
*points,
|
|
77
|
+
*(measurements or []),
|
|
78
|
+
]
|
|
127
79
|
mutiplexed.sort(key=lambda m: m.time)
|
|
128
80
|
|
|
129
81
|
return mutiplexed
|
|
130
82
|
|
|
131
83
|
|
|
132
84
|
def convert_telemetry_to_raw_samples(
|
|
133
|
-
measurements: T.Sequence[TelemetryMeasurement],
|
|
85
|
+
measurements: T.Sequence[camm_parser.TelemetryMeasurement],
|
|
134
86
|
timescale: int,
|
|
135
87
|
) -> T.Generator[sample_parser.RawSample, None, None]:
|
|
136
88
|
for idx, measurement in enumerate(measurements):
|
|
@@ -283,7 +235,7 @@ def create_camm_trak(
|
|
|
283
235
|
|
|
284
236
|
def camm_sample_generator2(
|
|
285
237
|
video_metadata: types.VideoMetadata,
|
|
286
|
-
telemetry_measurements: T.Optional[T.List[
|
|
238
|
+
telemetry_measurements: T.Optional[T.List[camm_parser.TelemetryMeasurement]] = None,
|
|
287
239
|
):
|
|
288
240
|
def _f(
|
|
289
241
|
fp: T.BinaryIO,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# pyre-ignore-all-errors[5, 11, 16, 21, 24, 58]
|
|
2
2
|
|
|
3
|
+
import abc
|
|
3
4
|
import dataclasses
|
|
4
5
|
import io
|
|
5
6
|
import logging
|
|
@@ -19,9 +20,7 @@ LOG = logging.getLogger(__name__)
|
|
|
19
20
|
|
|
20
21
|
TelemetryMeasurement = T.Union[
|
|
21
22
|
geo.Point,
|
|
22
|
-
telemetry.
|
|
23
|
-
telemetry.GyroscopeData,
|
|
24
|
-
telemetry.MagnetometerData,
|
|
23
|
+
telemetry.TelemetryMeasurement,
|
|
25
24
|
]
|
|
26
25
|
|
|
27
26
|
|
|
@@ -36,100 +35,320 @@ class CAMMType(Enum):
|
|
|
36
35
|
GPS = 6
|
|
37
36
|
MAGNETIC_FIELD = 7
|
|
38
37
|
|
|
38
|
+
# Mapillary extensions are offset by 1024
|
|
39
|
+
# GoPro GPS is not compatible with CAMMType.GPS,
|
|
40
|
+
# so we use a new type to represent it
|
|
41
|
+
MLY_GOPRO_GPS = 1024 + 6
|
|
42
|
+
|
|
39
43
|
|
|
40
44
|
# All fields are little-endian
|
|
41
45
|
Float = C.Float32l
|
|
42
46
|
Double = C.Float64l
|
|
43
47
|
|
|
44
|
-
_SWITCH: T.Dict[int, C.Struct] = {
|
|
45
|
-
# angle_axis
|
|
46
|
-
CAMMType.ANGLE_AXIS.value: Float[3],
|
|
47
|
-
CAMMType.EXPOSURE_TIME.value: C.Struct(
|
|
48
|
-
"pixel_exposure_time" / C.Int32sl,
|
|
49
|
-
"rolling_shutter_skew_time" / C.Int32sl,
|
|
50
|
-
),
|
|
51
|
-
# gyro
|
|
52
|
-
CAMMType.GYRO.value: Float[3],
|
|
53
|
-
# acceleration
|
|
54
|
-
CAMMType.ACCELERATION.value: Float[3],
|
|
55
|
-
# position
|
|
56
|
-
CAMMType.POSITION.value: Float[3],
|
|
57
|
-
# lat, lon, alt
|
|
58
|
-
CAMMType.MIN_GPS.value: Double[3],
|
|
59
|
-
CAMMType.GPS.value: C.Struct(
|
|
60
|
-
"time_gps_epoch" / Double,
|
|
61
|
-
"gps_fix_type" / C.Int32sl,
|
|
62
|
-
"latitude" / Double,
|
|
63
|
-
"longitude" / Double,
|
|
64
|
-
"altitude" / Float,
|
|
65
|
-
"horizontal_accuracy" / Float,
|
|
66
|
-
"vertical_accuracy" / Float,
|
|
67
|
-
"velocity_east" / Float,
|
|
68
|
-
"velocity_north" / Float,
|
|
69
|
-
"velocity_up" / Float,
|
|
70
|
-
"speed_accuracy" / Float,
|
|
71
|
-
),
|
|
72
|
-
# magnetic_field
|
|
73
|
-
CAMMType.MAGNETIC_FIELD.value: Float[3],
|
|
74
|
-
}
|
|
75
48
|
|
|
76
|
-
|
|
77
|
-
C.Padding(2),
|
|
78
|
-
"type" / C.Int16ul,
|
|
79
|
-
"data"
|
|
80
|
-
/ C.Switch(
|
|
81
|
-
C.this.type,
|
|
82
|
-
_SWITCH,
|
|
83
|
-
),
|
|
84
|
-
)
|
|
49
|
+
TTelemetry = T.TypeVar("TTelemetry", bound=TelemetryMeasurement)
|
|
85
50
|
|
|
86
51
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
52
|
+
class CAMMSampleEntry(abc.ABC, T.Generic[TTelemetry]):
|
|
53
|
+
camm_type: CAMMType
|
|
54
|
+
|
|
55
|
+
construct: C.Struct
|
|
56
|
+
|
|
57
|
+
telemetry_cls: T.Type[TTelemetry]
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def serializable(cls, data: T.Any, throw: bool = False) -> bool:
|
|
61
|
+
# Use "is" for exact type match, instead of isinstance
|
|
62
|
+
if type(data) is cls.telemetry_cls:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
if throw:
|
|
66
|
+
raise TypeError(
|
|
67
|
+
f"{cls} can not serialize {type(data)}: expect {cls.telemetry_cls}"
|
|
68
|
+
)
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
@abc.abstractmethod
|
|
73
|
+
def serialize(cls, data: TTelemetry) -> bytes:
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
@abc.abstractmethod
|
|
78
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> TTelemetry:
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MinGPSSampleEntry(CAMMSampleEntry):
|
|
83
|
+
camm_type = CAMMType.MIN_GPS
|
|
84
|
+
|
|
85
|
+
construct = Double[3] # type: ignore
|
|
86
|
+
|
|
87
|
+
telemetry_cls = geo.Point
|
|
88
|
+
|
|
89
|
+
@classmethod
|
|
90
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> geo.Point:
|
|
94
91
|
return geo.Point(
|
|
95
92
|
time=sample.exact_time,
|
|
96
|
-
lat=
|
|
97
|
-
lon=
|
|
98
|
-
alt=
|
|
93
|
+
lat=data[0],
|
|
94
|
+
lon=data[1],
|
|
95
|
+
alt=data[2],
|
|
99
96
|
angle=None,
|
|
100
97
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def serialize(cls, data: geo.Point) -> bytes:
|
|
101
|
+
cls.serializable(data, throw=True)
|
|
102
|
+
|
|
103
|
+
return CAMMSampleData.build(
|
|
104
|
+
{
|
|
105
|
+
"type": cls.camm_type.value,
|
|
106
|
+
"data": [
|
|
107
|
+
data.lat,
|
|
108
|
+
data.lon,
|
|
109
|
+
-1.0 if data.alt is None else data.alt,
|
|
110
|
+
],
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class GPSSampleEntry(CAMMSampleEntry):
|
|
116
|
+
camm_type: CAMMType = CAMMType.GPS
|
|
117
|
+
|
|
118
|
+
construct = C.Struct(
|
|
119
|
+
"time_gps_epoch" / Double, # type: ignore
|
|
120
|
+
"gps_fix_type" / C.Int32sl, # type: ignore
|
|
121
|
+
"latitude" / Double, # type: ignore
|
|
122
|
+
"longitude" / Double, # type: ignore
|
|
123
|
+
"altitude" / Float, # type: ignore
|
|
124
|
+
"horizontal_accuracy" / Float, # type: ignore
|
|
125
|
+
"vertical_accuracy" / Float, # type: ignore
|
|
126
|
+
"velocity_east" / Float, # type: ignore
|
|
127
|
+
"velocity_north" / Float, # type: ignore
|
|
128
|
+
"velocity_up" / Float, # type: ignore
|
|
129
|
+
"speed_accuracy" / Float, # type: ignore
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
telemetry_cls = telemetry.CAMMGPSPoint
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> telemetry.CAMMGPSPoint:
|
|
136
|
+
return telemetry.CAMMGPSPoint(
|
|
105
137
|
time=sample.exact_time,
|
|
106
|
-
lat=
|
|
107
|
-
lon=
|
|
108
|
-
alt=
|
|
138
|
+
lat=data.latitude,
|
|
139
|
+
lon=data.longitude,
|
|
140
|
+
alt=data.altitude,
|
|
109
141
|
angle=None,
|
|
142
|
+
time_gps_epoch=data.time_gps_epoch,
|
|
143
|
+
gps_fix_type=data.gps_fix_type,
|
|
144
|
+
horizontal_accuracy=data.horizontal_accuracy,
|
|
145
|
+
vertical_accuracy=data.vertical_accuracy,
|
|
146
|
+
velocity_east=data.velocity_east,
|
|
147
|
+
velocity_north=data.velocity_north,
|
|
148
|
+
velocity_up=data.velocity_up,
|
|
149
|
+
speed_accuracy=data.speed_accuracy,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def serialize(cls, data: telemetry.CAMMGPSPoint) -> bytes:
|
|
154
|
+
cls.serializable(data, throw=True)
|
|
155
|
+
|
|
156
|
+
return CAMMSampleData.build(
|
|
157
|
+
{
|
|
158
|
+
"type": cls.camm_type.value,
|
|
159
|
+
"data": {
|
|
160
|
+
"time_gps_epoch": data.time_gps_epoch,
|
|
161
|
+
"gps_fix_type": data.gps_fix_type,
|
|
162
|
+
"latitude": data.lat,
|
|
163
|
+
"longitude": data.lon,
|
|
164
|
+
"altitude": -1.0 if data.alt is None else data.alt,
|
|
165
|
+
"horizontal_accuracy": data.horizontal_accuracy,
|
|
166
|
+
"vertical_accuracy": data.vertical_accuracy,
|
|
167
|
+
"velocity_east": data.velocity_east,
|
|
168
|
+
"velocity_north": data.velocity_north,
|
|
169
|
+
"velocity_up": data.velocity_up,
|
|
170
|
+
"speed_accuracy": data.speed_accuracy,
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class GoProGPSSampleEntry(CAMMSampleEntry):
|
|
177
|
+
camm_type: CAMMType = CAMMType.MLY_GOPRO_GPS
|
|
178
|
+
|
|
179
|
+
construct = C.Struct(
|
|
180
|
+
"latitude" / Double, # type: ignore
|
|
181
|
+
"longitude" / Double, # type: ignore
|
|
182
|
+
"altitude" / Float, # type: ignore
|
|
183
|
+
"epoch_time" / Double, # type: ignore
|
|
184
|
+
"fix" / C.Int32sl, # type: ignore
|
|
185
|
+
"precision" / Float, # type: ignore
|
|
186
|
+
"ground_speed" / Float, # type: ignore
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
telemetry_cls = telemetry.GPSPoint
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> telemetry.GPSPoint:
|
|
193
|
+
return telemetry.GPSPoint(
|
|
194
|
+
time=sample.exact_time,
|
|
195
|
+
lat=data.latitude,
|
|
196
|
+
lon=data.longitude,
|
|
197
|
+
alt=data.altitude,
|
|
198
|
+
angle=None,
|
|
199
|
+
epoch_time=data.epoch_time,
|
|
200
|
+
fix=telemetry.GPSFix(data.fix),
|
|
201
|
+
precision=data.precision,
|
|
202
|
+
ground_speed=data.ground_speed,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def serialize(cls, data: telemetry.GPSPoint) -> bytes:
|
|
207
|
+
cls.serializable(data, throw=True)
|
|
208
|
+
|
|
209
|
+
if data.fix is None:
|
|
210
|
+
gps_fix = telemetry.GPSFix.NO_FIX.value
|
|
211
|
+
else:
|
|
212
|
+
gps_fix = data.fix.value
|
|
213
|
+
|
|
214
|
+
return CAMMSampleData.build(
|
|
215
|
+
{
|
|
216
|
+
"type": cls.camm_type.value,
|
|
217
|
+
"data": {
|
|
218
|
+
"latitude": data.lat,
|
|
219
|
+
"longitude": data.lon,
|
|
220
|
+
"altitude": -1.0 if data.alt is None else data.alt,
|
|
221
|
+
"epoch_time": data.epoch_time,
|
|
222
|
+
"fix": gps_fix,
|
|
223
|
+
"precision": data.precision,
|
|
224
|
+
"ground_speed": data.ground_speed,
|
|
225
|
+
},
|
|
226
|
+
}
|
|
110
227
|
)
|
|
111
|
-
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class AccelerationSampleEntry(CAMMSampleEntry):
|
|
231
|
+
camm_type: CAMMType = CAMMType.ACCELERATION
|
|
232
|
+
|
|
233
|
+
construct: C.Struct = Float[3] # type: ignore
|
|
234
|
+
|
|
235
|
+
telemetry_cls = telemetry.AccelerationData
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> telemetry.AccelerationData:
|
|
112
239
|
return telemetry.AccelerationData(
|
|
113
240
|
time=sample.exact_time,
|
|
114
|
-
x=
|
|
115
|
-
y=
|
|
116
|
-
z=
|
|
241
|
+
x=data[0],
|
|
242
|
+
y=data[1],
|
|
243
|
+
z=data[2],
|
|
117
244
|
)
|
|
118
|
-
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def serialize(cls, data: telemetry.AccelerationData) -> bytes:
|
|
248
|
+
cls.serializable(data, throw=True)
|
|
249
|
+
|
|
250
|
+
return CAMMSampleData.build(
|
|
251
|
+
{
|
|
252
|
+
"type": cls.camm_type.value,
|
|
253
|
+
"data": [data.x, data.y, data.z],
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class GyroscopeSampleEntry(CAMMSampleEntry):
|
|
259
|
+
camm_type: CAMMType = CAMMType.GYRO
|
|
260
|
+
|
|
261
|
+
construct: C.Struct = Float[3] # type: ignore
|
|
262
|
+
|
|
263
|
+
telemetry_cls = telemetry.GyroscopeData
|
|
264
|
+
|
|
265
|
+
@classmethod
|
|
266
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> telemetry.GyroscopeData:
|
|
119
267
|
return telemetry.GyroscopeData(
|
|
120
268
|
time=sample.exact_time,
|
|
121
|
-
x=
|
|
122
|
-
y=
|
|
123
|
-
z=
|
|
269
|
+
x=data[0],
|
|
270
|
+
y=data[1],
|
|
271
|
+
z=data[2],
|
|
124
272
|
)
|
|
125
|
-
|
|
273
|
+
|
|
274
|
+
@classmethod
|
|
275
|
+
def serialize(cls, data: telemetry.GyroscopeData) -> bytes:
|
|
276
|
+
cls.serializable(data)
|
|
277
|
+
|
|
278
|
+
return CAMMSampleData.build(
|
|
279
|
+
{
|
|
280
|
+
"type": cls.camm_type.value,
|
|
281
|
+
"data": [data.x, data.y, data.z],
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class MagnetometerSampleEntry(CAMMSampleEntry):
|
|
287
|
+
camm_type: CAMMType = CAMMType.MAGNETIC_FIELD
|
|
288
|
+
|
|
289
|
+
construct: C.Struct = Float[3] # type: ignore
|
|
290
|
+
|
|
291
|
+
telemetry_cls = telemetry.MagnetometerData
|
|
292
|
+
|
|
293
|
+
@classmethod
|
|
294
|
+
def deserialize(cls, sample: Sample, data: T.Any) -> telemetry.MagnetometerData:
|
|
126
295
|
return telemetry.MagnetometerData(
|
|
127
296
|
time=sample.exact_time,
|
|
128
|
-
x=
|
|
129
|
-
y=
|
|
130
|
-
z=
|
|
297
|
+
x=data[0],
|
|
298
|
+
y=data[1],
|
|
299
|
+
z=data[2],
|
|
131
300
|
)
|
|
132
|
-
|
|
301
|
+
|
|
302
|
+
@classmethod
|
|
303
|
+
def serialize(cls, data: telemetry.MagnetometerData) -> bytes:
|
|
304
|
+
cls.serializable(data)
|
|
305
|
+
|
|
306
|
+
return CAMMSampleData.build(
|
|
307
|
+
{
|
|
308
|
+
"type": cls.camm_type.value,
|
|
309
|
+
"data": [data.x, data.y, data.z],
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
SAMPLE_ENTRY_CLS_BY_CAMM_TYPE = {
|
|
315
|
+
sample_entry_cls.camm_type: sample_entry_cls
|
|
316
|
+
for sample_entry_cls in CAMMSampleEntry.__subclasses__()
|
|
317
|
+
}
|
|
318
|
+
assert len(SAMPLE_ENTRY_CLS_BY_CAMM_TYPE) == 6, SAMPLE_ENTRY_CLS_BY_CAMM_TYPE.keys()
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
_SWITCH: T.Dict[int, C.Struct] = {
|
|
322
|
+
# angle_axis
|
|
323
|
+
CAMMType.ANGLE_AXIS.value: Float[3], # type: ignore
|
|
324
|
+
CAMMType.EXPOSURE_TIME.value: C.Struct(
|
|
325
|
+
"pixel_exposure_time" / C.Int32sl, # type: ignore
|
|
326
|
+
"rolling_shutter_skew_time" / C.Int32sl, # type: ignore
|
|
327
|
+
),
|
|
328
|
+
# position
|
|
329
|
+
CAMMType.POSITION.value: Float[3], # type: ignore
|
|
330
|
+
**{t.value: cls.construct for t, cls in SAMPLE_ENTRY_CLS_BY_CAMM_TYPE.items()},
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
CAMMSampleData = C.Struct(
|
|
334
|
+
C.Padding(2),
|
|
335
|
+
"type" / C.Int16ul,
|
|
336
|
+
"data" / C.Switch(C.this.type, _SWITCH),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _parse_telemetry_from_sample(
|
|
341
|
+
fp: T.BinaryIO, sample: Sample
|
|
342
|
+
) -> T.Optional[TelemetryMeasurement]:
|
|
343
|
+
fp.seek(sample.raw_sample.offset, io.SEEK_SET)
|
|
344
|
+
data = fp.read(sample.raw_sample.size)
|
|
345
|
+
box = CAMMSampleData.parse(data)
|
|
346
|
+
|
|
347
|
+
camm_type = CAMMType(box.type) # type: ignore
|
|
348
|
+
SampleKlass = SAMPLE_ENTRY_CLS_BY_CAMM_TYPE.get(camm_type)
|
|
349
|
+
if SampleKlass is None:
|
|
350
|
+
return None
|
|
351
|
+
return SampleKlass.deserialize(sample, box.data)
|
|
133
352
|
|
|
134
353
|
|
|
135
354
|
def _filter_telemetry_by_elst_segments(
|
|
@@ -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",
|
|
@@ -252,9 +252,24 @@ def _gps9_timestamp_to_epoch_time(
|
|
|
252
252
|
return epoch_time
|
|
253
253
|
|
|
254
254
|
|
|
255
|
+
def _get_gps_type(input) -> bytes:
|
|
256
|
+
final = b""
|
|
257
|
+
for val in input or []:
|
|
258
|
+
if isinstance(val, bytes):
|
|
259
|
+
final += val
|
|
260
|
+
elif isinstance(val, list):
|
|
261
|
+
final += _get_gps_type(val)
|
|
262
|
+
else:
|
|
263
|
+
raise ValueError(f"Unexpected type {type(val)} in {input}")
|
|
264
|
+
|
|
265
|
+
return final
|
|
266
|
+
|
|
267
|
+
|
|
255
268
|
def gps9_from_stream(
|
|
256
269
|
stream: T.Sequence[KLVDict],
|
|
257
270
|
) -> T.Generator[GPSPoint, None, None]:
|
|
271
|
+
NUM_VALUES = 9
|
|
272
|
+
|
|
258
273
|
indexed: T.Dict[bytes, T.List[T.List[T.Any]]] = {
|
|
259
274
|
klv["key"]: klv["data"] for klv in stream
|
|
260
275
|
}
|
|
@@ -270,17 +285,25 @@ def gps9_from_stream(
|
|
|
270
285
|
if any(s == 0 for s in scal_values):
|
|
271
286
|
return
|
|
272
287
|
|
|
273
|
-
|
|
274
|
-
if
|
|
288
|
+
gps_value_types = _get_gps_type(indexed.get(b"TYPE"))
|
|
289
|
+
if not gps_value_types:
|
|
275
290
|
return
|
|
276
|
-
|
|
291
|
+
|
|
292
|
+
if len(gps_value_types) != NUM_VALUES:
|
|
293
|
+
raise ValueError(
|
|
294
|
+
f"Error parsing the complex type {gps_value_types!r}: expect {NUM_VALUES} types but got {len(gps_value_types)}"
|
|
295
|
+
)
|
|
277
296
|
|
|
278
297
|
try:
|
|
279
298
|
sample_parser = C.Sequence(
|
|
280
|
-
*[
|
|
299
|
+
*[
|
|
300
|
+
# Changed in version 3.11: Added default argument values for length and byteorder
|
|
301
|
+
_type_mapping[t.to_bytes(length=1, byteorder="big")][0]
|
|
302
|
+
for t in gps_value_types
|
|
303
|
+
]
|
|
281
304
|
)
|
|
282
305
|
except Exception as ex:
|
|
283
|
-
raise ValueError(f"Error parsing the complex type {gps_value_types}: {ex}")
|
|
306
|
+
raise ValueError(f"Error parsing the complex type {gps_value_types!r}: {ex}")
|
|
284
307
|
|
|
285
308
|
for sample_data_bytes in gps9:
|
|
286
309
|
sample_data = sample_parser.parse(sample_data_bytes)
|
mapillary_tools/telemetry.py
CHANGED
|
@@ -12,14 +12,6 @@ class GPSFix(Enum):
|
|
|
12
12
|
FIX_3D = 3
|
|
13
13
|
|
|
14
14
|
|
|
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
15
|
@dataclasses.dataclass(order=True)
|
|
24
16
|
class TelemetryMeasurement:
|
|
25
17
|
"""Base class for all telemetry measurements.
|
|
@@ -32,6 +24,26 @@ class TelemetryMeasurement:
|
|
|
32
24
|
time: float
|
|
33
25
|
|
|
34
26
|
|
|
27
|
+
@dataclasses.dataclass
|
|
28
|
+
class GPSPoint(TelemetryMeasurement, Point):
|
|
29
|
+
epoch_time: T.Optional[float]
|
|
30
|
+
fix: T.Optional[GPSFix]
|
|
31
|
+
precision: T.Optional[float]
|
|
32
|
+
ground_speed: T.Optional[float]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclasses.dataclass
|
|
36
|
+
class CAMMGPSPoint(TelemetryMeasurement, Point):
|
|
37
|
+
time_gps_epoch: float
|
|
38
|
+
gps_fix_type: int
|
|
39
|
+
horizontal_accuracy: float
|
|
40
|
+
vertical_accuracy: float
|
|
41
|
+
velocity_east: float
|
|
42
|
+
velocity_north: float
|
|
43
|
+
velocity_up: float
|
|
44
|
+
speed_accuracy: float
|
|
45
|
+
|
|
46
|
+
|
|
35
47
|
@dataclasses.dataclass(order=True)
|
|
36
48
|
class GyroscopeData(TelemetryMeasurement):
|
|
37
49
|
"""Gyroscope signal in radians/seconds around XYZ axes of the camera."""
|
mapillary_tools/upload.py
CHANGED
|
@@ -18,14 +18,13 @@ from . import (
|
|
|
18
18
|
exceptions,
|
|
19
19
|
history,
|
|
20
20
|
ipc,
|
|
21
|
-
telemetry,
|
|
22
21
|
types,
|
|
23
22
|
upload_api_v4,
|
|
24
23
|
uploader,
|
|
25
24
|
utils,
|
|
26
25
|
VERSION,
|
|
27
26
|
)
|
|
28
|
-
from .camm import camm_builder
|
|
27
|
+
from .camm import camm_builder, camm_parser
|
|
29
28
|
from .geotag import gpmf_parser
|
|
30
29
|
from .mp4 import simple_mp4_builder
|
|
31
30
|
from .types import FileType
|
|
@@ -616,7 +615,7 @@ def upload(
|
|
|
616
615
|
assert isinstance(video_metadata.md5sum, str), "md5sum should be updated"
|
|
617
616
|
|
|
618
617
|
# extract telemetry measurements from GoPro videos
|
|
619
|
-
telemetry_measurements: T.List[
|
|
618
|
+
telemetry_measurements: T.List[camm_parser.TelemetryMeasurement] = []
|
|
620
619
|
if MAPILLARY__EXPERIMENTAL_ENABLE_IMU == "YES":
|
|
621
620
|
if video_metadata.filetype is FileType.GOPRO:
|
|
622
621
|
with video_metadata.filename.open("rb") as fp:
|
|
@@ -13,8 +13,12 @@ class CammParser(BaseParser):
|
|
|
13
13
|
parser_label = "camm"
|
|
14
14
|
|
|
15
15
|
@functools.cached_property
|
|
16
|
-
def
|
|
17
|
-
|
|
16
|
+
def _camera_info(self) -> T.Tuple[str, str]:
|
|
17
|
+
source_path = self.geotag_source_path
|
|
18
|
+
if not source_path:
|
|
19
|
+
return "", ""
|
|
20
|
+
|
|
21
|
+
with source_path.open("rb") as fp:
|
|
18
22
|
return camm_parser.extract_camera_make_and_model(fp)
|
|
19
23
|
|
|
20
24
|
def extract_points(self) -> T.Sequence[geo.Point]:
|
|
@@ -28,15 +32,7 @@ class CammParser(BaseParser):
|
|
|
28
32
|
return []
|
|
29
33
|
|
|
30
34
|
def extract_make(self) -> T.Optional[str]:
|
|
31
|
-
|
|
32
|
-
if not source_path:
|
|
33
|
-
return None
|
|
34
|
-
with source_path.open("rb") as _fp:
|
|
35
|
-
return self.__camera_info[0] or None
|
|
35
|
+
return self._camera_info[0] or None
|
|
36
36
|
|
|
37
37
|
def extract_model(self) -> T.Optional[str]:
|
|
38
|
-
|
|
39
|
-
if not source_path:
|
|
40
|
-
return None
|
|
41
|
-
with source_path.open("rb") as _fp:
|
|
42
|
-
return self.__camera_info[1] or None
|
|
38
|
+
return self._camera_info[1] or None
|
|
@@ -20,7 +20,13 @@ class GpxParser(BaseParser):
|
|
|
20
20
|
if not path:
|
|
21
21
|
return []
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
try:
|
|
24
|
+
gpx_tracks = geotag_images_from_gpx_file.parse_gpx(path)
|
|
25
|
+
except Exception as ex:
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
f"Error parsing GPX {path}: {ex.__class__.__name__}: {ex}"
|
|
28
|
+
)
|
|
29
|
+
|
|
24
30
|
if 1 < len(gpx_tracks):
|
|
25
31
|
LOG.warning(
|
|
26
32
|
"Found %s tracks in the GPX file %s. Will merge points in all the tracks as a single track for interpolation",
|
|
@@ -32,40 +38,71 @@ class GpxParser(BaseParser):
|
|
|
32
38
|
if not gpx_points:
|
|
33
39
|
return gpx_points
|
|
34
40
|
|
|
41
|
+
offset = self._synx_gpx_by_first_gps_timestamp(gpx_points)
|
|
42
|
+
|
|
43
|
+
self._rebase_times(gpx_points, offset=offset)
|
|
44
|
+
|
|
45
|
+
return gpx_points
|
|
46
|
+
|
|
47
|
+
def _synx_gpx_by_first_gps_timestamp(
|
|
48
|
+
self, gpx_points: T.Sequence[geo.Point]
|
|
49
|
+
) -> float:
|
|
50
|
+
offset: float = 0.0
|
|
51
|
+
|
|
52
|
+
if not gpx_points:
|
|
53
|
+
return offset
|
|
54
|
+
|
|
35
55
|
first_gpx_dt = datetime.datetime.fromtimestamp(
|
|
36
56
|
gpx_points[0].time, tz=datetime.timezone.utc
|
|
37
57
|
)
|
|
38
58
|
LOG.info("First GPX timestamp: %s", first_gpx_dt)
|
|
39
59
|
|
|
40
60
|
# Extract first GPS timestamp (if found) for synchronization
|
|
41
|
-
|
|
42
|
-
parser = GenericVideoParser(self.videoPath, self.options,
|
|
61
|
+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
|
|
62
|
+
parser = GenericVideoParser(self.videoPath, self.options, {})
|
|
43
63
|
gps_points = parser.extract_points()
|
|
44
|
-
if gps_points:
|
|
45
|
-
first_gps_point = gps_points[0]
|
|
46
|
-
if isinstance(first_gps_point, telemetry.GPSPoint):
|
|
47
|
-
if first_gps_point.epoch_time is not None:
|
|
48
|
-
first_gps_dt = datetime.datetime.fromtimestamp(
|
|
49
|
-
first_gps_point.epoch_time, tz=datetime.timezone.utc
|
|
50
|
-
)
|
|
51
|
-
LOG.info("First GPS timestamp: %s", first_gps_dt)
|
|
52
|
-
offset = gpx_points[0].time - first_gps_point.epoch_time
|
|
53
|
-
if offset:
|
|
54
|
-
LOG.warning(
|
|
55
|
-
"Found offset between GPX %s and video GPS timestamps %s: %s seconds",
|
|
56
|
-
first_gpx_dt,
|
|
57
|
-
first_gps_dt,
|
|
58
|
-
offset,
|
|
59
|
-
)
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
if not gps_points:
|
|
66
|
+
LOG.warning(
|
|
67
|
+
"Skip GPX synchronization because no GPS found in video %s",
|
|
68
|
+
self.videoPath,
|
|
69
|
+
)
|
|
70
|
+
return offset
|
|
62
71
|
|
|
63
|
-
|
|
72
|
+
first_gps_point = gps_points[0]
|
|
73
|
+
if isinstance(first_gps_point, telemetry.GPSPoint):
|
|
74
|
+
if first_gps_point.epoch_time is not None:
|
|
75
|
+
first_gps_dt = datetime.datetime.fromtimestamp(
|
|
76
|
+
first_gps_point.epoch_time, tz=datetime.timezone.utc
|
|
77
|
+
)
|
|
78
|
+
LOG.info("First GPS timestamp: %s", first_gps_dt)
|
|
79
|
+
offset = gpx_points[0].time - first_gps_point.epoch_time
|
|
80
|
+
if offset:
|
|
81
|
+
LOG.warning(
|
|
82
|
+
"Found offset between GPX %s and video GPS timestamps %s: %s seconds",
|
|
83
|
+
first_gpx_dt,
|
|
84
|
+
first_gps_dt,
|
|
85
|
+
offset,
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
LOG.info(
|
|
89
|
+
"GPX and GPS are perfectly synchronized (all starts from %s)",
|
|
90
|
+
first_gpx_dt,
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
LOG.warning(
|
|
94
|
+
"Skip GPX synchronization because no GPS epoch time found in video %s",
|
|
95
|
+
self.videoPath,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return offset
|
|
64
99
|
|
|
65
100
|
def extract_make(self) -> T.Optional[str]:
|
|
66
|
-
|
|
101
|
+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
|
|
102
|
+
parser = GenericVideoParser(self.videoPath, self.options, {})
|
|
67
103
|
return parser.extract_make()
|
|
68
104
|
|
|
69
105
|
def extract_model(self) -> T.Optional[str]:
|
|
70
|
-
|
|
106
|
+
# Use an empty dictionary to force video parsers to extract make/model from the video metadata itself
|
|
107
|
+
parser = GenericVideoParser(self.videoPath, self.options, {})
|
|
71
108
|
return parser.extract_model()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
mapillary_tools/__init__.py,sha256=
|
|
1
|
+
mapillary_tools/__init__.py,sha256=XBFY-aPf1nqJ7U8CvnYiXTRu3uDJLRQjOxibBTi-54U,19
|
|
2
2
|
mapillary_tools/api_v4.py,sha256=zhRtgx3EnzgqtjziRhvFq3ONvsPaB9hROsuKFcf_pFo,5197
|
|
3
3
|
mapillary_tools/authenticate.py,sha256=LCFcs6LqZmXaYkTUEKgGfmqytWdh5v_L3KXB48ojOZ4,3090
|
|
4
4
|
mapillary_tools/config.py,sha256=jCjaK4jJaTY4AV4qf_b_tcxn5LA_uPsEWlGIdm2zw6g,2103
|
|
@@ -15,14 +15,14 @@ mapillary_tools/ipc.py,sha256=DwWQb9hNshx0bg0Fo5NjY0mXjs-FkbR6tIQmjMgMtmg,1089
|
|
|
15
15
|
mapillary_tools/process_geotag_properties.py,sha256=w4hhv_c4sRydCK9QCO50sT2yo2zeVlY7dSdXQ93InFc,23159
|
|
16
16
|
mapillary_tools/process_sequence_properties.py,sha256=5oYEjz9crnLVQtCkxbwn57TkeuHFbBh_zQXQSA4ENWg,11561
|
|
17
17
|
mapillary_tools/sample_video.py,sha256=dpdX7bUNEmcrz-3gh3Y3awnTDX66pChbTKuF8qGfeCI,14400
|
|
18
|
-
mapillary_tools/telemetry.py,sha256=
|
|
18
|
+
mapillary_tools/telemetry.py,sha256=3a7MvTH4Rr_mXp2NInubPIS8JFOuBeQC7PC2prfXNfI,1559
|
|
19
19
|
mapillary_tools/types.py,sha256=6kww2UdKM6YzabYbc862BYzEWtxL2hhxCRFfeDiUtF0,22074
|
|
20
|
-
mapillary_tools/upload.py,sha256=
|
|
20
|
+
mapillary_tools/upload.py,sha256=ab1WEut6qv352njWKtNbUXmV8KShlApOjcwb1FpcC_Q,24439
|
|
21
21
|
mapillary_tools/upload_api_v4.py,sha256=1WvoUis92KDXbqfoyvyyDmiCqwXezYkMJZhnYaVm3BA,8560
|
|
22
22
|
mapillary_tools/uploader.py,sha256=VieDKi51wdXTIhN7x_mcuQeHESUyFlF5cgB-TAnF4g0,14093
|
|
23
23
|
mapillary_tools/utils.py,sha256=VNtK1tAb3Hh8y3P5e5Y3iewREkIoLDa3C2myRYcF2lY,5970
|
|
24
|
-
mapillary_tools/camm/camm_builder.py,sha256=
|
|
25
|
-
mapillary_tools/camm/camm_parser.py,sha256=
|
|
24
|
+
mapillary_tools/camm/camm_builder.py,sha256=x4WF-n6zOEUhK1Poo7du004-7DQhjnf598ZNfM3r7iA,9075
|
|
25
|
+
mapillary_tools/camm/camm_parser.py,sha256=ARwImVcXY1FckCnPLPoyYAuODJb5iZI0hlQurAIeiWQ,16473
|
|
26
26
|
mapillary_tools/commands/__init__.py,sha256=41CFrPLGlG3566uhxssEF3TGAtSpADFPPcDMHbViU0E,171
|
|
27
27
|
mapillary_tools/commands/__main__.py,sha256=VdWkx1ekPH-88Ybe78IcO9FWpZ5cUhsbGRw7LuzQObU,4832
|
|
28
28
|
mapillary_tools/commands/authenticate.py,sha256=4aVvAQal_mqtm2NEMBt5aKLahi0iRdO8b7WSBf6jokA,1136
|
|
@@ -40,13 +40,13 @@ mapillary_tools/geotag/geotag_images_from_exif.py,sha256=hCgBwZABk2tbBQC3cHQBV5p
|
|
|
40
40
|
mapillary_tools/geotag/geotag_images_from_exiftool.py,sha256=a-c4H8VIyPdJkfUIvJho0phR0QU0zN8-lSyiCz0wc4s,3981
|
|
41
41
|
mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py,sha256=nRVAjgTJwx_eCaSBpPCgcIaZs3EYgGueYxSS9XhKv40,3350
|
|
42
42
|
mapillary_tools/geotag/geotag_images_from_gpx.py,sha256=S9Pw6FvP5kRSpHUnKUYKXmw0CHa9V92UmrS_MJfbjS4,9053
|
|
43
|
-
mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256
|
|
43
|
+
mapillary_tools/geotag/geotag_images_from_gpx_file.py,sha256=-vTbZ1HufZzJCd8VvukdTjsJRcymtfld2W5t65VSG5E,5300
|
|
44
44
|
mapillary_tools/geotag/geotag_images_from_nmea_file.py,sha256=dDdHnJInQ_WN3ZRf-w44NSBElDLPs7XYBiimvE2iCNo,1651
|
|
45
45
|
mapillary_tools/geotag/geotag_images_from_video.py,sha256=XsaWOFChGItl-j1UbKM4hNjUqN29pVNbMpGT_BvI-o8,3306
|
|
46
46
|
mapillary_tools/geotag/geotag_videos_from_exiftool_video.py,sha256=fkkWou1WFt3ft024399vis9No2cxrwot7Pg5HBw7o7s,5225
|
|
47
47
|
mapillary_tools/geotag/geotag_videos_from_video.py,sha256=mqBZKUEkqT96nOzl5LJxzzTKuKsnAkMK5lH8k3oY3YE,7330
|
|
48
48
|
mapillary_tools/geotag/gpmf_gps_filter.py,sha256=7cg8wEjC1DrujKY76FZguXsaPqTRkG9-t32OeuOJQIc,2755
|
|
49
|
-
mapillary_tools/geotag/gpmf_parser.py,sha256=
|
|
49
|
+
mapillary_tools/geotag/gpmf_parser.py,sha256=6Q38oeSd2kHTs44Fzpg9R555EbQqdd5QcIuIZdG4z_o,23233
|
|
50
50
|
mapillary_tools/geotag/gps_filter.py,sha256=4CPL8glxdzPWIbfGPPgyqMLyiZFt-djce5vhiFPuZB8,3766
|
|
51
51
|
mapillary_tools/geotag/utils.py,sha256=Orl35Df4ypxj4v6Lu1Mhk9d2XZxa8yNffz1s1C0JZsQ,651
|
|
52
52
|
mapillary_tools/mp4/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -60,16 +60,16 @@ mapillary_tools/video_data_extraction/extract_video_data.py,sha256=_2BBdSYeYKR4B
|
|
|
60
60
|
mapillary_tools/video_data_extraction/video_data_parser_factory.py,sha256=qaJHvLgwI5lukJncMd8ggxeSxXOiVzBSJO5GlGQYiXY,1134
|
|
61
61
|
mapillary_tools/video_data_extraction/extractors/base_parser.py,sha256=s7Xuwg4I5JZ27oL4ebMSdo093plAXfZ-6uDQ_h97WHY,2134
|
|
62
62
|
mapillary_tools/video_data_extraction/extractors/blackvue_parser.py,sha256=jAcGyF6PML2EdJ4zle8cR12QeTRZc5qxlz8_4gcTZPU,1089
|
|
63
|
-
mapillary_tools/video_data_extraction/extractors/camm_parser.py,sha256=
|
|
63
|
+
mapillary_tools/video_data_extraction/extractors/camm_parser.py,sha256=YMiViocXSVlfn8_qm1jcwSJhnnEaK8v5ADHwo2YXe10,1117
|
|
64
64
|
mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py,sha256=PFNCRk9pGrPIfVwLMcnzmVNMITVjNHhbrOOMwxaSstg,2270
|
|
65
65
|
mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py,sha256=Tt0h4TiCKocERWMlRXzlpoaA_WJ_4b20MgMLGYNl4AM,1734
|
|
66
66
|
mapillary_tools/video_data_extraction/extractors/generic_video_parser.py,sha256=34O6Km5kNDoJNJtIUOwtAzzMntuqkSZJfeli7caWSkA,1693
|
|
67
67
|
mapillary_tools/video_data_extraction/extractors/gopro_parser.py,sha256=IVnTyquSraTUaG9rxbJfVWc1-drdY5PaHn5urh3IBk4,1325
|
|
68
|
-
mapillary_tools/video_data_extraction/extractors/gpx_parser.py,sha256=
|
|
68
|
+
mapillary_tools/video_data_extraction/extractors/gpx_parser.py,sha256=FNrdnXl48k8I1I5fGwYsClhfFEHVsooRLRboUYECv3I,3811
|
|
69
69
|
mapillary_tools/video_data_extraction/extractors/nmea_parser.py,sha256=raSXavBvP-0LJCB_TwLL0mOv2uHSsB744igTsaKAaGc,658
|
|
70
|
-
mapillary_tools-0.13.
|
|
71
|
-
mapillary_tools-0.13.
|
|
72
|
-
mapillary_tools-0.13.
|
|
73
|
-
mapillary_tools-0.13.
|
|
74
|
-
mapillary_tools-0.13.
|
|
75
|
-
mapillary_tools-0.13.
|
|
70
|
+
mapillary_tools-0.13.1.dist-info/LICENSE,sha256=l2D8cKfFmmJq_wcVq_JElPJrlvWQOzNWx7gMLINucxc,1292
|
|
71
|
+
mapillary_tools-0.13.1.dist-info/METADATA,sha256=K1pmlXXrkP_o1qFddzeG6RyXGt6HqVKhU8aLhIVUK_w,19758
|
|
72
|
+
mapillary_tools-0.13.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
73
|
+
mapillary_tools-0.13.1.dist-info/entry_points.txt,sha256=A3f3LP-BO_P-U8Y29QfpT4jx6Mjk3sXjTi2Yew4bvj8,75
|
|
74
|
+
mapillary_tools-0.13.1.dist-info/top_level.txt,sha256=FbDkMgOrt1S70ho1WSBrOwzKOSkJFDwwqFOoY5-527s,16
|
|
75
|
+
mapillary_tools-0.13.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|